rst/
main.rs

1#![warn(clippy::pedantic)]
2
3use clap::Parser;
4
5use rst_parser::parse;
6use rst_renderer::{render_html, render_json, render_xml};
7
8use std::io::{self, Read};
9
10#[derive(Debug, Clone, clap::ValueEnum)]
11#[allow(non_camel_case_types)]
12enum Format {
13    json,
14    xml,
15    html,
16}
17
18#[derive(Debug, Parser)]
19struct Cli {
20    /// Output format
21    #[arg(short = 'f', long, default_value = "html")]
22    format: Format,
23    /// Input file
24    file: Option<String>,
25    #[command(flatten)]
26    verbosity: clap_verbosity_flag::Verbosity,
27}
28
29fn main() -> Result<(), anyhow::Error> {
30    let args = Cli::parse();
31
32    let level_filter = args.verbosity.log_level().unwrap().to_level_filter();
33    env_logger::Builder::new()
34        .filter(Some("rst"), level_filter)
35        .filter(None, log::Level::Warn.to_level_filter())
36        .try_init()?;
37
38    let content = preprocess_content(args.file.as_deref())?;
39    let document = parse(&content)?;
40    let stdout = std::io::stdout();
41    match args.format {
42        Format::json => render_json(&document, stdout)?,
43        Format::xml => render_xml(&document, stdout)?,
44        Format::html => render_html(&document, stdout, true)?,
45    }
46    Ok(())
47}
48
49fn preprocess_content(file: Option<&str>) -> Result<String, clap::Error> {
50    let mut content = if let Some(file) = file {
51        std::fs::read_to_string(file)?
52    } else {
53        let mut stdin = String::new();
54        io::stdin().read_to_string(&mut stdin)?;
55        stdin
56    };
57    content = content.replace('\t', " ".repeat(8).as_ref());
58    if !content.ends_with('\n') {
59        content.push('\n');
60    }
61    Ok(content)
62}