diff --git a/src/css.rs b/src/css.rs index fa85c17..c853b9a 100644 --- a/src/css.rs +++ b/src/css.rs @@ -69,12 +69,8 @@ impl Selector { false } SelectorComponent::Element(name) => match &node.data { - Element { name: eltname, .. } => { - if name == eltname.expanded().local.deref() { - Self::do_matches(&comps[1..], node) - } else { - false - } + Element { name: eltname, .. } if name == eltname.expanded().local.deref() => { + Self::do_matches(&comps[1..], node) } _ => false, }, @@ -306,11 +302,11 @@ impl StyleData { pub(crate) fn computed_style( &self, - parent_style: &ComputedStyle, + parent_style: ComputedStyle, handle: &Handle, use_doc_css: bool, ) -> ComputedStyle { - let mut result = *parent_style; + let mut result = parent_style; for (origin, ruleset) in [ (StyleOrigin::Agent, &self.agent_rules), @@ -425,9 +421,9 @@ impl StyleData { } } -fn pending<'a, F>(handle: Handle, f: F) -> TreeMapResult<'a, (), Handle, Vec> +fn pending(handle: Handle, f: F) -> TreeMapResult<'static, (), Handle, Vec> where - for<'r> F: Fn(&'r mut (), Vec>) -> Result>> + 'static, + F: Fn(&mut (), Vec>) -> Result>> + 'static, { TreeMapResult::PendingChildren { children: handle.children.borrow().clone(), @@ -451,10 +447,10 @@ fn combine_vecs(vecs: Vec>) -> Vec { } } -fn extract_style_nodes<'a, T: Write>( +fn extract_style_nodes( handle: Handle, _err_out: &mut T, -) -> TreeMapResult<'a, (), Handle, Vec> { +) -> TreeMapResult<'static, (), Handle, Vec> { use TreeMapResult::*; match handle.clone().data { @@ -467,7 +463,7 @@ fn extract_style_nodes<'a, T: Write>( // Assume just a flat text node for child in handle.children.borrow().iter() { if let NodeData::Text { ref contents } = child.data { - result += &String::from(contents.borrow().deref()); + result += &contents.borrow(); } } Finished(vec![result]) diff --git a/src/css/parser.rs b/src/css/parser.rs index eac743b..eeecf80 100644 --- a/src/css/parser.rs +++ b/src/css/parser.rs @@ -371,12 +371,12 @@ fn parse_token_not_semicolon(text: &str) -> IResult<&str, Token> { fn parse_value(text: &str) -> IResult<&str, RawValue> { let (rest, mut tokens) = many0(parse_token_not_semicolon)(text)?; let mut important = false; - if tokens.len() >= 2 - && tokens[tokens.len() - 2..] == [Token::Delim('!'), Token::Ident("important".into())] - { - tokens.pop(); - tokens.pop(); - important = true; + if let [.., Token::Delim('!'), Token::Ident(x)] = &tokens[..] { + if x == "important" { + tokens.pop(); + tokens.pop(); + important = true; + } } Ok((rest, RawValue { tokens, important })) } @@ -492,21 +492,18 @@ fn parse_color(tokens: &[Token]) -> Result { + [Token::Function(name), rgb_args @ .., Token::CloseRound] => { use Token::*; match name.deref() { - "rgb" => { - let rgb_args = &tokens[1..tokens.len() - 1]; - match rgb_args { - [Number(r), Comma, Number(g), Comma, Number(b)] => { - let r = r.parse().map_err(|_e| empty_fail())?; - let g = g.parse().map_err(|_e| empty_fail())?; - let b = b.parse().map_err(|_e| empty_fail())?; - Ok(Colour::Rgb(r, g, b)) - } - _ => Err(empty_fail()), + "rgb" => match rgb_args { + [Number(r), Comma, Number(g), Comma, Number(b)] => { + let r = r.parse().map_err(|_e| empty_fail())?; + let g = g.parse().map_err(|_e| empty_fail())?; + let b = b.parse().map_err(|_e| empty_fail())?; + Ok(Colour::Rgb(r, g, b)) } - } + _ => Err(empty_fail()), + }, _ => Err(empty_fail()), } } diff --git a/src/lib.rs b/src/lib.rs index e025398..d766b9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,7 +177,7 @@ impl PartialOrd for Specificity { } #[derive(Clone, Copy, Debug)] -pub(crate) struct WithSpec { +pub(crate) struct WithSpec { val: Option, origin: StyleOrigin, specificity: Specificity, @@ -271,7 +271,7 @@ pub enum Error { Fail, /// An I/O error #[error("I/O error")] - IoError(#[from] std::io::Error), + IoError(#[from] io::Error), } impl PartialEq for Error { @@ -291,17 +291,6 @@ impl Eq for Error {} type Result = std::result::Result; -/// A dummy writer which does nothing -struct Discard {} -impl Write for Discard { - fn write(&mut self, bytes: &[u8]) -> std::result::Result { - Ok(bytes.len()) - } - fn flush(&mut self) -> std::result::Result<(), io::Error> { - Ok(()) - } -} - const MIN_WIDTH: usize = 3; /// Size information/estimate @@ -358,15 +347,16 @@ struct RenderTableCell { impl RenderTableCell { /// Calculate or return the estimate size of the cell fn get_size_estimate(&self) -> SizeEstimate { - if self.size_estimate.get().is_none() { + let Some(size) = self.size_estimate.get() else { let size = self .content .iter() .map(|node| node.get_size_estimate()) .fold(Default::default(), SizeEstimate::add); self.size_estimate.set(Some(size)); - } - self.size_estimate.get().unwrap() + return size; + }; + size } } @@ -636,7 +626,7 @@ impl RenderNode { fn calc_size_estimate( &self, context: &HtmlContext, - decorator: &'_ D, + decorator: &D, ) -> SizeEstimate { // If it's already calculated, then just return the answer. if let Some(s) = self.size_estimate.get() { @@ -791,16 +781,16 @@ impl RenderNode { } } -fn precalc_size_estimate<'a, 'b: 'a, D: TextDecorator>( +fn precalc_size_estimate<'a, D: TextDecorator>( node: &'a RenderNode, context: &mut HtmlContext, - decorator: &'b D, -) -> Result> { + decorator: &'a D, +) -> TreeMapResult<'a, HtmlContext, &'a RenderNode, ()> { use RenderNodeInfo::*; if node.size_estimate.get().is_some() { - return Ok(TreeMapResult::Nothing); + return TreeMapResult::Nothing; } - Ok(match node.info { + match node.info { Text(_) | Img(_, _) | Break | FragStart(_) => { let _ = node.calc_size_estimate(context, decorator); TreeMapResult::Nothing @@ -850,7 +840,7 @@ fn precalc_size_estimate<'a, 'b: 'a, D: TextDecorator>( } } TableRow(..) | TableBody(_) | TableCell(_) => unimplemented!(), - }) + } } /// Make a Vec of RenderNodes from the children of a node. @@ -914,12 +904,12 @@ fn table_to_render_tree<'a, T: Write>( } } if rows.is_empty() { - Ok(None) + None } else { - Ok(Some(RenderNode::new_styled( + Some(RenderNode::new_styled( RenderNodeInfo::Table(RenderTable::new(rows)), computed, - ))) + )) } }) } @@ -970,10 +960,10 @@ fn tbody_to_render_tree<'a, T: Write>( } } - Ok(Some(RenderNode::new_styled( + Some(RenderNode::new_styled( RenderNodeInfo::TableBody(rows), computed, - ))) + )) }) } @@ -995,7 +985,7 @@ fn tr_to_render_tree<'a, T: Write>( } }) .collect(); - Ok(Some(RenderNode::new_styled( + Some(RenderNode::new_styled( RenderNodeInfo::TableRow( RenderTableRow { cells, @@ -1005,7 +995,7 @@ fn tr_to_render_tree<'a, T: Write>( false, ), computed, - ))) + )) }) } @@ -1025,7 +1015,7 @@ fn td_to_render_tree<'a, T: Write>( } } pending(input, move |_, children| { - Ok(Some(RenderNode::new_styled( + Some(RenderNode::new_styled( RenderNodeInfo::TableCell(RenderTableCell { colspan, content: children, @@ -1034,7 +1024,7 @@ fn td_to_render_tree<'a, T: Write>( style: computed, }), computed, - ))) + )) }) } @@ -1073,7 +1063,7 @@ fn tree_map_reduce<'a, C, N, R, M>( mut process_node: M, ) -> Result> where - M: for<'c> FnMut(&'c mut C, N) -> Result>, + M: FnMut(&mut C, N) -> Result>, { /// A node partially decoded, waiting for its children to /// be processed. @@ -1101,14 +1091,15 @@ where let mut pending_stack = Vec::new(); loop { // Get the next child node to process - if let Some(h) = last.to_process.next() { - last.prefn - .as_ref() - .map(|ref f| f(context, &h)) - .transpose()?; + while let Some(h) = last.to_process.next() { + if let Some(f) = &last.prefn { + f(context, &h)?; + } match process_node(context, h)? { TreeMapResult::Finished(result) => { - last.postfn.as_ref().map(|ref f| f(context, &result)); + if let Some(f) = &last.postfn { + f(context, &result)?; + } last.children.push(result); } TreeMapResult::PendingChildren { @@ -1128,27 +1119,20 @@ where } TreeMapResult::Nothing => {} }; - } else { - // No more children, so finally construct the parent. - let reduced = (last.construct)(context, last.children)?; - let parent = pending_stack.pop(); - if let Some(node) = reduced { - if let Some(mut parent) = parent { - parent.postfn.as_ref().map(|ref f| f(context, &node)); - parent.children.push(node); - last = parent; - } else { - // Finished the whole stack! - break Ok(Some(node)); - } - } else { - /* Finished the stack, and have nothing */ - match parent { - Some(parent) => last = parent, - None => break Ok(None), + } + // No more children, so finally construct the parent. + if let Some(mut parent) = pending_stack.pop() { + if let Some(node) = (last.construct)(context, last.children)? { + if let Some(f) = &parent.postfn { + f(context, &node)?; } + parent.children.push(node); } + last = parent; + continue; } + // Finished the whole stack! + break Ok((last.construct)(context, last.children)?); } } @@ -1216,29 +1200,27 @@ fn dom_to_render_tree_with_context( result } -fn pending<'a, F>( +fn pending( input: RenderInput, f: F, -) -> TreeMapResult<'a, HtmlContext, RenderInput, RenderNode> +) -> TreeMapResult<'static, HtmlContext, RenderInput, RenderNode> where - for<'r> F: - Fn(&'r mut HtmlContext, std::vec::Vec) -> Result> + 'static, + F: Fn(&mut HtmlContext, Vec) -> Option + 'static, { TreeMapResult::PendingChildren { children: input.children(), - cons: Box::new(f), + cons: Box::new(move |ctx, children| Ok(f(ctx, children))), prefn: None, postfn: None, } } -fn pending_noempty<'a, F>( +fn pending_noempty( input: RenderInput, f: F, -) -> TreeMapResult<'a, HtmlContext, RenderInput, RenderNode> +) -> TreeMapResult<'static, HtmlContext, RenderInput, RenderNode> where - for<'r> F: - Fn(&'r mut HtmlContext, std::vec::Vec) -> Result> + 'static, + F: Fn(&mut HtmlContext, Vec) -> Option + 'static, { let handle = &input.handle; let style = &input.parent_style; @@ -1256,7 +1238,7 @@ where if children.is_empty() { Ok(None) } else { - f(ctx, children) + Ok(f(ctx, children)) } }), prefn: None, @@ -1324,19 +1306,17 @@ fn prepend_marker(prefix: RenderNode, mut orig: RenderNode) -> RenderNode { orig } -fn process_dom_node<'a, T: Write>( +fn process_dom_node( input: RenderInput, err_out: &mut T, #[allow(unused)] // Used with css feature context: &mut HtmlContext, -) -> Result> { +) -> Result> { use RenderNodeInfo::*; use TreeMapResult::*; Ok(match input.handle.clone().data { - Document => pending(input, |_context, cs| { - Ok(Some(RenderNode::new(Container(cs)))) - }), + Document => pending(input, |_context, cs| Some(RenderNode::new(Container(cs)))), Comment { .. } => Nothing, Element { ref name, @@ -1355,7 +1335,7 @@ fn process_dom_node<'a, T: Write>( let computed = context .style_data - .computed_style(parent_style, handle, context.use_doc_css); + .computed_style(**parent_style, handle, context.use_doc_css); if let Some(true) = computed.display_none.val() { return Ok(Nothing); } @@ -1368,7 +1348,7 @@ fn process_dom_node<'a, T: Write>( expanded_name!(html "html") | expanded_name!(html "body") => { /* process children, but don't add anything */ pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Container(cs), computed))) + Some(RenderNode::new_styled(Container(cs), computed)) }) } expanded_name!(html "link") @@ -1383,7 +1363,7 @@ fn process_dom_node<'a, T: Write>( expanded_name!(html "span") => { /* process children, but don't add anything */ pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Container(cs), computed))) + Some(RenderNode::new_styled(Container(cs), computed)) }) } expanded_name!(html "a") => { @@ -1419,18 +1399,18 @@ fn process_dom_node<'a, T: Write>( expanded_name!(html "em") | expanded_name!(html "i") | expanded_name!(html "ins") => pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Em(cs), computed))) + Some(RenderNode::new_styled(Em(cs), computed)) }), expanded_name!(html "strong") => pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Strong(cs), computed))) + Some(RenderNode::new_styled(Strong(cs), computed)) }), expanded_name!(html "s") | expanded_name!(html "del") => { pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Strikeout(cs), computed))) + Some(RenderNode::new_styled(Strikeout(cs), computed)) }) } expanded_name!(html "code") => pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Code(cs), computed))) + Some(RenderNode::new_styled(Code(cs), computed)) }), expanded_name!(html "img") => { let borrowed = attrs.borrow(); @@ -1462,20 +1442,20 @@ fn process_dom_node<'a, T: Write>( | expanded_name!(html "h4") => { let level: usize = name.local[1..].parse().unwrap(); pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Header(level, cs), computed))) + Some(RenderNode::new_styled(Header(level, cs), computed)) }) } expanded_name!(html "p") => pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Block(cs), computed))) + Some(RenderNode::new_styled(Block(cs), computed)) }), expanded_name!(html "li") => pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(ListItem(cs), computed))) + Some(RenderNode::new_styled(ListItem(cs), computed)) }), expanded_name!(html "sup") => pending(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Sup(cs), computed))) + Some(RenderNode::new_styled(Sup(cs), computed)) }), expanded_name!(html "div") => pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Div(cs), computed))) + Some(RenderNode::new_styled(Div(cs), computed)) }), expanded_name!(html "pre") => pending(input, move |_, cs| { let mut computed = computed; @@ -1486,7 +1466,7 @@ fn process_dom_node<'a, T: Write>( WhiteSpace::Pre, ); computed.internal_pre = true; - Ok(Some(RenderNode::new_styled(Block(cs), computed))) + Some(RenderNode::new_styled(Block(cs), computed)) }), expanded_name!(html "br") => Finished(RenderNode::new_styled(Break, computed)), expanded_name!(html "table") => table_to_render_tree(input, computed, err_out), @@ -1498,10 +1478,10 @@ fn process_dom_node<'a, T: Write>( td_to_render_tree(input, computed, err_out) } expanded_name!(html "blockquote") => pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(BlockQuote(cs), computed))) + Some(RenderNode::new_styled(BlockQuote(cs), computed)) }), expanded_name!(html "ul") => pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Ul(cs), computed))) + Some(RenderNode::new_styled(Ul(cs), computed)) }), expanded_name!(html "ol") => { let borrowed = attrs.borrow(); @@ -1520,7 +1500,7 @@ fn process_dom_node<'a, T: Write>( .into_iter() .filter(|n| matches!(n.info, RenderNodeInfo::ListItem(..))) .collect(); - Ok(Some(RenderNode::new_styled(Ol(start, cs), computed))) + Some(RenderNode::new_styled(Ol(start, cs), computed)) }) } expanded_name!(html "dl") => Finished(RenderNode::new_styled( @@ -1534,9 +1514,8 @@ fn process_dom_node<'a, T: Write>( _ => { html_trace!("Unhandled element: {:?}\n", name.local); pending_noempty(input, move |_, cs| { - Ok(Some(RenderNode::new_styled(Container(cs), computed))) + Some(RenderNode::new_styled(Container(cs), computed)) }) - //None } }; @@ -1549,35 +1528,32 @@ fn process_dom_node<'a, T: Write>( } } - let result = if let Some(fragname) = fragment { - match result { - Finished(node) => { - Finished(prepend_marker(RenderNode::new(FragStart(fragname)), node)) - } - Nothing => Finished(RenderNode::new(FragStart(fragname))), - PendingChildren { - children, - cons, - prefn, - postfn, - } => PendingChildren { - children, - prefn, - postfn, - cons: Box::new(move |ctx, ch| { - let fragnode = RenderNode::new(FragStart(fragname)); - match cons(ctx, ch)? { - None => Ok(Some(fragnode)), - Some(node) => Ok(Some(prepend_marker(fragnode, node))), - } - }), - }, - } - } else { - result + let Some(fragname) = fragment else { + return Ok(result); }; - - result + match result { + Finished(node) => { + Finished(prepend_marker(RenderNode::new(FragStart(fragname)), node)) + } + Nothing => Finished(RenderNode::new(FragStart(fragname))), + PendingChildren { + children, + cons, + prefn, + postfn, + } => PendingChildren { + children, + prefn, + postfn, + cons: Box::new(move |ctx, ch| { + let fragnode = RenderNode::new(FragStart(fragname)); + match cons(ctx, ch)? { + None => Ok(Some(fragnode)), + Some(node) => Ok(Some(prepend_marker(fragnode, node))), + } + }), + }, + } } markup5ever_rcdom::NodeData::Text { contents: ref tstr } => { Finished(RenderNode::new(Text((&*tstr.borrow()).into()))) @@ -1598,13 +1574,14 @@ fn render_tree_to_string( err_out: &mut T, ) -> Result> { /* Phase 1: get size estimates. */ + // can't actually error, but Ok-wrap to satisfy tree_map_reduce signature tree_map_reduce(context, &tree, |context, node| { - precalc_size_estimate(node, context, decorator) + Ok(precalc_size_estimate(node, context, decorator)) })?; /* Phase 2: actually render. */ let mut renderer = TextRenderer::new(renderer); tree_map_reduce(&mut renderer, tree, |renderer, node| { - do_render_node(renderer, node, err_out) + Ok(do_render_node(renderer, node, err_out)?) })?; let (mut renderer, links) = renderer.into_inner(); let lines = renderer.finalise(links); @@ -1617,7 +1594,6 @@ fn render_tree_to_string( } fn pending2< - 'a, D: TextDecorator, F: FnOnce( &mut TextRenderer, @@ -1627,7 +1603,7 @@ fn pending2< >( children: Vec, f: F, -) -> TreeMapResult<'a, TextRenderer, RenderNode, Option>> { +) -> TreeMapResult<'static, TextRenderer, RenderNode, Option>> { TreeMapResult::PendingChildren { children, cons: Box::new(f), @@ -1650,35 +1626,25 @@ impl PushedStyleInfo { fn apply(render: &mut TextRenderer, style: &ComputedStyle) -> Self { #[allow(unused_mut)] let mut result: PushedStyleInfo = Default::default(); - { - #[cfg(feature = "css")] - if let Some(col) = style.colour.val() { - render.push_colour(col); - result.colour = true; - } - #[cfg(feature = "css")] - if let Some(col) = style.bg_colour.val() { - render.push_bgcolour(col); - result.bgcolour = true; - } - if let Some(ws) = style.white_space.val() { - match ws { - WhiteSpace::Normal => {} - WhiteSpace::Pre | WhiteSpace::PreWrap => { - render.push_ws(ws); - result.white_space = true; - } - } - } - if style.internal_pre { - render.push_preformat(); - result.preformat = true; + #[cfg(feature = "css")] + if let Some(col) = style.colour.val() { + render.push_colour(col); + result.colour = true; + } + #[cfg(feature = "css")] + if let Some(col) = style.bg_colour.val() { + render.push_bgcolour(col); + result.bgcolour = true; + } + if let Some(ws) = style.white_space.val() { + if let WhiteSpace::Pre | WhiteSpace::PreWrap = ws { + render.push_ws(ws); + result.white_space = true; } } - #[cfg(not(feature = "css"))] - { - let _ = render; - let _ = style; + if style.internal_pre { + render.push_preformat(); + result.preformat = true; } result } @@ -1702,7 +1668,7 @@ fn do_render_node( renderer: &mut TextRenderer, tree: RenderNode, err_out: &mut T, -) -> Result, RenderNode, Option>>> { +) -> render::Result, RenderNode, Option>>> { html_trace!("do_render_node({:?}", tree); use RenderNodeInfo::*; use TreeMapResult::*; @@ -1944,10 +1910,10 @@ fn do_render_node( // Special case for digit-only superscripts - use superscript // characters. fn sup_digits(children: &[RenderNode]) -> Option { - if children.len() != 1 { + let [node] = children else { return None; - } - if let Text(s) = &children[0].info { + }; + if let Text(s) = &node.info { if s.chars().all(|d| d.is_ascii_digit()) { // It's just a string of digits - replace by superscript characters. const SUPERSCRIPTS: [char; 10] = @@ -1981,7 +1947,7 @@ fn render_table_tree( renderer: &mut TextRenderer, table: RenderTable, _err_out: &mut T, -) -> Result, RenderNode, Option>>> { +) -> render::Result, RenderNode, Option>>> { /* Now lay out the table. */ let num_columns = table.num_columns; @@ -2080,8 +2046,8 @@ fn render_table_tree( Ok(TreeMapResult::PendingChildren { children: table.into_rows(col_widths, vert_row), cons: Box::new(|_, _| Ok(Some(None))), - prefn: Some(Box::new(|_, _| Ok(()))), - postfn: Some(Box::new(|_, _| Ok(()))), + prefn: None, + postfn: None, }) } @@ -2158,8 +2124,9 @@ fn render_table_cell( pub mod config { //! Configure the HTML to text translation using the `Config` type, which can be //! constructed using one of the functions in this module. + use std::io; - use super::{Discard, Error}; + use super::Error; #[cfg(feature = "css")] use crate::css::StyleData; use crate::{ @@ -2208,7 +2175,7 @@ pub mod config { } } /// Parse with context. - fn do_parse( + fn do_parse( &mut self, context: &mut HtmlContext, input: R, @@ -2217,7 +2184,7 @@ pub mod config { } /// Parse the HTML into a DOM structure. - pub fn parse_html(&self, mut input: R) -> Result { + pub fn parse_html(&self, mut input: R) -> Result { use html5ever::tendril::TendrilSink; let opts = super::ParseOpts { tree_builder: super::TreeBuilderOpts { @@ -2236,7 +2203,7 @@ pub mod config { Ok(RenderTree( super::dom_to_render_tree_with_context( dom.document.clone(), - &mut Discard {}, + &mut io::sink(), &mut self.make_context(), )? .ok_or(Error::Fail)?, @@ -2245,13 +2212,14 @@ pub mod config { /// Render an existing RenderTree into a string. pub fn render_to_string(&self, render_tree: RenderTree, width: usize) -> Result { - render_tree + let s = render_tree .render_with_context( &mut self.make_context(), width, self.decorator.make_subblock_decorator(), )? - .into_string() + .into_string()?; + Ok(s) } /// Take an existing RenderTree, and returns text wrapped to `width` columns. @@ -2280,9 +2248,11 @@ pub mod config { width: usize, ) -> Result { let mut context = self.make_context(); - self.do_parse(&mut context, input)? + let s = self + .do_parse(&mut context, input)? .render_with_context(&mut context, width, self.decorator)? - .into_string() + .into_string()?; + Ok(s) } /// Reads HTML from `input`, and returns text wrapped to `width` columns. @@ -2517,7 +2487,7 @@ impl RenderTree { let test_decorator = decorator.make_subblock_decorator(); let builder = SubRenderer::new(width, render_options, decorator); let builder = - render_tree_to_string(context, builder, &test_decorator, self.0, &mut Discard {})?; + render_tree_to_string(context, builder, &test_decorator, self.0, &mut io::sink())?; Ok(RenderedText(builder)) } @@ -2532,7 +2502,7 @@ struct RenderedText(SubRenderer); impl RenderedText { /// Convert the rendered HTML document to a string. - fn into_string(self) -> Result { + fn into_string(self) -> render::Result { self.0.into_string() } @@ -2560,7 +2530,7 @@ fn parse_with_context(mut input: impl io::Read, context: &mut HtmlContext) -> Re .from_utf8() .read_from(&mut input)?; let render_tree = - dom_to_render_tree_with_context(dom.document.clone(), &mut Discard {}, context)? + dom_to_render_tree_with_context(dom.document.clone(), &mut io::sink(), context)? .ok_or(Error::Fail)?; Ok(RenderTree(render_tree)) } diff --git a/src/markup5ever_rcdom.rs b/src/markup5ever_rcdom.rs index 56782d4..92b53dd 100644 --- a/src/markup5ever_rcdom.rs +++ b/src/markup5ever_rcdom.rs @@ -168,23 +168,20 @@ fn append(new_parent: &Handle, child: Handle) { /// If the node has a parent, get it and this node's position in its children fn get_parent_and_index(target: &Handle) -> Option<(Handle, usize)> { - if let Some(weak) = target.parent.take() { - let parent = weak.upgrade().expect("dangling weak pointer"); - target.parent.set(Some(weak)); - let i = match parent - .children - .borrow() - .iter() - .enumerate() - .find(|&(_, child)| Rc::ptr_eq(child, target)) - { - Some((i, _)) => i, - None => panic!("have parent but couldn't find in parent's children!"), - }; - Some((parent, i)) - } else { - None - } + let weak = target.parent.take()?; + let parent = weak.upgrade().expect("dangling weak pointer"); + target.parent.set(Some(weak)); + let i = match parent + .children + .borrow() + .iter() + .enumerate() + .find(|&(_, child)| Rc::ptr_eq(child, target)) + { + Some((i, _)) => i, + None => panic!("have parent but couldn't find in parent's children!"), + }; + Some((parent, i)) } fn append_to_existing_text(prev: &Handle, text: &str) -> bool { diff --git a/src/render/mod.rs b/src/render/mod.rs index c684563..80f9df8 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -2,7 +2,6 @@ //! particular text output. use crate::Colour; -use crate::Error; use crate::WhiteSpace; pub(crate) mod text_renderer; @@ -12,36 +11,47 @@ pub use text_renderer::{ TrivialDecorator, }; +pub(crate) type Result = std::result::Result; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct TooNarrow; + +impl From for crate::Error { + fn from(_: TooNarrow) -> crate::Error { + crate::Error::TooNarrow + } +} + /// A type which is a backend for HTML to text rendering. pub(crate) trait Renderer { /// Add an empty line to the output (ie between blocks). - fn add_empty_line(&mut self) -> crate::Result<()>; + fn add_empty_line(&mut self) -> Result<()>; /// Create a sub-renderer for nested blocks. - fn new_sub_renderer(&self, width: usize) -> crate::Result + fn new_sub_renderer(&self, width: usize) -> Result where Self: Sized; /// Start a new block. - fn start_block(&mut self) -> crate::Result<()>; + fn start_block(&mut self) -> Result<()>; /// Mark the end of a block. fn end_block(&mut self); /// Start a new line, if necessary (but don't add a new line). - fn new_line(&mut self) -> Result<(), Error>; + fn new_line(&mut self) -> Result<()>; /// Start a new line. - fn new_line_hard(&mut self) -> Result<(), Error>; + fn new_line_hard(&mut self) -> Result<()>; /// Add a horizontal table border. - fn add_horizontal_border(&mut self) -> Result<(), Error>; + fn add_horizontal_border(&mut self) -> Result<()>; /// Add a horizontal border which is not the full width fn add_horizontal_border_width( &mut self, #[allow(unused_variables)] width: usize, - ) -> Result<(), Error> { + ) -> Result<()> { self.add_horizontal_border() } @@ -58,18 +68,16 @@ pub(crate) trait Renderer { /// End the current white-space setting. fn pop_ws(&mut self); - // - /// Add some inline text (which should be wrapped at the /// appropriate width) to the current block. - fn add_inline_text(&mut self, text: &str) -> crate::Result<()>; + fn add_inline_text(&mut self, text: &str) -> Result<()>; /// Return the current width in character cells fn width(&self) -> usize; /// Add a new block from a sub renderer, and prefix every line by the /// corresponding text from each iteration of prefixes. - fn append_subrender<'a, I>(&mut self, other: Self, prefixes: I) -> Result<(), Error> + fn append_subrender<'a, I>(&mut self, other: Self, prefixes: I) -> Result<()> where I: Iterator; @@ -77,14 +85,14 @@ pub(crate) trait Renderer { /// and add a horizontal line below. /// If collapse is true, then merge top/bottom borders of the subrenderer /// with the surrounding one. - fn append_columns_with_borders(&mut self, cols: I, collapse: bool) -> Result<(), Error> + fn append_columns_with_borders(&mut self, cols: I, collapse: bool) -> Result<()> where I: IntoIterator, Self: Sized; /// Append a set of sub renderers joined vertically with lines, for tables /// which would otherwise be too wide for the screen. - fn append_vert_row(&mut self, cols: I) -> Result<(), Error> + fn append_vert_row(&mut self, cols: I) -> Result<()> where I: IntoIterator, Self: Sized; @@ -95,37 +103,37 @@ pub(crate) trait Renderer { /// Start a hyperlink /// TODO: return sub-builder or similar to make misuse /// of start/link harder? - fn start_link(&mut self, target: &str) -> crate::Result<()>; + fn start_link(&mut self, target: &str) -> Result<()>; /// Finish a hyperlink started earlier. - fn end_link(&mut self) -> crate::Result<()>; + fn end_link(&mut self) -> Result<()>; /// Start an emphasised region - fn start_emphasis(&mut self) -> crate::Result<()>; + fn start_emphasis(&mut self) -> Result<()>; /// Finish emphasised text started earlier. - fn end_emphasis(&mut self) -> crate::Result<()>; + fn end_emphasis(&mut self) -> Result<()>; /// Start a strong region - fn start_strong(&mut self) -> crate::Result<()>; + fn start_strong(&mut self) -> Result<()>; /// Finish strong text started earlier. - fn end_strong(&mut self) -> crate::Result<()>; + fn end_strong(&mut self) -> Result<()>; /// Start a strikeout region - fn start_strikeout(&mut self) -> crate::Result<()>; + fn start_strikeout(&mut self) -> Result<()>; /// Finish strikeout text started earlier. - fn end_strikeout(&mut self) -> crate::Result<()>; + fn end_strikeout(&mut self) -> Result<()>; /// Start a code region - fn start_code(&mut self) -> crate::Result<()>; + fn start_code(&mut self) -> Result<()>; /// End a code region - fn end_code(&mut self) -> crate::Result<()>; + fn end_code(&mut self) -> Result<()>; /// Add an image - fn add_image(&mut self, src: &str, title: &str) -> crate::Result<()>; + fn add_image(&mut self, src: &str, title: &str) -> Result<()>; /// Get prefix string of header in specific level. fn header_prefix(&mut self, level: usize) -> String; @@ -159,8 +167,8 @@ pub(crate) trait Renderer { fn pop_bgcolour(&mut self); /// Start a section of superscript text. - fn start_superscript(&mut self) -> crate::Result<()>; + fn start_superscript(&mut self) -> Result<()>; /// End a section of superscript text. - fn end_superscript(&mut self) -> crate::Result<()>; + fn end_superscript(&mut self) -> Result<()>; } diff --git a/src/render/text_renderer.rs b/src/render/text_renderer.rs index b59c4a7..8680658 100644 --- a/src/render/text_renderer.rs +++ b/src/render/text_renderer.rs @@ -4,10 +4,11 @@ //! into different text formats. use crate::Colour; -use crate::Error; use crate::WhiteSpace; use super::Renderer; +use super::Result; +use super::TooNarrow; use std::cell::Cell; use std::mem; use std::ops::Deref; @@ -52,7 +53,7 @@ impl TextRenderer { // hack overloads start_link method otherwise coming from the Renderer trait // impl on SubRenderer /// Add link to global link collection - pub fn start_link(&mut self, target: &str) -> crate::Result<()> { + pub fn start_link(&mut self, target: &str) -> Result<()> { self.links.push(target.to_string()); self.subrender.last_mut().unwrap().start_link(target)?; Ok(()) @@ -162,9 +163,10 @@ impl TaggedLine { } /// Join the line into a String, ignoring the tags and markers. - fn into_string(self) -> String { + #[allow(clippy::inherent_to_string)] + fn to_string(&self) -> String { let mut s = String::new(); - for tle in self.v { + for tle in &self.v { if let TaggedLineElement::Str(ts) = tle { s.push_str(&ts.s); } @@ -260,8 +262,7 @@ impl TaggedLine { } /// Iterator over the chars in this line. - #[allow(clippy::needless_lifetimes)] - pub fn chars<'a>(&'a self) -> impl Iterator + 'a { + pub fn chars(&self) -> impl Iterator + '_ { use self::TaggedLineElement::Str; self.v.iter().flat_map(|tle| { @@ -273,12 +274,6 @@ impl TaggedLine { }) } - #[cfg(feature = "html_trace")] - /// Return a string contents for debugging. - fn to_string(&self) -> String { - self.chars().collect() - } - /// Iterator over TaggedLineElements pub fn iter(&self) -> impl Iterator> + '_ { self.v.iter() @@ -352,7 +347,7 @@ impl WrappedBlock { } } - fn flush_word(&mut self, ws_mode: WhiteSpace) -> Result<(), Error> { + fn flush_word(&mut self, ws_mode: WhiteSpace) -> Result<()> { use self::TaggedLineElement::Str; /* Finish the word. */ @@ -429,7 +424,7 @@ impl WrappedBlock { // Write the current word out, hard-wrapped. (This may originally be pre-formatted, // or be a word which just doesn't fit on the line.) - fn flush_word_hard_wrap(&mut self) -> Result<(), Error> { + fn flush_word_hard_wrap(&mut self) -> Result<()> { use self::TaggedLineElement::Str; let mut lineleft = self.width - self.line.len; @@ -456,7 +451,7 @@ impl WrappedBlock { wpos += c_w; break; } else { - return Err(Error::TooNarrow); + return Err(TooNarrow); } } split_idx = idx; @@ -508,7 +503,7 @@ impl WrappedBlock { self.text.push(tmp_line); } - fn flush(&mut self) -> Result<(), Error> { + fn flush(&mut self) -> Result<()> { self.flush_word(WhiteSpace::Normal)?; self.flush_line(); Ok(()) @@ -532,7 +527,7 @@ impl WrappedBlock { */ /// Consume self and return vector of lines including annotations. - pub fn into_lines(mut self) -> Result>, Error> { + pub fn into_lines(mut self) -> Result>> { self.flush()?; Ok(self.text) @@ -544,7 +539,7 @@ impl WrappedBlock { ws_mode: WhiteSpace, main_tag: &T, wrap_tag: &T, - ) -> Result<(), Error> { + ) -> Result<()> { html_trace!("WrappedBlock::add_text({}), {:?}", text, main_tag); // We walk character by character. // 1. First, build up whitespace columns in self.wslen @@ -885,9 +880,10 @@ impl BorderHoriz { } /// Turn into a string with drawing characters - fn into_string(self) -> String { + #[allow(clippy::inherent_to_string)] + fn to_string(&self) -> String { self.segments - .into_iter() + .iter() .map(|seg| match seg { BorderSegHoriz::Straight => '─', BorderSegHoriz::StraightVert => '/', @@ -897,12 +893,6 @@ impl BorderHoriz { }) .collect::() } - - /// Return a string without destroying self - #[allow(clippy::inherent_to_string)] - fn to_string(&self) -> String { - self.clone().into_string() - } } /// A line, which can either be text or a line. @@ -916,10 +906,11 @@ pub(crate) enum RenderLine { impl RenderLine { /// Turn the rendered line into a String - fn into_string(self) -> String { + #[allow(clippy::inherent_to_string)] + fn to_string(&self) -> String { match self { - RenderLine::Text(tagged) => tagged.into_string(), - RenderLine::Line(border) => border.into_string(), + RenderLine::Text(tagged) => tagged.to_string(), + RenderLine::Line(border) => border.to_string(), } } @@ -934,7 +925,7 @@ impl RenderLine { let mut tagged = TaggedLine::new(); let tag = border.tag.clone(); tagged.push(Str(TaggedString { - s: border.into_string(), + s: border.to_string(), tag, })); tagged @@ -942,15 +933,6 @@ impl RenderLine { } } - #[cfg(feature = "html_trace")] - /// For testing, return a simple string of the contents. - fn to_string(&self) -> String { - match self { - RenderLine::Text(tagged) => tagged.to_string(), - RenderLine::Line(border) => border.to_string(), - } - } - /// Return whether this line has any text content /// Borders do not count as text. fn has_content(&self) -> bool { @@ -1080,7 +1062,7 @@ impl SubRenderer { } /// Flushes the current wrapped block into the lines. - fn flush_wrapping(&mut self) -> Result<(), Error> { + fn flush_wrapping(&mut self) -> Result<()> { if let Some(w) = self.wrapping.take() { self.lines .extend(w.into_lines()?.into_iter().map(RenderLine::Text)) @@ -1090,26 +1072,26 @@ impl SubRenderer { /// Flush the wrapping text and border. Only one should have /// anything to do. - fn flush_all(&mut self) -> Result<(), Error> { + fn flush_all(&mut self) -> Result<()> { self.flush_wrapping()?; Ok(()) } /// Consumes this renderer and return a multiline `String` with the result. - pub fn into_string(self) -> Result { + pub fn into_string(mut self) -> Result { let mut result = String::new(); - #[cfg(feature = "html_trace")] - let width: usize = self.width; - for line in self.into_lines()? { - result.push_str(&line.into_string()); + self.flush_wrapping()?; + for line in &self.lines { + result.push_str(&line.to_string()); result.push('\n'); } - html_trace!("into_string({}, {:?})", width, result); + html_trace!("into_string({}, {:?})", self.width, result); Ok(result) } #[cfg(feature = "html_trace")] /// Returns a string of the current builder contents (for testing). + #[allow(clippy::inherent_to_string)] fn to_string(&self) -> String { let mut result = String::new(); for line in &self.lines { @@ -1167,21 +1149,21 @@ impl SubRenderer { } /// Returns a `Vec` of `TaggedLine`s with the rendered text. - pub fn into_lines(mut self) -> Result>>, Error> { + pub fn into_lines(mut self) -> Result>>> { self.flush_wrapping()?; Ok(self.lines) } - fn add_horizontal_line(&mut self, line: BorderHoriz>) -> Result<(), Error> { + fn add_horizontal_line(&mut self, line: BorderHoriz>) -> Result<()> { self.flush_wrapping()?; self.lines.push_back(RenderLine::Line(line)); Ok(()) } - pub fn width_minus(&self, prefix_len: usize, min_width: usize) -> crate::Result { + pub fn width_minus(&self, prefix_len: usize, min_width: usize) -> Result { let new_width = self.width.saturating_sub(prefix_len); if new_width < min_width && !self.options.allow_width_overflow { - return Err(Error::TooNarrow); + return Err(TooNarrow); } Ok(new_width.max(min_width)) } @@ -1205,7 +1187,7 @@ fn filter_text_strikeout(s: &str) -> Option { } impl Renderer for SubRenderer { - fn add_empty_line(&mut self) -> crate::Result<()> { + fn add_empty_line(&mut self) -> Result<()> { html_trace!("add_empty_line()"); self.flush_all()?; self.lines.push_back(RenderLine::Text(TaggedLine::new())); @@ -1215,7 +1197,7 @@ impl Renderer for SubRenderer { Ok(()) } - fn new_sub_renderer(&self, width: usize) -> crate::Result { + fn new_sub_renderer(&self, width: usize) -> Result { let mut result = SubRenderer::new( width, self.options.clone(), @@ -1226,7 +1208,7 @@ impl Renderer for SubRenderer { Ok(result) } - fn start_block(&mut self) -> crate::Result<()> { + fn start_block(&mut self) -> Result<()> { html_trace!("start_block({})", self.width); self.flush_all()?; if self.lines.iter().any(|l| l.has_content()) { @@ -1237,11 +1219,11 @@ impl Renderer for SubRenderer { Ok(()) } - fn new_line(&mut self) -> crate::Result<()> { + fn new_line(&mut self) -> Result<()> { self.flush_all() } - fn new_line_hard(&mut self) -> Result<(), Error> { + fn new_line_hard(&mut self) -> Result<()> { match &self.wrapping { None => self.add_empty_line(), Some(wrapping) => { @@ -1254,7 +1236,7 @@ impl Renderer for SubRenderer { } } - fn add_horizontal_border(&mut self) -> Result<(), Error> { + fn add_horizontal_border(&mut self) -> Result<()> { self.flush_wrapping()?; self.lines.push_back(RenderLine::Line(BorderHoriz::new( self.width, @@ -1263,7 +1245,7 @@ impl Renderer for SubRenderer { Ok(()) } - fn add_horizontal_border_width(&mut self, width: usize) -> Result<(), Error> { + fn add_horizontal_border_width(&mut self, width: usize) -> Result<()> { self.flush_wrapping()?; self.lines.push_back(RenderLine::Line(BorderHoriz::new( width, @@ -1293,7 +1275,7 @@ impl Renderer for SubRenderer { self.at_block_end = true; } - fn add_inline_text(&mut self, text: &str) -> crate::Result<()> { + fn add_inline_text(&mut self, text: &str) -> Result<()> { html_trace!("add_inline_text({}, {})", self.width, text); if !self.ws_mode().preserve_whitespace() && self.at_block_end @@ -1340,7 +1322,7 @@ impl Renderer for SubRenderer { self.width } - fn append_subrender<'a, I>(&mut self, other: Self, prefixes: I) -> Result<(), Error> + fn append_subrender<'a, I>(&mut self, other: Self, prefixes: I) -> Result<()> where I: Iterator, { @@ -1370,7 +1352,7 @@ impl Renderer for SubRenderer { tag: tag.clone(), })); tline.push(Str(TaggedString { - s: l.into_string(), + s: l.to_string(), tag: tag.clone(), })); RenderLine::Text(tline) @@ -1380,7 +1362,7 @@ impl Renderer for SubRenderer { Ok(()) } - fn append_columns_with_borders(&mut self, cols: I, collapse: bool) -> Result<(), Error> + fn append_columns_with_borders(&mut self, cols: I, collapse: bool) -> Result<()> where I: IntoIterator, Self: Sized, @@ -1418,7 +1400,7 @@ impl Renderer for SubRenderer { .collect(), )) }) - .collect::>)>, Error>>()?; + .collect::>)>>>()?; tot_width += line_sets.len().saturating_sub(1); @@ -1524,7 +1506,7 @@ impl Renderer for SubRenderer { Ok(()) } - fn append_vert_row(&mut self, cols: I) -> Result<(), Error> + fn append_vert_row(&mut self, cols: I) -> Result<()> where I: IntoIterator, Self: Sized, @@ -1565,47 +1547,47 @@ impl Renderer for SubRenderer { } } - fn start_link(&mut self, target: &str) -> crate::Result<()> { + fn start_link(&mut self, target: &str) -> Result<()> { let (s, annotation) = self.decorator.decorate_link_start(target); self.ann_stack.push(annotation); self.add_inline_text(&s) } - fn end_link(&mut self) -> crate::Result<()> { + fn end_link(&mut self) -> Result<()> { let s = self.decorator.decorate_link_end(); self.add_inline_text(&s)?; self.ann_stack.pop(); Ok(()) } - fn start_emphasis(&mut self) -> crate::Result<()> { + fn start_emphasis(&mut self) -> Result<()> { let (s, annotation) = self.decorator.decorate_em_start(); self.ann_stack.push(annotation); self.add_inline_text(&s) } - fn end_emphasis(&mut self) -> crate::Result<()> { + fn end_emphasis(&mut self) -> Result<()> { let s = self.decorator.decorate_em_end(); self.add_inline_text(&s)?; self.ann_stack.pop(); Ok(()) } - fn start_strong(&mut self) -> crate::Result<()> { + fn start_strong(&mut self) -> Result<()> { let (s, annotation) = self.decorator.decorate_strong_start(); self.ann_stack.push(annotation); self.add_inline_text(&s) } - fn end_strong(&mut self) -> crate::Result<()> { + fn end_strong(&mut self) -> Result<()> { let s = self.decorator.decorate_strong_end(); self.add_inline_text(&s)?; self.ann_stack.pop(); Ok(()) } - fn start_strikeout(&mut self) -> crate::Result<()> { + fn start_strikeout(&mut self) -> Result<()> { let (s, annotation) = self.decorator.decorate_strikeout_start(); self.ann_stack.push(annotation); self.add_inline_text(&s)?; self.text_filter_stack.push(filter_text_strikeout); Ok(()) } - fn end_strikeout(&mut self) -> crate::Result<()> { + fn end_strikeout(&mut self) -> Result<()> { self.text_filter_stack .pop() .expect("end_strikeout() called without a corresponding start_strokeout()"); @@ -1614,19 +1596,19 @@ impl Renderer for SubRenderer { self.ann_stack.pop(); Ok(()) } - fn start_code(&mut self) -> crate::Result<()> { + fn start_code(&mut self) -> Result<()> { let (s, annotation) = self.decorator.decorate_code_start(); self.ann_stack.push(annotation); self.add_inline_text(&s)?; Ok(()) } - fn end_code(&mut self) -> crate::Result<()> { + fn end_code(&mut self) -> Result<()> { let s = self.decorator.decorate_code_end(); self.add_inline_text(&s)?; self.ann_stack.pop(); Ok(()) } - fn add_image(&mut self, src: &str, title: &str) -> crate::Result<()> { + fn add_image(&mut self, src: &str, title: &str) -> Result<()> { let (s, tag) = self.decorator.decorate_image(src, title); self.ann_stack.push(tag); self.add_inline_text(&s)?; @@ -1683,13 +1665,13 @@ impl Renderer for SubRenderer { } } - fn start_superscript(&mut self) -> crate::Result<()> { + fn start_superscript(&mut self) -> Result<()> { let (s, annotation) = self.decorator.decorate_superscript_start(); self.ann_stack.push(annotation); self.add_inline_text(&s)?; Ok(()) } - fn end_superscript(&mut self) -> crate::Result<()> { + fn end_superscript(&mut self) -> Result<()> { let s = self.decorator.decorate_superscript_end(); self.add_inline_text(&s)?; self.ann_stack.pop();