diff --git a/crates/rustdoc_to_markdown/src/markdown_writer.rs b/crates/rustdoc_to_markdown/src/markdown_writer.rs index ffc910a6213cd0fbb9ab09a07214fd57801dfe27..1aea134f1238a0819864a9aef8df4a7117b82602 100644 --- a/crates/rustdoc_to_markdown/src/markdown_writer.rs +++ b/crates/rustdoc_to_markdown/src/markdown_writer.rs @@ -30,6 +30,10 @@ enum StartTagOutcome { pub struct MarkdownWriter { current_element_stack: VecDeque, + /// The number of columns in the current ``. + current_table_columns: usize, + is_first_th: bool, + is_first_td: bool, /// The Markdown output. markdown: String, } @@ -38,6 +42,9 @@ impl MarkdownWriter { pub fn new() -> Self { Self { current_element_stack: VecDeque::new(), + current_table_columns: 0, + is_first_th: true, + is_first_td: true, markdown: String::new(), } } @@ -58,6 +65,11 @@ impl MarkdownWriter { self.push_str("\n"); } + /// Appends a blank line to the end of the Markdown output. + fn push_blank_line(&mut self) { + self.push_str("\n\n"); + } + pub fn run(mut self, root_node: &Handle) -> Result { self.visit_node(&root_node)?; Ok(Self::prettify_markdown(self.markdown)) @@ -166,6 +178,25 @@ impl MarkdownWriter { } "ul" | "ol" => self.push_newline(), "li" => self.push_str("- "), + "thead" => self.push_blank_line(), + "tr" => self.push_newline(), + "th" => { + self.current_table_columns += 1; + if self.is_first_th { + self.is_first_th = false; + } else { + self.push_str(" "); + } + self.push_str("| "); + } + "td" => { + if self.is_first_td { + self.is_first_td = false; + } else { + self.push_str(" "); + } + self.push_str("| "); + } "summary" => { if tag.attrs.borrow().iter().any(|attr| { attr.name.local.to_string() == "class" && attr.value.to_string() == "hideme" @@ -209,6 +240,24 @@ impl MarkdownWriter { "pre" => self.push_str("\n```\n"), "ul" | "ol" => self.push_newline(), "li" => self.push_newline(), + "thead" => { + self.push_newline(); + for ix in 0..self.current_table_columns { + if ix > 0 { + self.push_str(" "); + } + self.push_str("| ---"); + } + self.push_str(" |"); + self.is_first_th = true; + } + "tr" => { + self.push_str(" |"); + self.is_first_td = true; + } + "table" => { + self.current_table_columns = 0; + } "div" => { if tag.attrs.borrow().iter().any(|attr| { attr.name.local.to_string() == "class" && attr.value.to_string() == "item-name" diff --git a/crates/rustdoc_to_markdown/src/rustdoc_to_markdown.rs b/crates/rustdoc_to_markdown/src/rustdoc_to_markdown.rs index 7a2d52a4e6a4197af89b4a78881663679a7a8cc5..dd791545686f5fa7823a7ebbf2f95efe22b4f7eb 100644 --- a/crates/rustdoc_to_markdown/src/rustdoc_to_markdown.rs +++ b/crates/rustdoc_to_markdown/src/rustdoc_to_markdown.rs @@ -117,4 +117,57 @@ mod tests { expected ) } + + #[test] + fn test_table() { + let html = indoc! {r##" +

§Feature flags

+

axum uses a set of feature flags to reduce the amount of compiled and + optional dependencies.

+

The following optional features are available:

+
+ + + + + + + + + + + + + +
NameDescriptionDefault?
http1Enables hyper’s http1 featureYes
http2Enables hyper’s http2 featureNo
jsonEnables the Json type and some similar convenience functionalityYes
macrosEnables optional utility macrosNo
matched-pathEnables capturing of every request’s router path and the MatchedPath extractorYes
multipartEnables parsing multipart/form-data requests with MultipartNo
original-uriEnables capturing of every request’s original URI and the OriginalUri extractorYes
tokioEnables tokio as a dependency and axum::serve, SSE and extract::connect_info types.Yes
tower-logEnables tower’s log featureYes
tracingLog rejections from built-in extractorsYes
wsEnables WebSockets support via extract::wsNo
formEnables the Form extractorYes
queryEnables the Query extractorYes
+ "##}; + let expected = indoc! {r#" + ## Feature flags + + axum uses a set of feature flags to reduce the amount of compiled and + optional dependencies.The following optional features are available: + + | Name | Description | Default? | + | --- | --- | --- | + | `http1` | Enables hyper’s `http1` feature | Yes | + | `http2` | Enables hyper’s `http2` feature | No | + | `json` | Enables the `Json` type and some similar convenience functionality | Yes | + | `macros` | Enables optional utility macros | No | + | `matched-path` | Enables capturing of every request’s router path and the `MatchedPath` extractor | Yes | + | `multipart` | Enables parsing `multipart/form-data` requests with `Multipart` | No | + | `original-uri` | Enables capturing of every request’s original URI and the `OriginalUri` extractor | Yes | + | `tokio` | Enables `tokio` as a dependency and `axum::serve`, `SSE` and `extract::connect_info` types. | Yes | + | `tower-log` | Enables `tower`’s `log` feature | Yes | + | `tracing` | Log rejections from built-in extractors | Yes | + | `ws` | Enables WebSockets support via `extract::ws` | No | + | `form` | Enables the `Form` extractor | Yes | + | `query` | Enables the `Query` extractor | Yes | + "#} + .trim(); + + assert_eq!( + convert_rustdoc_to_markdown(html.as_bytes()).unwrap(), + expected + ) + } }