use std::io::IsTerminal;
use std::path::PathBuf;
use anyhow::Result;
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Parser)]
#[command(
name = "docgen",
version,
about = "Static documentation-site generator"
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Build {
#[arg(default_value = ".")]
root: PathBuf,
},
Init {
#[arg(default_value = ".")]
dir: PathBuf,
#[arg(long, default_value_t = false)]
force: bool,
},
Dev {
#[arg(default_value = ".")]
root: PathBuf,
#[arg(long, default_value_t = 4321)]
port: u16,
#[arg(long, default_value_t = false)]
open: bool,
},
Lint {
#[arg(default_value = ".")]
root: PathBuf,
#[arg(long, value_enum, default_value_t = LintFormat::Pretty)]
format: LintFormat,
#[arg(long, default_value_t = false)]
deny_warnings: bool,
#[arg(long, value_delimiter = ',')]
rules: Vec<String>,
#[arg(long, default_value_t = false)]
quiet: bool,
#[arg(long, default_value_t = false)]
list_rules: bool,
},
Plantuml {
#[arg(long, default_value_t = 8080)]
port: u16,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Build { root } => {
let outcome = docgen_build::build(&root)?;
println!(
"Built {} page(s) -> {}",
outcome.page_count,
outcome.out_dir.display()
);
Ok(())
}
Command::Init { dir, force } => {
docgen_init::scaffold(&docgen_init::InitOptions {
target: dir.clone(),
force,
})?;
println!("Scaffolded a new docgen site at {}", dir.display());
println!("Next: cd {} && docgen dev", dir.display());
Ok(())
}
Command::Dev { root, port, open } => docgen_server::serve(docgen_server::DevOptions {
project_root: root,
port,
open,
}),
Command::Lint {
root,
format,
deny_warnings,
rules,
quiet,
list_rules,
} => run_lint(&root, format, deny_warnings, rules, quiet, list_rules),
Command::Plantuml { port } => docgen_plantuml::run_container(port),
}
}
#[derive(Clone, Copy, PartialEq, Eq, ValueEnum)]
enum LintFormat {
Pretty,
Json,
Github,
Gitlab,
}
fn run_lint(
root: &std::path::Path,
format: LintFormat,
deny_warnings: bool,
rules: Vec<String>,
quiet: bool,
list_rules: bool,
) -> Result<()> {
if list_rules {
for (id, severity, description) in docgen_lint::list_rules() {
println!("{id:<22} {:<6} {description}", severity.to_string());
}
return Ok(());
}
let options = docgen_lint::LintOptions {
only_rules: (!rules.is_empty()).then_some(rules),
deny_warnings,
};
let outcome = match docgen_lint::run(root, &options) {
Ok(outcome) => outcome,
Err(err) => {
eprintln!("error: {err}");
std::process::exit(2);
}
};
let rendered = match format {
LintFormat::Pretty => {
let use_color = std::io::stdout().is_terminal();
if quiet {
let errors_only = docgen_lint::LintOutcome {
diagnostics: outcome
.diagnostics
.iter()
.filter(|d| d.severity == docgen_lint::Severity::Error)
.cloned()
.collect(),
..outcome.clone()
};
docgen_lint::format::pretty(&errors_only, use_color)
} else {
docgen_lint::format::pretty(&outcome, use_color)
}
}
LintFormat::Json => docgen_lint::format::json(&outcome),
LintFormat::Github => docgen_lint::format::github(&outcome),
LintFormat::Gitlab => docgen_lint::format::gitlab(&outcome),
};
print!("{rendered}");
if !rendered.ends_with('\n') {
println!();
}
if outcome.errors > 0 {
std::process::exit(1);
}
Ok(())
}