use std::fmt::{Display, Formatter};
use std::{fs, io};
const INPUT: &str = "docs.md";
const OUTPUT: &str = "README.md";
const COPYRIGHT: &str = "\
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/. --> \
";
const NOTE: &str = "\
<!-- This `README.md` file is automatically generated from `docs.md`, which uses `rustdoc`'s syntax
- to provide documentation for the `#[cfg_attrs { ... }]` macro too.
-
- See `build.rs` if you're interested to see the code, or edit `docs.md` to edit the
- documentation. --> \
";
const HEADER: &str = "# `#[cfg_attrs { ... }]`";
fn main() -> io::Result<()> {
println!("cargo:rerun-if-changed={INPUT}");
println!("cargo:rerun-if-changed={OUTPUT}");
if let Ok(input) = fs::read_to_string(INPUT) {
let copyright_lines = COPYRIGHT.lines().count() + 1;
let docs: Doc<'_> = input.lines().skip(copyright_lines).collect();
let _ = fs::write(OUTPUT, docs.to_string());
}
Ok(())
}
struct Doc<'lines> {
nodes: Vec<Node<'lines>>,
}
impl<'lines> FromIterator<&'lines str> for Doc<'lines> {
fn from_iter<T: IntoIterator<Item = &'lines str>>(lines: T) -> Self {
let mut code_block = None;
let mut nodes = Vec::new();
const INDENTATION: usize = 3;
let code_indentation = |line: &str| {
let mut indentation = 0;
for r#char in line.chars().take(INDENTATION) {
if r#char == ' ' {
indentation += 1;
} else {
break;
}
}
if indentation == INDENTATION {
if let Some(r#char) = line.chars().nth(INDENTATION) {
if r#char.is_whitespace() {
return None;
}
}
}
Some(indentation)
};
for line in lines {
if let Some(CodeBlock {
backticks,
indentation,
lines,
..
}) = &mut code_block
{
if code_indentation(line).map_or(false, |indent| indent < *indentation) && !line.is_empty() {
nodes.push(Node::CodeBlock(code_block.take().unwrap()));
} else {
if line.len() > *indentation && line[*indentation..].trim_end() == *backticks {
nodes.push(Node::CodeBlock(code_block.take().unwrap()));
} else {
lines.push(line.get(*indentation..).unwrap_or(""));
}
continue;
}
}
if let Some(indentation) = code_indentation(line) {
let mut backticks = 0;
for r#char in line.chars().skip(indentation) {
if r#char == '`' {
backticks += 1;
} else {
break;
}
}
if backticks >= 3 {
code_block = Some(CodeBlock {
backticks: &line[indentation..(indentation + backticks)],
indentation,
info: {
let info = line.get((indentation + backticks)..).map(|info| info.trim_end());
info.filter(|info| !info.is_empty())
},
lines: Vec::new(),
});
continue;
}
}
nodes.push(Node::Line(process_heading(line)));
}
Doc { nodes }
}
}
enum Node<'lines> {
Line(String),
CodeBlock(CodeBlock<'lines>),
}
struct CodeBlock<'lines> {
backticks: &'lines str,
indentation: usize,
info: Option<&'lines str>,
lines: Vec<&'lines str>,
}
fn process_hiding(line: &str, indentation: usize) -> Option<String> {
if line.len() > indentation {
let trim = line[indentation..].trim_end();
if trim == "#" || trim.starts_with("# ") {
return None;
} else if trim == "##" || trim.starts_with("## ") {
return Some(format!("{}{}", &line[..indentation], &line[(indentation + 1)..]));
}
}
Some(line.to_owned())
}
fn process_heading(line: &str) -> String {
const LEVELS: usize = 6;
let mut levels = 0;
for r#char in line.chars().take(LEVELS) {
if r#char == '#' {
levels += 1;
} else {
break;
}
}
if levels >= 1 && line.chars().nth(levels) == Some(' ') {
format!("#{}", line)
} else {
line.to_owned()
}
}
impl<'lines> Display for CodeBlock<'lines> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let ws: String = " ".repeat(self.indentation);
let backticks = self.backticks;
writeln!(f, "{ws}{backticks}{}", self.info.unwrap_or("rust"))?;
for line in &self.lines {
if let Some(line) = process_hiding(line, self.indentation) {
writeln!(f, "{}", line)?;
}
}
write!(f, "{ws}{backticks}")?;
Ok(())
}
}
impl<'lines> Display for Node<'lines> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::CodeBlock(code_block) => write!(f, "{}", code_block),
Self::Line(line) => write!(f, "{}", line),
}
}
}
impl<'lines> Display for Doc<'lines> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", COPYRIGHT)?;
writeln!(f)?;
writeln!(f, "{}", NOTE)?;
writeln!(f)?;
writeln!(f, "{}", HEADER)?;
for node in &self.nodes {
writeln!(f, "{}", node)?;
}
Ok(())
}
}