From ba7abda5a54bba1037a5b4f1a118d2677b93ea44 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Thu, 27 Feb 2025 10:33:28 +0100 Subject: [PATCH 1/2] clippy --- document_tree/src/attribute_types.rs | 32 +- document_tree/src/element_categories.rs | 1 + document_tree/src/elements.rs | 2 + document_tree/src/extra_attributes.rs | 1 + document_tree/src/lib.rs | 5 +- document_tree/src/url.rs | 11 + parser/src/conversion.rs | 14 +- parser/src/conversion/block.rs | 23 +- parser/src/conversion/inline.rs | 230 +++++------ parser/src/conversion/tests.rs | 56 ++- parser/src/lib.rs | 8 + parser/src/simplify.rs | 493 ++++++++++++------------ renderer/src/html.rs | 36 +- renderer/src/html/tests.rs | 4 +- renderer/src/lib.rs | 10 + rst/src/main.rs | 2 + 16 files changed, 480 insertions(+), 448 deletions(-) diff --git a/document_tree/src/attribute_types.rs b/document_tree/src/attribute_types.rs index eeb6161..5878248 100644 --- a/document_tree/src/attribute_types.rs +++ b/document_tree/src/attribute_types.rs @@ -91,14 +91,14 @@ pub enum Measure { impl FromStr for AlignHV { type Err = Error; fn from_str(s: &str) -> Result { - use self::AlignHV::*; + use self::AlignHV as A; Ok(match s { - "top" => Top, - "middle" => Middle, - "bottom" => Bottom, - "left" => Left, - "center" => Center, - "right" => Right, + "top" => A::Top, + "middle" => A::Middle, + "bottom" => A::Bottom, + "left" => A::Left, + "center" => A::Center, + "right" => A::Right, s => bail!("Invalid Alignment {}", s), }) } @@ -119,7 +119,7 @@ impl From<&str> for NameToken { impl FromStr for Measure { type Err = Error; fn from_str(s: &str) -> Result { - use self::Measure::*; + use self::Measure as M; let re = Regex::new(r"(?P\d+\.\d*|\.?\d+)\s*(?Pem|ex|mm|cm|in|px|pt|pc)").unwrap(); let caps: regex::Captures = re @@ -127,14 +127,14 @@ impl FromStr for Measure { .ok_or_else(|| format_err!("Invalid measure"))?; let value: f64 = caps["float"].parse()?; Ok(match &caps["unit"] { - "em" => Em(value), - "ex" => Ex(value), - "mm" => Mm(value), - "cm" => Cm(value), - "in" => In(value), - "px" => Px(value), - "pt" => Pt(value), - "pc" => Pc(value), + "em" => M::Em(value), + "ex" => M::Ex(value), + "mm" => M::Mm(value), + "cm" => M::Cm(value), + "in" => M::In(value), + "px" => M::Px(value), + "pt" => M::Pt(value), + "pc" => M::Pc(value), _ => unreachable!(), }) } diff --git a/document_tree/src/element_categories.rs b/document_tree/src/element_categories.rs index e0a1886..4e761ed 100644 --- a/document_tree/src/element_categories.rs +++ b/document_tree/src/element_categories.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter}; use serde_derive::Serialize; +#[allow(clippy::wildcard_imports)] use crate::elements::*; pub trait HasChildren { diff --git a/document_tree/src/elements.rs b/document_tree/src/elements.rs index 4557243..a5bd3ed 100644 --- a/document_tree/src/elements.rs +++ b/document_tree/src/elements.rs @@ -2,6 +2,7 @@ use serde_derive::Serialize; use std::path::PathBuf; use crate::attribute_types::{CanBeEmpty, ID, NameToken}; +#[allow(clippy::wildcard_imports)] use crate::element_categories::*; use crate::extra_attributes::{self, ExtraAttributes}; @@ -132,6 +133,7 @@ macro_rules! impl_new {( $(#[$fattr])* $field: $typ, )* } impl $name { + #[must_use] pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field, )* } } } )} diff --git a/document_tree/src/extra_attributes.rs b/document_tree/src/extra_attributes.rs index 41ba4ec..1d2c338 100644 --- a/document_tree/src/extra_attributes.rs +++ b/document_tree/src/extra_attributes.rs @@ -106,6 +106,7 @@ impl_extra!(RawInline { space: FixedSpace, format: Vec }); pub type ImageInline = Image; impl Image { + #[must_use] pub fn new(uri: Url) -> Image { Image { uri, diff --git a/document_tree/src/lib.rs b/document_tree/src/lib.rs index 9e44023..17eab5d 100644 --- a/document_tree/src/lib.rs +++ b/document_tree/src/lib.rs @@ -1,3 +1,4 @@ +#![warn(clippy::pedantic)] #![recursion_limit = "256"] //! See [doctree][] reference. @@ -33,7 +34,7 @@ mod tests { title.append_child(image); doc.append_child(title); - println!("{:?}", doc); + println!("{doc:?}"); } #[test] @@ -49,6 +50,6 @@ mod tests { .into(), ]); - println!("{:?}", doc); + println!("{doc:?}"); } } diff --git a/document_tree/src/url.rs b/document_tree/src/url.rs index be320e9..1a3b6d7 100644 --- a/document_tree/src/url.rs +++ b/document_tree/src/url.rs @@ -29,9 +29,19 @@ fn starts_with_scheme(input: &str) -> bool { pub struct Url(String); impl Url { + /// Parse an absolute URL. + /// + /// # Errors + /// Returns an error if the string is not a valid absolute URL. pub fn parse_absolute(input: &str) -> Result { Ok(url::Url::parse(input)?.into()) } + + /// Parse a relative path as URL. + /// + /// # Errors + /// Returns an error if the string is not a relative path or can’t be converted to an url. + #[allow(clippy::missing_panics_doc)] pub fn parse_relative(input: &str) -> Result { // We're assuming that any scheme through which RsT documents are being // accessed is a hierarchical scheme, and so we can parse relative to a @@ -49,6 +59,7 @@ impl Url { Err(ParseError::SetHostOnCannotBeABaseUrl) } } + #[must_use] pub fn as_str(&self) -> &str { self.0.as_str() } diff --git a/parser/src/conversion.rs b/parser/src/conversion.rs index 587bf0b..5f7eb74 100644 --- a/parser/src/conversion.rs +++ b/parser/src/conversion.rs @@ -14,8 +14,8 @@ use crate::pest_rst::Rule; fn ssubel_to_section_unchecked_mut(ssubel: &mut c::StructuralSubElement) -> &mut e::Section { match ssubel { - c::StructuralSubElement::SubStructure(b) => match **b { - c::SubStructure::Section(ref mut s) => s, + c::StructuralSubElement::SubStructure(b) => match b.as_mut() { + c::SubStructure::Section(s) => s, _ => unreachable!(), }, _ => unreachable!(), @@ -27,16 +27,14 @@ fn get_level<'tl>( section_idxs: &[Option], ) -> &'tl mut Vec { let mut level = toplevel; - for maybe_i in section_idxs { - if let Some(i) = *maybe_i { - level = ssubel_to_section_unchecked_mut(&mut level[i]).children_mut(); - } + for i in section_idxs.iter().flatten().copied() { + level = ssubel_to_section_unchecked_mut(&mut level[i]).children_mut(); } level } pub fn convert_document(pairs: Pairs) -> Result { - use self::block::TitleOrSsubel::*; + use self::block::TitleOrSsubel::{Ssubel, Title}; let mut toplevel: Vec = vec![]; // The kinds of section titles encountered. @@ -60,7 +58,7 @@ pub fn convert_document(pairs: Pairs) -> Result { // If idx > len: Add None for skipped levels // TODO: test skipped levels while section_idxs.len() < idx { - section_idxs.push(None) + section_idxs.push(None); } } None => kinds.push(kind), diff --git a/parser/src/conversion/block.rs b/parser/src/conversion/block.rs index 16b1e33..dcd4c36 100644 --- a/parser/src/conversion/block.rs +++ b/parser/src/conversion/block.rs @@ -21,7 +21,7 @@ pub(super) enum TitleOrSsubel { } pub(super) fn convert_ssubel(pair: Pair) -> Result, Error> { - use self::TitleOrSsubel::*; + use self::TitleOrSsubel::{Ssubel, Title}; Ok(Some(match pair.as_rule() { Rule::title => { let (t, k) = convert_title(pair)?; @@ -48,7 +48,7 @@ fn convert_body_elem(pair: Pair) -> Result { Rule::target => convert_target(pair)?.into(), Rule::substitution_def => convert_substitution_def(pair)?.into(), Rule::block_quote_directive => convert_block_quote_directive(pair)?.into(), - Rule::admonition_gen => convert_admonition_gen(pair)?, + Rule::admonition_gen => convert_admonition_gen(pair), Rule::image => convert_image::(pair)?.into(), Rule::bullet_list => convert_bullet_list(pair)?.into(), Rule::block_quote => convert_block_quote(pair)?.into(), @@ -74,7 +74,7 @@ fn convert_title(pair: Pair) -> Result<(e::Title, TitleKind), Error> { title_inlines = Some(convert_inlines(p)?); } Rule::adornments => { - adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")) + adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")); } rule => unimplemented!("Unexpected rule in title: {:?}", rule), }; @@ -100,7 +100,7 @@ fn convert_paragraph(pair: Pair) -> Result { } fn convert_target(pair: Pair) -> Result { - let mut elem: e::Target = Default::default(); + let mut elem = e::Target::default(); elem.extra_mut().anonymous = false; for p in pair.into_inner() { match p.as_rule() { @@ -110,7 +110,7 @@ fn convert_target(pair: Pair) -> Result { } // TODO: also handle non-urls Rule::link_target => elem.extra_mut().refuri = Some(p.parse()?), - rule => panic!("Unexpected rule in target: {:?}", rule), + rule => panic!("Unexpected rule in target: {rule:?}"), } } Ok(elem) @@ -123,7 +123,7 @@ fn convert_substitution_def(pair: Pair) -> Result = match inner_pair.as_rule() { Rule::replace => convert_replace(inner_pair)?, Rule::image => vec![convert_image::(inner_pair)?.into()], - rule => panic!("Unknown substitution rule {:?}", rule), + rule => panic!("Unknown substitution rule {rule:?}"), }; let mut subst_def = e::SubstitutionDefinition::with_children(inner); subst_def.names_mut().push(at::NameToken(name)); @@ -164,13 +164,14 @@ where } fn parse_scale(pair: &Pair) -> Result { + use pest::error::{Error, ErrorVariant}; + let input = pair.as_str().trim(); let input = if let Some(percentage) = input.strip_suffix('%') { percentage.trim_end() } else { input }; - use pest::error::{Error, ErrorVariant}; Ok(input.parse().map_err(|e: std::num::ParseIntError| { let var: ErrorVariant = ErrorVariant::CustomError { message: e.to_string(), @@ -179,14 +180,14 @@ fn parse_scale(pair: &Pair) -> Result { })?) } -fn convert_admonition_gen(pair: Pair) -> Result { +fn convert_admonition_gen(pair: Pair) -> document_tree::element_categories::BodyElement { let mut iter = pair.into_inner(); let typ = iter.next().unwrap().as_str(); // TODO: in reality it contains body elements. let children: Vec = iter .map(|p| e::Paragraph::with_children(vec![p.as_str().into()]).into()) .collect(); - Ok(match typ { + match typ { "attention" => e::Attention::with_children(children).into(), "hint" => e::Hint::with_children(children).into(), "note" => e::Note::with_children(children).into(), @@ -196,8 +197,8 @@ fn convert_admonition_gen(pair: Pair) -> Result { "important" => e::Important::with_children(children).into(), "tip" => e::Tip::with_children(children).into(), "warning" => e::Warning::with_children(children).into(), - typ => panic!("Unknown admontion type {}!", typ), - }) + typ => panic!("Unknown admontion type {typ}!"), + } } fn convert_bullet_list(pair: Pair) -> Result { diff --git a/parser/src/conversion/inline.rs b/parser/src/conversion/inline.rs index cb2d3f7..7511310 100644 --- a/parser/src/conversion/inline.rs +++ b/parser/src/conversion/inline.rs @@ -2,7 +2,7 @@ use anyhow::Error; use pest::iterators::Pair; use document_tree::{ - HasChildren, attribute_types as at, element_categories as c, elements as e, + CommonAttributes, HasChildren, attribute_types as at, element_categories as c, elements as e, extra_attributes as a, url::Url, }; @@ -14,7 +14,7 @@ pub fn convert_inline(pair: Pair) -> Result Rule::str | Rule::str_nested => pair.as_str().into(), Rule::ws_newline => " ".to_owned().into(), Rule::reference => convert_reference(pair)?, - Rule::substitution_name => convert_substitution_ref(pair)?.into(), + Rule::substitution_name => convert_substitution_ref(&pair).into(), Rule::emph => e::Emphasis::with_children(convert_inlines(pair)?).into(), Rule::strong => e::Strong::with_children(convert_inlines(pair)?).into(), Rule::literal => e::Literal::with_children(vec![pair.as_str().to_owned()]).into(), @@ -27,130 +27,134 @@ pub fn convert_inlines(pair: Pair) -> Result, } fn convert_reference(pair: Pair) -> Result { - let name; - let refuri; - let refid; - let mut refname = vec![]; - let mut children: Vec = vec![]; let concrete = pair.into_inner().next().unwrap(); match concrete.as_rule() { - Rule::reference_target => { - let rt_inner = concrete.into_inner().next().unwrap(); // reference_target_uq or target_name_qu - match rt_inner.as_rule() { - Rule::reference_target_uq => { - refid = None; - name = Some(rt_inner.as_str().into()); - refuri = None; - refname.push(rt_inner.as_str().into()); - children.push(rt_inner.as_str().into()); + Rule::reference_target => convert_reference_target(concrete).map(Into::into), + Rule::reference_explicit => unimplemented!("explicit reference"), + Rule::reference_auto => Ok(convert_reference_auto(concrete)), + _ => unreachable!(), + } +} + +fn convert_reference_target(concrete: Pair<'_, Rule>) -> Result { + let rt_inner = concrete.into_inner().next().unwrap(); + Ok(match rt_inner.as_rule() { + Rule::reference_target_uq => e::Reference::new( + CommonAttributes::default(), + a::Reference { + name: Some(rt_inner.as_str().into()), + refuri: None, + refid: None, + refname: vec![rt_inner.as_str().into()], + }, + vec![rt_inner.as_str().into()], + ), + Rule::reference_target_qu => { + let (text, reference) = { + let mut text = None; + let mut reference = None; + for inner in rt_inner.clone().into_inner() { + match inner.as_rule() { + Rule::reference_text => text = Some(inner), + Rule::reference_bracketed => reference = Some(inner), + _ => unreachable!(), + } } - Rule::reference_target_qu => { - let (text, reference) = { - let mut text = None; - let mut reference = None; - for inner in rt_inner.clone().into_inner() { - match inner.as_rule() { - Rule::reference_text => text = Some(inner), - Rule::reference_bracketed => reference = Some(inner), - _ => unreachable!(), + (text, reference) + }; + let trimmed_text = match (&text, &reference) { + (Some(text), None) => text.as_str(), + (_, Some(reference)) => text + .map(|text| text.as_str().trim_end_matches(|ch| " \n\r".contains(ch))) + .filter(|text| !text.is_empty()) + .unwrap_or_else(|| reference.clone().into_inner().next().unwrap().as_str()), + (None, None) => unreachable!(), + }; + let (refuri, refname): (Option, Vec) = + if let Some(reference) = reference { + let inner = reference.into_inner().next().unwrap(); + match inner.as_rule() { + // The URL rules in our parser accept a narrow superset of + // valid URLs, so we need to handle false positives. + Rule::url => { + if let Ok(target) = Url::parse_absolute(inner.as_str()) { + (Some(target), Vec::new()) + } else if inner.as_str().ends_with('_') { + // like target_name_qu (minus the final underscore) + let full_str = inner.as_str(); + (None, vec![full_str[0..full_str.len() - 1].into()]) + } else { + // like relative_reference + (Some(Url::parse_relative(inner.as_str())?), Vec::new()) } } - (text, reference) - }; - let trimmed_text = match (&text, &reference) { - (Some(text), None) => text.as_str(), - (_, Some(reference)) => text - .map(|text| text.as_str().trim_end_matches(|ch| " \n\r".contains(ch))) - .filter(|text| !text.is_empty()) - .unwrap_or_else(|| { - reference.clone().into_inner().next().unwrap().as_str() - }), - (None, None) => unreachable!(), - }; - refid = None; - name = Some(trimmed_text.into()); - refuri = if let Some(reference) = reference { - let inner = reference.into_inner().next().unwrap(); - match inner.as_rule() { - // The URL rules in our parser accept a narrow superset of - // valid URLs, so we need to handle false positives. - Rule::url => { - if let Ok(target) = Url::parse_absolute(inner.as_str()) { - Some(target) - } else if inner.as_str().ends_with('_') { - // like target_name_qu (minus the final underscore) - let full_str = inner.as_str(); - refname.push(full_str[0..full_str.len() - 1].into()); - None - } else { - // like relative_reference - Some(Url::parse_relative(inner.as_str())?) - } - } - Rule::target_name_qu => { - refname.push(inner.as_str().into()); - None - } - Rule::relative_reference => Some(Url::parse_relative(inner.as_str())?), - _ => unreachable!(), + Rule::target_name_qu => (None, vec![inner.as_str().into()]), + Rule::relative_reference => { + (Some(Url::parse_relative(inner.as_str())?), Vec::new()) } - } else { - refname.push(trimmed_text.into()); - None - }; - children.push(trimmed_text.into()); - } - _ => unreachable!(), - } - } - Rule::reference_explicit => unimplemented!("explicit reference"), - Rule::reference_auto => { - let rt_inner = concrete.into_inner().next().unwrap(); - match rt_inner.as_rule() { - Rule::url_auto => match Url::parse_absolute(rt_inner.as_str()) { - Ok(target) => { - refuri = Some(target); - name = None; - refid = None; - children.push(rt_inner.as_str().into()); + _ => unreachable!(), } - // if our parser got a URL wrong, return it as a string - Err(_) => return Ok(rt_inner.as_str().into()), + } else { + (None, vec![trimmed_text.into()]) + }; + e::Reference::new( + CommonAttributes::default(), + a::Reference { + name: Some(trimmed_text.into()), + refuri, + refid: None, + refname, }, - Rule::email => { - let mailto_url = String::from("mailto:") + rt_inner.as_str(); - match Url::parse_absolute(&mailto_url) { - Ok(target) => { - refuri = Some(target); - name = None; - refid = None; - children.push(rt_inner.as_str().into()); - } - // if our parser got a URL wrong, return it as a string - Err(_) => return Ok(rt_inner.as_str().into()), - } - } - _ => unreachable!(), - } + vec![trimmed_text.into()], + ) } _ => unreachable!(), - }; - Ok(e::Reference::new( - Default::default(), - a::Reference { - name, - refuri, - refid, - refname, + }) +} + +fn convert_reference_auto(concrete: Pair<'_, Rule>) -> c::TextOrInlineElement { + let rt_inner = concrete.into_inner().next().unwrap(); + match rt_inner.as_rule() { + Rule::url_auto => match Url::parse_absolute(rt_inner.as_str()) { + Ok(target) => e::Reference::new( + CommonAttributes::default(), + a::Reference { + name: None, + refuri: Some(target), + refid: None, + refname: Vec::new(), + }, + vec![rt_inner.as_str().into()], + ) + .into(), + // if our parser got a URL wrong, return it as a string + Err(_) => rt_inner.as_str().into(), }, - children, - ) - .into()) + Rule::email => { + let mailto_url = String::from("mailto:") + rt_inner.as_str(); + match Url::parse_absolute(&mailto_url) { + Ok(target) => e::Reference::new( + CommonAttributes::default(), + a::Reference { + name: None, + refuri: Some(target), + refid: None, + refname: Vec::new(), + }, + vec![rt_inner.as_str().into()], + ) + .into(), + // if our parser got a URL wrong, return it as a string + Err(_) => rt_inner.as_str().into(), + } + } + _ => unreachable!(), + } } -fn convert_substitution_ref(pair: Pair) -> Result { +fn convert_substitution_ref(pair: &Pair) -> e::SubstitutionReference { let name = whitespace_normalize_name(pair.as_str()); - Ok(a::ExtraAttributes::with_extra(a::SubstitutionReference { + a::ExtraAttributes::with_extra(a::SubstitutionReference { refname: vec![at::NameToken(name)], - })) + }) } diff --git a/parser/src/conversion/tests.rs b/parser/src/conversion/tests.rs index 1bc813e..1d02977 100644 --- a/parser/src/conversion/tests.rs +++ b/parser/src/conversion/tests.rs @@ -6,28 +6,28 @@ use crate::parse; fn ssubel_to_section(ssubel: &c::StructuralSubElement) -> &e::Section { match ssubel { - c::StructuralSubElement::SubStructure(b) => match **b { - c::SubStructure::Section(ref s) => s, - ref c => panic!("Expected section, not {:?}", c), + c::StructuralSubElement::SubStructure(b) => match b.as_ref() { + c::SubStructure::Section(s) => s, + c => panic!("Expected section, not {c:?}"), }, - c => panic!("Expected SubStructure, not {:?}", c), + c => panic!("Expected SubStructure, not {c:?}"), } } fn ssubel_to_body_element(ssubel: &c::StructuralSubElement) -> &c::BodyElement { match ssubel { - c::StructuralSubElement::SubStructure(b) => match **b { - c::SubStructure::BodyElement(ref b) => b, - ref c => panic!("Expected BodyElement, not {:?}", c), + c::StructuralSubElement::SubStructure(b) => match b.as_ref() { + c::SubStructure::BodyElement(b) => b, + c => panic!("Expected BodyElement, not {c:?}"), }, - c => panic!("Expected SubStructure, not {:?}", c), + c => panic!("Expected SubStructure, not {c:?}"), } } fn body_element_to_image(bodyel: &c::BodyElement) -> &e::Image { match bodyel { c::BodyElement::Image(i) => i, - c => panic!("Expected Image, not {:?}", c), + c => panic!("Expected Image, not {c:?}"), } } @@ -58,8 +58,7 @@ fn convert_skipped_section() { assert_eq!( lvl0.len(), 3, - "Should be a paragraph and 2 sections: {:?}", - lvl0 + "Should be a paragraph and 2 sections: {lvl0:?}" ); assert_eq!( @@ -69,45 +68,40 @@ fn convert_skipped_section() { "The intro text should fit" ); - let lvl1a = ssubel_to_section(&lvl0[1]).children(); + let lvl1_a = ssubel_to_section(&lvl0[1]).children(); assert_eq!( - lvl1a.len(), + lvl1_a.len(), 2, - "The 1st lvl1 section should have (a title and) a single lvl2 section as child: {:?}", - lvl1a + "The 1st lvl1 section should have (a title and) a single lvl2 section as child: {lvl1_a:?}" ); //TODO: test title lvl1a[0] - let lvl2 = ssubel_to_section(&lvl1a[1]).children(); + let lvl2 = ssubel_to_section(&lvl1_a[1]).children(); assert_eq!( lvl2.len(), 2, - "The lvl2 section should have (a title and) a single lvl3 section as child: {:?}", - lvl2 + "The lvl2 section should have (a title and) a single lvl3 section as child: {lvl2:?}" ); //TODO: test title lvl2[0] - let lvl3a = ssubel_to_section(&lvl2[1]).children(); + let lvl3_a = ssubel_to_section(&lvl2[1]).children(); assert_eq!( - lvl3a.len(), + lvl3_a.len(), 1, - "The 1st lvl3 section should just a title: {:?}", - lvl3a + "The 1st lvl3 section should just a title: {lvl3_a:?}" ); //TODO: test title lvl3a[0] - let lvl1b = ssubel_to_section(&lvl0[2]).children(); + let lvl1_b = ssubel_to_section(&lvl0[2]).children(); assert_eq!( - lvl1b.len(), + lvl1_b.len(), 2, - "The 2nd lvl1 section should have (a title and) a single lvl2 section as child: {:?}", - lvl1b + "The 2nd lvl1 section should have (a title and) a single lvl2 section as child: {lvl1_b:?}" ); //TODO: test title lvl1b[0] - let lvl3b = ssubel_to_section(&lvl1b[1]).children(); + let lvl3_b = ssubel_to_section(&lvl1_b[1]).children(); assert_eq!( - lvl3b.len(), + lvl3_b.len(), 1, - "The 2nd lvl3 section should have just a title: {:?}", - lvl3b + "The 2nd lvl3 section should have just a title: {lvl3_b:?}" ); //TODO: test title lvl3b[0] } @@ -116,7 +110,7 @@ fn convert_skipped_section() { fn test_convert_image_scale() { let doctree = parse(".. image:: /path/to/img.jpg\n :scale: 90%\n\n").unwrap(); let lvl0 = doctree.children(); - assert_eq!(lvl0.len(), 1, "Should be a single image: {:?}", lvl0); + assert_eq!(lvl0.len(), 1, "Should be a single image: {lvl0:?}"); let be = ssubel_to_body_element(&lvl0[0]); let img = body_element_to_image(be); assert_eq!(img.extra().scale, Some(90)); diff --git a/parser/src/lib.rs b/parser/src/lib.rs index bd6cd2b..86b13af 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(clippy::pedantic)] + mod conversion; mod pair_ext_parse; mod pest_rst; @@ -16,12 +18,18 @@ use self::pest_rst::{RstParser, Rule}; use self::simplify::resolve_references; /// Parse into a document tree and resolve sections, but not references. +/// +/// # Errors +/// Returns an error if parsing fails. pub fn parse_only(source: &str) -> Result { let pairs = RstParser::parse(Rule::document, source)?; convert_document(pairs) } /// Parse into a document tree and resolve sections and references. +/// +/// # Errors +/// Returns an error if parsing fails. pub fn parse(source: &str) -> Result { parse_only(source).map(resolve_references) } diff --git a/parser/src/simplify.rs b/parser/src/simplify.rs index 21590d9..e336b22 100644 --- a/parser/src/simplify.rs +++ b/parser/src/simplify.rs @@ -67,9 +67,10 @@ struct TargetsCollected { impl TargetsCollected { fn target_url<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Url> { // TODO: Check if the target would expand circularly - if refname.len() != 1 { - panic!("Expected exactly one name in a reference."); - } + assert!( + refname.len() == 1, + "Expected exactly one name in a reference." + ); let name = refname[0].clone(); match self.named_targets.get(&name)? { NamedTargetType::ExternalLink(url) => Some(url), @@ -82,9 +83,10 @@ impl TargetsCollected { refname: &[NameToken], ) -> Option<&'t Substitution> { // TODO: Check if the substitution would expand circularly - if refname.len() != 1 { - panic!("Expected exactly one name in a substitution reference."); - } + assert!( + refname.len() == 1, + "Expected exactly one name in a substitution reference." + ); let name = refname[0].clone(); self.substitutions .get(&name) @@ -100,7 +102,7 @@ trait ResolvableRefs { } pub fn resolve_references(mut doc: Document) -> Document { - let mut references: TargetsCollected = Default::default(); + let mut references = TargetsCollected::default(); for c in doc.children() { c.populate_targets(&mut references); } @@ -164,60 +166,59 @@ where impl ResolvableRefs for c::StructuralSubElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::StructuralSubElement::*; + use c::StructuralSubElement as S; match self { - Title(e) => sub_pop(&**e, refs), - Subtitle(e) => sub_pop(&**e, refs), - Decoration(e) => sub_pop(&**e, refs), - Docinfo(e) => sub_pop(&**e, refs), - SubStructure(e) => e.populate_targets(refs), + S::Title(e) => sub_pop(e.as_ref(), refs), + S::Subtitle(e) => sub_pop(e.as_ref(), refs), + S::Decoration(e) => sub_pop(e.as_ref(), refs), + S::Docinfo(e) => sub_pop(e.as_ref(), refs), + S::SubStructure(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::StructuralSubElement::*; + use c::StructuralSubElement as S; vec![match self { - Title(e) => sub_res(*e, refs).into(), - Subtitle(e) => sub_res(*e, refs).into(), - Decoration(e) => sub_res(*e, refs).into(), - Docinfo(e) => sub_res(*e, refs).into(), - SubStructure(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), + S::Title(e) => sub_res(*e, refs).into(), + S::Subtitle(e) => sub_res(*e, refs).into(), + S::Decoration(e) => sub_res(*e, refs).into(), + S::Docinfo(e) => sub_res(*e, refs).into(), + S::SubStructure(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), }] } } impl ResolvableRefs for c::SubStructure { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubStructure::*; + use c::SubStructure as S; match self { - Topic(e) => sub_pop(&**e, refs), - Sidebar(e) => sub_pop(&**e, refs), - Transition(_) => {} - Section(e) => sub_pop(&**e, refs), - BodyElement(e) => e.populate_targets(refs), + S::Topic(e) => sub_pop(e.as_ref(), refs), + S::Sidebar(e) => sub_pop(e.as_ref(), refs), + S::Transition(_) => {} + S::Section(e) => sub_pop(e.as_ref(), refs), + S::BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubStructure::*; + use c::SubStructure as S; vec![match self { - Topic(e) => sub_res(*e, refs).into(), - Sidebar(e) => sub_res(*e, refs).into(), - Transition(e) => Transition(e), - Section(e) => sub_res(*e, refs).into(), - BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), + S::Topic(e) => sub_res(*e, refs).into(), + S::Sidebar(e) => sub_res(*e, refs).into(), + S::Transition(e) => S::Transition(e), + S::Section(e) => sub_res(*e, refs).into(), + S::BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), }] } } impl ResolvableRefs for c::BodyElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::BodyElement::*; + use c::BodyElement as B; match self { - Paragraph(e) => sub_pop(&**e, refs), - LiteralBlock(e) => sub_pop(&**e, refs), - DoctestBlock(e) => sub_pop(&**e, refs), - MathBlock(_) => {} - Rubric(e) => sub_pop(&**e, refs), - SubstitutionDefinition(e) => { + B::Paragraph(e) => sub_pop(e.as_ref(), refs), + B::LiteralBlock(e) => sub_pop(e.as_ref(), refs), + B::DoctestBlock(e) => sub_pop(e.as_ref(), refs), + B::Rubric(e) => sub_pop(e.as_ref(), refs), + B::SubstitutionDefinition(e) => { let subst = Substitution { content: e.children().clone(), ltrim: e.extra().ltrim, @@ -233,11 +234,10 @@ impl ResolvableRefs for c::BodyElement { .insert(name.0.to_lowercase(), subst.clone()); } } - Comment(_) => {} - Pending(_) => { + B::Pending(_) => { unimplemented!(); } - Target(e) => { + B::Target(e) => { if let Some(uri) = &e.extra().refuri { for name in e.names() { refs.named_targets @@ -247,147 +247,144 @@ impl ResolvableRefs for c::BodyElement { // TODO: as is, people can only refer to the target directly containing the URL. // add refid and refnames to some HashMap and follow those later. } - Raw(_) => {} - Image(_) => {} - Compound(e) => sub_pop(&**e, refs), - Container(e) => sub_pop(&**e, refs), - BulletList(e) => sub_sub_pop(&**e, refs), - EnumeratedList(e) => sub_sub_pop(&**e, refs), - DefinitionList(e) => sub_sub_pop(&**e, refs), - FieldList(e) => sub_sub_pop(&**e, refs), - OptionList(e) => sub_sub_pop(&**e, refs), - LineBlock(e) => sub_pop(&**e, refs), - BlockQuote(e) => sub_pop(&**e, refs), - Admonition(e) => sub_pop(&**e, refs), - Attention(e) => sub_pop(&**e, refs), - Hint(e) => sub_pop(&**e, refs), - Note(e) => sub_pop(&**e, refs), - Caution(e) => sub_pop(&**e, refs), - Danger(e) => sub_pop(&**e, refs), - Error(e) => sub_pop(&**e, refs), - Important(e) => sub_pop(&**e, refs), - Tip(e) => sub_pop(&**e, refs), - Warning(e) => sub_pop(&**e, refs), - Footnote(e) => sub_pop(&**e, refs), - Citation(e) => sub_pop(&**e, refs), - SystemMessage(e) => sub_pop(&**e, refs), - Figure(e) => sub_pop(&**e, refs), - Table(e) => sub_pop(&**e, refs), + B::Compound(e) => sub_pop(e.as_ref(), refs), + B::Container(e) => sub_pop(e.as_ref(), refs), + B::BulletList(e) => sub_sub_pop(e.as_ref(), refs), + B::EnumeratedList(e) => sub_sub_pop(e.as_ref(), refs), + B::DefinitionList(e) => sub_sub_pop(e.as_ref(), refs), + B::FieldList(e) => sub_sub_pop(e.as_ref(), refs), + B::OptionList(e) => sub_sub_pop(e.as_ref(), refs), + B::LineBlock(e) => sub_pop(e.as_ref(), refs), + B::BlockQuote(e) => sub_pop(e.as_ref(), refs), + B::Admonition(e) => sub_pop(e.as_ref(), refs), + B::Attention(e) => sub_pop(e.as_ref(), refs), + B::Hint(e) => sub_pop(e.as_ref(), refs), + B::Note(e) => sub_pop(e.as_ref(), refs), + B::Caution(e) => sub_pop(e.as_ref(), refs), + B::Danger(e) => sub_pop(e.as_ref(), refs), + B::Error(e) => sub_pop(e.as_ref(), refs), + B::Important(e) => sub_pop(e.as_ref(), refs), + B::Tip(e) => sub_pop(e.as_ref(), refs), + B::Warning(e) => sub_pop(e.as_ref(), refs), + B::Footnote(e) => sub_pop(e.as_ref(), refs), + B::Citation(e) => sub_pop(e.as_ref(), refs), + B::SystemMessage(e) => sub_pop(e.as_ref(), refs), + B::Figure(e) => sub_pop(e.as_ref(), refs), + B::Table(e) => sub_pop(e.as_ref(), refs), + B::MathBlock(_) | B::Comment(_) | B::Raw(_) | B::Image(_) => {} } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::BodyElement::*; + use c::BodyElement as B; vec![match self { - Paragraph(e) => sub_res(*e, refs).into(), - LiteralBlock(e) => sub_res(*e, refs).into(), - DoctestBlock(e) => sub_res(*e, refs).into(), - MathBlock(e) => MathBlock(e), - Rubric(e) => sub_res(*e, refs).into(), - SubstitutionDefinition(_) => return vec![], - Comment(e) => Comment(e), - Pending(e) => Pending(e), - Target(e) => Target(e), - Raw(e) => Raw(e), - Image(e) => Image(e), - Compound(e) => sub_res(*e, refs).into(), - Container(e) => sub_res(*e, refs).into(), - BulletList(e) => sub_sub_res(*e, refs).into(), - EnumeratedList(e) => sub_sub_res(*e, refs).into(), - DefinitionList(e) => sub_sub_res(*e, refs).into(), - FieldList(e) => sub_sub_res(*e, refs).into(), - OptionList(e) => sub_sub_res(*e, refs).into(), - LineBlock(e) => sub_res(*e, refs).into(), - BlockQuote(e) => sub_res(*e, refs).into(), - Admonition(e) => sub_res(*e, refs).into(), - Attention(e) => sub_res(*e, refs).into(), - Hint(e) => sub_res(*e, refs).into(), - Note(e) => sub_res(*e, refs).into(), - Caution(e) => sub_res(*e, refs).into(), - Danger(e) => sub_res(*e, refs).into(), - Error(e) => sub_res(*e, refs).into(), - Important(e) => sub_res(*e, refs).into(), - Tip(e) => sub_res(*e, refs).into(), - Warning(e) => sub_res(*e, refs).into(), - Footnote(e) => sub_res(*e, refs).into(), - Citation(e) => sub_res(*e, refs).into(), - SystemMessage(e) => sub_res(*e, refs).into(), - Figure(e) => sub_res(*e, refs).into(), - Table(e) => sub_res(*e, refs).into(), + B::Paragraph(e) => sub_res(*e, refs).into(), + B::LiteralBlock(e) => sub_res(*e, refs).into(), + B::DoctestBlock(e) => sub_res(*e, refs).into(), + B::MathBlock(e) => B::MathBlock(e), + B::Rubric(e) => sub_res(*e, refs).into(), + B::SubstitutionDefinition(_) => return vec![], + B::Comment(e) => B::Comment(e), + B::Pending(e) => B::Pending(e), + B::Target(e) => B::Target(e), + B::Raw(e) => B::Raw(e), + B::Image(e) => B::Image(e), + B::Compound(e) => sub_res(*e, refs).into(), + B::Container(e) => sub_res(*e, refs).into(), + B::BulletList(e) => sub_sub_res(*e, refs).into(), + B::EnumeratedList(e) => sub_sub_res(*e, refs).into(), + B::DefinitionList(e) => sub_sub_res(*e, refs).into(), + B::FieldList(e) => sub_sub_res(*e, refs).into(), + B::OptionList(e) => sub_sub_res(*e, refs).into(), + B::LineBlock(e) => sub_res(*e, refs).into(), + B::BlockQuote(e) => sub_res(*e, refs).into(), + B::Admonition(e) => sub_res(*e, refs).into(), + B::Attention(e) => sub_res(*e, refs).into(), + B::Hint(e) => sub_res(*e, refs).into(), + B::Note(e) => sub_res(*e, refs).into(), + B::Caution(e) => sub_res(*e, refs).into(), + B::Danger(e) => sub_res(*e, refs).into(), + B::Error(e) => sub_res(*e, refs).into(), + B::Important(e) => sub_res(*e, refs).into(), + B::Tip(e) => sub_res(*e, refs).into(), + B::Warning(e) => sub_res(*e, refs).into(), + B::Footnote(e) => sub_res(*e, refs).into(), + B::Citation(e) => sub_res(*e, refs).into(), + B::SystemMessage(e) => sub_res(*e, refs).into(), + B::Figure(e) => sub_res(*e, refs).into(), + B::Table(e) => sub_res(*e, refs).into(), }] } } impl ResolvableRefs for c::BibliographicElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::BibliographicElement::*; + use c::BibliographicElement as B; match self { - Author(e) => sub_pop(&**e, refs), - Authors(e) => sub_pop(&**e, refs), - Organization(e) => sub_pop(&**e, refs), - Address(e) => sub_pop(&**e, refs), - Contact(e) => sub_pop(&**e, refs), - Version(e) => sub_pop(&**e, refs), - Revision(e) => sub_pop(&**e, refs), - Status(e) => sub_pop(&**e, refs), - Date(e) => sub_pop(&**e, refs), - Copyright(e) => sub_pop(&**e, refs), - Field(e) => sub_pop(&**e, refs), + B::Author(e) => sub_pop(e.as_ref(), refs), + B::Authors(e) => sub_pop(e.as_ref(), refs), + B::Organization(e) => sub_pop(e.as_ref(), refs), + B::Address(e) => sub_pop(e.as_ref(), refs), + B::Contact(e) => sub_pop(e.as_ref(), refs), + B::Version(e) => sub_pop(e.as_ref(), refs), + B::Revision(e) => sub_pop(e.as_ref(), refs), + B::Status(e) => sub_pop(e.as_ref(), refs), + B::Date(e) => sub_pop(e.as_ref(), refs), + B::Copyright(e) => sub_pop(e.as_ref(), refs), + B::Field(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::BibliographicElement::*; + use c::BibliographicElement as B; vec![match self { - Author(e) => sub_res(*e, refs).into(), - Authors(e) => sub_res(*e, refs).into(), - Organization(e) => sub_res(*e, refs).into(), - Address(e) => sub_res(*e, refs).into(), - Contact(e) => sub_res(*e, refs).into(), - Version(e) => sub_res(*e, refs).into(), - Revision(e) => sub_res(*e, refs).into(), - Status(e) => sub_res(*e, refs).into(), - Date(e) => sub_res(*e, refs).into(), - Copyright(e) => sub_res(*e, refs).into(), - Field(e) => sub_res(*e, refs).into(), + B::Author(e) => sub_res(*e, refs).into(), + B::Authors(e) => sub_res(*e, refs).into(), + B::Organization(e) => sub_res(*e, refs).into(), + B::Address(e) => sub_res(*e, refs).into(), + B::Contact(e) => sub_res(*e, refs).into(), + B::Version(e) => sub_res(*e, refs).into(), + B::Revision(e) => sub_res(*e, refs).into(), + B::Status(e) => sub_res(*e, refs).into(), + B::Date(e) => sub_res(*e, refs).into(), + B::Copyright(e) => sub_res(*e, refs).into(), + B::Field(e) => sub_res(*e, refs).into(), }] } } impl ResolvableRefs for c::TextOrInlineElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::TextOrInlineElement::*; + use c::TextOrInlineElement as T; match self { - String(_) => {} - Emphasis(e) => sub_pop(&**e, refs), - Strong(e) => sub_pop(&**e, refs), - Literal(_) => {} - Reference(e) => sub_pop(&**e, refs), - FootnoteReference(e) => sub_pop(&**e, refs), - CitationReference(e) => sub_pop(&**e, refs), - SubstitutionReference(e) => sub_pop(&**e, refs), - TitleReference(e) => sub_pop(&**e, refs), - Abbreviation(e) => sub_pop(&**e, refs), - Acronym(e) => sub_pop(&**e, refs), - Superscript(e) => sub_pop(&**e, refs), - Subscript(e) => sub_pop(&**e, refs), - Inline(e) => sub_pop(&**e, refs), - Problematic(e) => sub_pop(&**e, refs), - Generated(e) => sub_pop(&**e, refs), - Math(_) => {} - TargetInline(_) => { + T::Emphasis(e) => sub_pop(e.as_ref(), refs), + T::Strong(e) => sub_pop(e.as_ref(), refs), + T::Reference(e) => sub_pop(e.as_ref(), refs), + T::FootnoteReference(e) => sub_pop(e.as_ref(), refs), + T::CitationReference(e) => sub_pop(e.as_ref(), refs), + T::SubstitutionReference(e) => sub_pop(e.as_ref(), refs), + T::TitleReference(e) => sub_pop(e.as_ref(), refs), + T::Abbreviation(e) => sub_pop(e.as_ref(), refs), + T::Acronym(e) => sub_pop(e.as_ref(), refs), + T::Superscript(e) => sub_pop(e.as_ref(), refs), + T::Subscript(e) => sub_pop(e.as_ref(), refs), + T::Inline(e) => sub_pop(e.as_ref(), refs), + T::Problematic(e) => sub_pop(e.as_ref(), refs), + T::Generated(e) => sub_pop(e.as_ref(), refs), + T::TargetInline(_) => { unimplemented!(); } - RawInline(_) => {} - ImageInline(_) => {} + T::String(_) | T::Literal(_) | T::Math(_) | T::RawInline(_) | T::ImageInline(_) => {} } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::TextOrInlineElement::*; + use c::TextOrInlineElement as T; + use document_tree::Problematic; + vec![match self { - String(e) => String(e), - Emphasis(e) => sub_res(*e, refs).into(), - Strong(e) => sub_res(*e, refs).into(), - Literal(e) => Literal(e), - Reference(mut e) => { + T::String(e) => T::String(e), + T::Emphasis(e) => sub_res(*e, refs).into(), + T::Strong(e) => sub_res(*e, refs).into(), + T::Literal(e) => T::Literal(e), + T::Reference(mut e) => { if e.extra().refuri.is_none() { if let Some(uri) = refs.target_url(&e.extra().refname) { e.extra_mut().refuri = Some(uri.clone()); @@ -395,14 +392,15 @@ impl ResolvableRefs for c::TextOrInlineElement { } (*e).into() } - FootnoteReference(e) => sub_res(*e, refs).into(), - CitationReference(e) => sub_res(*e, refs).into(), - SubstitutionReference(e) => match refs.substitution(&e.extra().refname) { - Some(Substitution { + T::FootnoteReference(e) => sub_res(*e, refs).into(), + T::CitationReference(e) => sub_res(*e, refs).into(), + T::SubstitutionReference(e) => { + if let Some(Substitution { content, ltrim, rtrim, - }) => { + }) = refs.substitution(&e.extra().refname) + { // (level 3 system message). // TODO: ltrim and rtrim. if *ltrim || *rtrim { @@ -410,71 +408,68 @@ impl ResolvableRefs for c::TextOrInlineElement { } return content.clone(); } - None => { - // Undefined substitution name (level 3 system message). - // TODO: This replaces the reference by a Problematic node. - // The corresponding SystemMessage node should go in a generated - // section with class "system-messages" at the end of the document. - use document_tree::Problematic; - let mut replacement: Box = Box::default(); - replacement - .children_mut() - .push(c::TextOrInlineElement::String(Box::new(format!( - "|{}|", - e.extra().refname[0].0 - )))); - // TODO: Create an ID for replacement for the system_message to reference. - // TODO: replacement.refid pointing to the system_message. - Problematic(replacement) - } - }, - TitleReference(e) => sub_res(*e, refs).into(), - Abbreviation(e) => sub_res(*e, refs).into(), - Acronym(e) => sub_res(*e, refs).into(), - Superscript(e) => sub_res(*e, refs).into(), - Subscript(e) => sub_res(*e, refs).into(), - Inline(e) => sub_res(*e, refs).into(), - Problematic(e) => sub_res(*e, refs).into(), - Generated(e) => sub_res(*e, refs).into(), - Math(e) => Math(e), - TargetInline(e) => TargetInline(e), - RawInline(e) => RawInline(e), - ImageInline(e) => ImageInline(e), + // Undefined substitution name (level 3 system message). + // TODO: This replaces the reference by a Problematic node. + // The corresponding SystemMessage node should go in a generated + // section with class "system-messages" at the end of the document. + let mut replacement: Box = Box::default(); + replacement + .children_mut() + .push(c::TextOrInlineElement::String(Box::new(format!( + "|{}|", + e.extra().refname[0].0 + )))); + // TODO: Create an ID for replacement for the system_message to reference. + // TODO: replacement.refid pointing to the system_message. + T::Problematic(replacement) + } + T::TitleReference(e) => sub_res(*e, refs).into(), + T::Abbreviation(e) => sub_res(*e, refs).into(), + T::Acronym(e) => sub_res(*e, refs).into(), + T::Superscript(e) => sub_res(*e, refs).into(), + T::Subscript(e) => sub_res(*e, refs).into(), + T::Inline(e) => sub_res(*e, refs).into(), + T::Problematic(e) => sub_res(*e, refs).into(), + T::Generated(e) => sub_res(*e, refs).into(), + T::Math(e) => T::Math(e), + T::TargetInline(e) => T::TargetInline(e), + T::RawInline(e) => T::RawInline(e), + T::ImageInline(e) => T::ImageInline(e), }] } } impl ResolvableRefs for c::AuthorInfo { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::AuthorInfo::*; + use c::AuthorInfo as A; match self { - Author(e) => sub_pop(&**e, refs), - Organization(e) => sub_pop(&**e, refs), - Address(e) => sub_pop(&**e, refs), - Contact(e) => sub_pop(&**e, refs), + A::Author(e) => sub_pop(e.as_ref(), refs), + A::Organization(e) => sub_pop(e.as_ref(), refs), + A::Address(e) => sub_pop(e.as_ref(), refs), + A::Contact(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::AuthorInfo::*; + use c::AuthorInfo as A; vec![match self { - Author(e) => sub_res(*e, refs).into(), - Organization(e) => sub_res(*e, refs).into(), - Address(e) => sub_res(*e, refs).into(), - Contact(e) => sub_res(*e, refs).into(), + A::Author(e) => sub_res(*e, refs).into(), + A::Organization(e) => sub_res(*e, refs).into(), + A::Address(e) => sub_res(*e, refs).into(), + A::Contact(e) => sub_res(*e, refs).into(), }] } } impl ResolvableRefs for c::DecorationElement { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::DecorationElement::*; + use c::DecorationElement::{Footer, Header}; match self { - Header(e) => sub_pop(&**e, refs), - Footer(e) => sub_pop(&**e, refs), + Header(e) => sub_pop(e.as_ref(), refs), + Footer(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::DecorationElement::*; + use c::DecorationElement::{Footer, Header}; vec![match self { Header(e) => sub_res(*e, refs).into(), Footer(e) => sub_res(*e, refs).into(), @@ -484,14 +479,14 @@ impl ResolvableRefs for c::DecorationElement { impl ResolvableRefs for c::SubTopic { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubTopic::*; + use c::SubTopic::{BodyElement, Title}; match self { - Title(e) => sub_pop(&**e, refs), + Title(e) => sub_pop(e.as_ref(), refs), BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubTopic::*; + use c::SubTopic::{BodyElement, Title}; match self { Title(e) => vec![sub_res(*e, refs).into()], BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), @@ -501,36 +496,36 @@ impl ResolvableRefs for c::SubTopic { impl ResolvableRefs for c::SubSidebar { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubSidebar::*; + use c::SubSidebar as S; match self { - Topic(e) => sub_pop(&**e, refs), - Title(e) => sub_pop(&**e, refs), - Subtitle(e) => sub_pop(&**e, refs), - BodyElement(e) => e.populate_targets(refs), + S::Topic(e) => sub_pop(e.as_ref(), refs), + S::Title(e) => sub_pop(e.as_ref(), refs), + S::Subtitle(e) => sub_pop(e.as_ref(), refs), + S::BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubSidebar::*; + use c::SubSidebar as S; vec![match self { - Topic(e) => sub_res(*e, refs).into(), - Title(e) => sub_res(*e, refs).into(), - Subtitle(e) => sub_res(*e, refs).into(), - BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), + S::Topic(e) => sub_res(*e, refs).into(), + S::Title(e) => sub_res(*e, refs).into(), + S::Subtitle(e) => sub_res(*e, refs).into(), + S::BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), }] } } impl ResolvableRefs for c::SubDLItem { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubDLItem::*; + use c::SubDLItem::{Classifier, Definition, Term}; match self { - Term(e) => sub_pop(&**e, refs), - Classifier(e) => sub_pop(&**e, refs), - Definition(e) => sub_pop(&**e, refs), + Term(e) => sub_pop(e.as_ref(), refs), + Classifier(e) => sub_pop(e.as_ref(), refs), + Definition(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubDLItem::*; + use c::SubDLItem::{Classifier, Definition, Term}; vec![match self { Term(e) => sub_res(*e, refs).into(), Classifier(e) => sub_res(*e, refs).into(), @@ -541,14 +536,14 @@ impl ResolvableRefs for c::SubDLItem { impl ResolvableRefs for c::SubField { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubField::*; + use c::SubField::{FieldBody, FieldName}; match self { - FieldName(e) => sub_pop(&**e, refs), - FieldBody(e) => sub_pop(&**e, refs), + FieldName(e) => sub_pop(e.as_ref(), refs), + FieldBody(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubField::*; + use c::SubField::{FieldBody, FieldName}; vec![match self { FieldName(e) => sub_res(*e, refs).into(), FieldBody(e) => sub_res(*e, refs).into(), @@ -558,14 +553,14 @@ impl ResolvableRefs for c::SubField { impl ResolvableRefs for c::SubOptionListItem { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubOptionListItem::*; + use c::SubOptionListItem::{Description, OptionGroup}; match self { - OptionGroup(e) => sub_sub_pop(&**e, refs), - Description(e) => sub_pop(&**e, refs), + OptionGroup(e) => sub_sub_pop(e.as_ref(), refs), + Description(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubOptionListItem::*; + use c::SubOptionListItem::{Description, OptionGroup}; vec![match self { OptionGroup(e) => sub_sub_res(*e, refs).into(), Description(e) => sub_res(*e, refs).into(), @@ -582,14 +577,14 @@ impl ResolvableRefs for c::SubOption { impl ResolvableRefs for c::SubLineBlock { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubLineBlock::*; + use c::SubLineBlock::{Line, LineBlock}; match self { - LineBlock(e) => sub_pop(&**e, refs), - Line(e) => sub_pop(&**e, refs), + LineBlock(e) => sub_pop(e.as_ref(), refs), + Line(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubLineBlock::*; + use c::SubLineBlock::{Line, LineBlock}; vec![match self { LineBlock(e) => sub_res(*e, refs).into(), Line(e) => sub_res(*e, refs).into(), @@ -599,14 +594,14 @@ impl ResolvableRefs for c::SubLineBlock { impl ResolvableRefs for c::SubBlockQuote { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubBlockQuote::*; + use c::SubBlockQuote::{Attribution, BodyElement}; match self { - Attribution(e) => sub_pop(&**e, refs), + Attribution(e) => sub_pop(e.as_ref(), refs), BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubBlockQuote::*; + use c::SubBlockQuote::{Attribution, BodyElement}; match self { Attribution(e) => vec![sub_res(*e, refs).into()], BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), @@ -616,14 +611,14 @@ impl ResolvableRefs for c::SubBlockQuote { impl ResolvableRefs for c::SubFootnote { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubFootnote::*; + use c::SubFootnote::{BodyElement, Label}; match self { - Label(e) => sub_pop(&**e, refs), + Label(e) => sub_pop(e.as_ref(), refs), BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubFootnote::*; + use c::SubFootnote::{BodyElement, Label}; match self { Label(e) => vec![sub_res(*e, refs).into()], BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), @@ -633,15 +628,15 @@ impl ResolvableRefs for c::SubFootnote { impl ResolvableRefs for c::SubFigure { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubFigure::*; + use c::SubFigure::{BodyElement, Caption, Legend}; match self { - Caption(e) => sub_pop(&**e, refs), - Legend(e) => sub_pop(&**e, refs), + Caption(e) => sub_pop(e.as_ref(), refs), + Legend(e) => sub_pop(e.as_ref(), refs), BodyElement(e) => e.populate_targets(refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubFigure::*; + use c::SubFigure::{BodyElement, Caption, Legend}; vec![match self { Caption(e) => sub_res(*e, refs).into(), Legend(e) => sub_res(*e, refs).into(), @@ -652,14 +647,14 @@ impl ResolvableRefs for c::SubFigure { impl ResolvableRefs for c::SubTable { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubTable::*; + use c::SubTable::{TableGroup, Title}; match self { - Title(e) => sub_pop(&**e, refs), - TableGroup(e) => sub_pop(&**e, refs), + Title(e) => sub_pop(e.as_ref(), refs), + TableGroup(e) => sub_pop(e.as_ref(), refs), } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubTable::*; + use c::SubTable::{TableGroup, Title}; vec![match self { Title(e) => sub_res(*e, refs).into(), TableGroup(e) => sub_res(*e, refs).into(), @@ -669,7 +664,7 @@ impl ResolvableRefs for c::SubTable { impl ResolvableRefs for c::SubTableGroup { fn populate_targets(&self, refs: &mut TargetsCollected) { - use c::SubTableGroup::*; + use c::SubTableGroup::{TableBody, TableColspec, TableHead}; match self { TableColspec(_) => { unimplemented!(); @@ -687,7 +682,7 @@ impl ResolvableRefs for c::SubTableGroup { } } fn resolve_refs(self, refs: &TargetsCollected) -> Vec { - use c::SubTableGroup::*; + use c::SubTableGroup::{TableBody, TableColspec, TableHead}; vec![match self { TableColspec(e) => TableColspec(e), TableHead(mut e) => { diff --git a/renderer/src/html.rs b/renderer/src/html.rs index 080eac9..38cfb9d 100644 --- a/renderer/src/html.rs +++ b/renderer/src/html.rs @@ -13,6 +13,10 @@ use document_tree::{ // static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; +/// Render document as HTML +/// +/// # Errors +/// Returns error if serialization fails pub fn render_html(document: &Document, stream: W, standalone: bool) -> Result<(), Error> where W: Write, @@ -22,7 +26,7 @@ where document.render_html(&mut renderer) } else { for c in document.children() { - (*c).render_html(&mut renderer)?; + c.render_html(&mut renderer)?; writeln!(renderer.stream)?; } Ok(()) @@ -54,7 +58,7 @@ macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => { impl HTMLRender for c::$cat { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { match self {$( - c::$cat::$member(elem) => (**elem).render_html(renderer), + c::$cat::$member(elem) => elem.render_html(renderer), )+} } } @@ -82,7 +86,7 @@ macro_rules! impl_html_render_simple { write!(renderer.stream, ">")?; if multiple_children { write!(renderer.stream, $post)?; } for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; if multiple_children { write!(renderer.stream, $post)?; } } write!(renderer.stream, "", stringify!($tag))?; @@ -110,7 +114,7 @@ impl HTMLRender for Document { { writeln!(renderer.stream, "")?; for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; writeln!(renderer.stream)?; } writeln!(renderer.stream, "")?; @@ -137,11 +141,11 @@ impl HTMLRender for e::Title { } else { renderer.level }; - write!(renderer.stream, "", level)?; + write!(renderer.stream, "")?; for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; } - write!(renderer.stream, "", level)?; + write!(renderer.stream, "")?; Ok(()) } } @@ -183,7 +187,7 @@ impl HTMLRender for e::Section { renderer.level += 1; writeln!(renderer.stream, "
", self.ids()[0].0)?; for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; writeln!(renderer.stream)?; } write!(renderer.stream, "
")?; @@ -303,13 +307,13 @@ impl HTMLRender for e::LiteralBlock { if is_code { // TODO: support those classes not being at the start if let Some(lang) = cls_iter.next() { - write!(renderer.stream, "", lang)?; + write!(renderer.stream, "")?; } else { write!(renderer.stream, "")?; } } for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; } if is_code { write!(renderer.stream, "")?; @@ -346,7 +350,7 @@ impl HTMLRender for e::Comment { { write!(renderer.stream, "")?; Ok(()) @@ -381,7 +385,7 @@ impl HTMLRender for e::Raw { let extra = self.extra(); if extra.format.contains(&at::NameToken("html".to_owned())) { for c in self.children() { - write!(renderer.stream, "{}", c)?; + write!(renderer.stream, "{c}")?; } } Ok(()) @@ -413,7 +417,7 @@ impl HTMLRender for e::SystemMessage { { write!(renderer.stream, "
System Message")?; for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; } write!(renderer.stream, "
")?; Ok(()) @@ -475,7 +479,7 @@ impl HTMLRender for e::Reference { */ write!(renderer.stream, ">")?; for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; } write!(renderer.stream, "")?; Ok(()) @@ -518,7 +522,7 @@ impl HTMLRender for e::RawInline { W: Write, { for c in self.children() { - write!(renderer.stream, "{}", c)?; + write!(renderer.stream, "{c}")?; } Ok(()) } @@ -575,7 +579,7 @@ impl HTMLRender for e::Line { W: Write, { for c in self.children() { - (*c).render_html(renderer)?; + c.render_html(renderer)?; } write!(renderer.stream, "
")?; Ok(()) diff --git a/renderer/src/html/tests.rs b/renderer/src/html/tests.rs index c8d415e..6389217 100644 --- a/renderer/src/html/tests.rs +++ b/renderer/src/html/tests.rs @@ -5,13 +5,13 @@ use rst_parser::parse; use crate::html::render_html; fn check_renders_to(rst: &str, expected: &str) { - println!("Rendering:\n{}\n---", rst); + println!("Rendering:\n{rst}\n---"); let doc = parse(rst).expect("Cannot parse"); let mut result_data: Vec = vec![]; render_html(&doc, &mut result_data, false).expect("Render error"); let result = String::from_utf8(result_data).expect("Could not decode"); assert_eq!(result.as_str().trim(), expected); - println!("{}", expected); + println!("{expected}"); } #[test] diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 9c4a105..6eb9f0c 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(clippy::pedantic)] + mod html; use std::io::Write; @@ -6,6 +8,10 @@ use anyhow::{Error, anyhow}; use document_tree::Document; +/// Render a document tree as JSON. +/// +/// # Errors +/// Returns an error if serialization fails. pub fn render_json(document: &Document, stream: W) -> Result<(), Error> where W: Write, @@ -14,6 +20,10 @@ where Ok(()) } +/// Render a document tree as XML. +/// +/// # Errors +/// Returns an error if serialization fails. pub fn render_xml(document: &Document, stream: W) -> Result<(), Error> where W: Write, diff --git a/rst/src/main.rs b/rst/src/main.rs index 5d7cf17..94a4d3b 100644 --- a/rst/src/main.rs +++ b/rst/src/main.rs @@ -1,3 +1,5 @@ +#![warn(clippy::pedantic)] + use clap::Parser; use rst_parser::parse; From 4a888922e23412c54fdb65a90fd5e3f088caf2ab Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Thu, 27 Feb 2025 10:45:36 +0100 Subject: [PATCH 2/2] simplify --- parser/src/conversion/inline.rs | 54 ++++++++++++--------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/parser/src/conversion/inline.rs b/parser/src/conversion/inline.rs index 7511310..f754533 100644 --- a/parser/src/conversion/inline.rs +++ b/parser/src/conversion/inline.rs @@ -114,42 +114,26 @@ fn convert_reference_target(concrete: Pair<'_, Rule>) -> Result) -> c::TextOrInlineElement { let rt_inner = concrete.into_inner().next().unwrap(); - match rt_inner.as_rule() { - Rule::url_auto => match Url::parse_absolute(rt_inner.as_str()) { - Ok(target) => e::Reference::new( - CommonAttributes::default(), - a::Reference { - name: None, - refuri: Some(target), - refid: None, - refname: Vec::new(), - }, - vec![rt_inner.as_str().into()], - ) - .into(), - // if our parser got a URL wrong, return it as a string - Err(_) => rt_inner.as_str().into(), - }, - Rule::email => { - let mailto_url = String::from("mailto:") + rt_inner.as_str(); - match Url::parse_absolute(&mailto_url) { - Ok(target) => e::Reference::new( - CommonAttributes::default(), - a::Reference { - name: None, - refuri: Some(target), - refid: None, - refname: Vec::new(), - }, - vec![rt_inner.as_str().into()], - ) - .into(), - // if our parser got a URL wrong, return it as a string - Err(_) => rt_inner.as_str().into(), - } - } + let str: c::TextOrInlineElement = rt_inner.as_str().into(); + let Ok(target) = (match rt_inner.as_rule() { + Rule::url_auto => Url::parse_absolute(rt_inner.as_str()), + Rule::email => Url::parse_absolute(&format!("mailto:{}", rt_inner.as_str())), _ => unreachable!(), - } + }) else { + // if our parser got a URL wrong, return it as a string + return str; + }; + e::Reference::new( + CommonAttributes::default(), + a::Reference { + name: None, + refuri: Some(target), + refid: None, + refname: Vec::new(), + }, + vec![str], + ) + .into() } fn convert_substitution_ref(pair: &Pair) -> e::SubstitutionReference {