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 #[arg(short = 'f', long, default_value = "html")]
22 format: Format,
23 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}