diff --git a/Cargo.lock b/Cargo.lock index 7fc398e..7c9df60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -682,9 +682,13 @@ dependencies = [ name = "mabo-cli" version = "0.1.0" dependencies = [ + "anyhow", "clap", "glob", + "mabo-compiler", + "mabo-doc", "mabo-parser", + "mabo-project", "miette", "mimalloc", ] diff --git a/book/.vitepress/config.mts b/book/.vitepress/config.mts index b012843..7b0e0e7 100644 --- a/book/.vitepress/config.mts +++ b/book/.vitepress/config.mts @@ -85,7 +85,8 @@ export default defineConfig({ items: [ { text: "mabo lint", link: "/reference/cli/lint" }, { text: "mabo check", link: "/reference/cli/check" }, - { text: "mabo format", link: "/reference/cli/format" }, + { text: "mabo fmt", link: "/reference/cli/fmt" }, + { text: "mabo doc", link: "/reference/cli/doc" }, ], }, { diff --git a/book/src/reference/cli/check.md b/book/src/reference/cli/check.md index e69de29..2c909ee 100644 --- a/book/src/reference/cli/check.md +++ b/book/src/reference/cli/check.md @@ -0,0 +1 @@ +# mabo check diff --git a/book/src/reference/cli/doc.md b/book/src/reference/cli/doc.md new file mode 100644 index 0000000..56306b3 --- /dev/null +++ b/book/src/reference/cli/doc.md @@ -0,0 +1 @@ +# mabo doc diff --git a/book/src/reference/cli/fmt.md b/book/src/reference/cli/fmt.md new file mode 100644 index 0000000..8e57d52 --- /dev/null +++ b/book/src/reference/cli/fmt.md @@ -0,0 +1 @@ +# mabo fmt diff --git a/book/src/reference/cli/format.md b/book/src/reference/cli/format.md deleted file mode 100644 index e69de29..0000000 diff --git a/book/src/reference/cli/lint.md b/book/src/reference/cli/lint.md index e69de29..9951adf 100644 --- a/book/src/reference/cli/lint.md +++ b/book/src/reference/cli/lint.md @@ -0,0 +1 @@ +# mabo lint diff --git a/crates/mabo-benches/Cargo.toml b/crates/mabo-benches/Cargo.toml index 1213b0d..96b48e3 100644 --- a/crates/mabo-benches/Cargo.toml +++ b/crates/mabo-benches/Cargo.toml @@ -23,9 +23,9 @@ name = "varint" harness = false [dependencies] -mimalloc.workspace = true mabo-compiler = { path = "../mabo-compiler" } mabo-parser = { path = "../mabo-parser" } +mimalloc.workspace = true [dev-dependencies] divan = "0.1.8" diff --git a/crates/mabo-build/Cargo.toml b/crates/mabo-build/Cargo.toml index 59eee8a..59cb2b6 100644 --- a/crates/mabo-build/Cargo.toml +++ b/crates/mabo-build/Cargo.toml @@ -10,13 +10,13 @@ repository.workspace = true license.workspace = true [dependencies] +mabo-compiler = { path = "../mabo-compiler" } +mabo-parser = { path = "../mabo-parser" } +mabo-project = { path = "../mabo-project" } miette = { workspace = true, features = ["fancy-no-backtrace"] } prettyplease = "0.2.16" proc-macro2.workspace = true quote.workspace = true -mabo-compiler = { path = "../mabo-compiler" } -mabo-parser = { path = "../mabo-parser" } -mabo-project = { path = "../mabo-project" } syn.workspace = true thiserror.workspace = true diff --git a/crates/mabo-cli/Cargo.toml b/crates/mabo-cli/Cargo.toml index f1e6d62..b0be118 100644 --- a/crates/mabo-cli/Cargo.toml +++ b/crates/mabo-cli/Cargo.toml @@ -14,11 +14,15 @@ name = "mabo" path = "src/main.rs" [dependencies] +anyhow.workspace = true clap.workspace = true glob.workspace = true +mabo-compiler = { path = "../mabo-compiler" } +mabo-doc = { path = "../mabo-doc" } +mabo-parser = { path = "../mabo-parser" } +mabo-project = { path = "../mabo-project" } miette = { workspace = true, features = ["fancy-no-backtrace"] } mimalloc.workspace = true -mabo-parser = { path = "../mabo-parser" } [lints] workspace = true diff --git a/crates/mabo-cli/src/cli.rs b/crates/mabo-cli/src/cli.rs index e34a597..5ac7063 100644 --- a/crates/mabo-cli/src/cli.rs +++ b/crates/mabo-cli/src/cli.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use clap::{Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; #[derive(Parser)] #[command(about, author, version, propagate_version = true)] @@ -11,17 +11,45 @@ pub struct Cli { #[derive(Subcommand)] pub enum Command { - Init { - path: Option, - }, - Check { - #[arg(num_args(1..))] - files: Vec, - }, - Format { - #[arg(num_args(1..))] - files: Vec, - }, + #[command(alias = "initialize")] + Init(InitArgs), + Check(CheckArgs), + #[command(alias = "format")] + Fmt(FmtArgs), + #[command(alias = "document")] + Doc(DocArgs), +} + +/// Arguments for the [`Command::Init`] subcommand. +#[derive(Args)] +pub struct InitArgs { + #[arg(long)] + pub name: Option, + pub path: Option, +} + +/// Arguments for the [`Command::Check`] subcommand. +#[derive(Args)] +pub struct CheckArgs { + #[arg(long)] + pub project_dir: Option, + pub files: Vec, +} + +/// Arguments for the [`Command::Fmt`] subcommand. +#[derive(Args)] +pub struct FmtArgs { + #[arg(long)] + pub project_dir: Option, + pub files: Vec, +} + +/// Arguments for the [`Command::Doc`] subcommand. +#[derive(Args)] +pub struct DocArgs { + #[arg(long)] + pub project_dir: Option, + pub out_dir: PathBuf, } impl Cli { diff --git a/crates/mabo-cli/src/main.rs b/crates/mabo-cli/src/main.rs index 818a4e1..feefe06 100644 --- a/crates/mabo-cli/src/main.rs +++ b/crates/mabo-cli/src/main.rs @@ -1,11 +1,16 @@ //! Main command line interface for tooling support of Mabo schema files. -use std::{fs, process::ExitCode}; +use std::{ + fs, + path::{Path, PathBuf}, + process::ExitCode, +}; +use anyhow::Context; use mabo_parser::Schema; -use miette::{Context, IntoDiagnostic, Result}; +use miette::Context as _; -use self::cli::Cli; +use self::cli::{CheckArgs, Cli, DocArgs, FmtArgs}; mod cli; @@ -17,12 +22,13 @@ fn main() -> ExitCode { if let Some(cmd) = cli.cmd { let result = match cmd { - cli::Command::Init { path } => { - println!("TODO: create basic setup at {path:?}"); + cli::Command::Init(args) => { + println!("TODO: create basic setup at {:?}", args.path); Ok(()) } - cli::Command::Check { files } => check(files), - cli::Command::Format { files } => format(files), + cli::Command::Check(args) => check(args), + cli::Command::Fmt(args) => format(args), + cli::Command::Doc(args) => doc(args), }; return match result { @@ -37,51 +43,92 @@ fn main() -> ExitCode { ExitCode::SUCCESS } -fn check(patterns: Vec) -> Result<()> { - for pattern in patterns { - for entry in glob::glob(&pattern) - .into_diagnostic() - .wrap_err("Failed parsing glob pattern")? - { - let entry = entry.into_diagnostic().wrap_err("Failed reading entry")?; - let buf = fs::read_to_string(&entry) - .into_diagnostic() - .wrap_err_with(|| format!("Failed reading {entry:?}"))?; - - if let Err(e) = Schema::parse(&buf, Some(&entry)).wrap_err("Failed parsing schema file") - { - eprintln!("{e:?}"); - } +fn check(args: CheckArgs) -> anyhow::Result<()> { + for file in project_or_files(args.project_dir, args.files)? { + let buf = fs::read_to_string(&file).with_context(|| format!("failed reading {file:?}"))?; + + if let Err(e) = Schema::parse(&buf, Some(&file)).wrap_err("failed parsing schema file") { + eprintln!("{e:?}"); } } Ok(()) } -fn format(patterns: Vec) -> Result<()> { - for pattern in patterns { - for entry in glob::glob(&pattern) - .into_diagnostic() - .wrap_err("Failed parsing glob pattern")? - { - let entry = entry.into_diagnostic().wrap_err("Failed reading entry")?; - let buf = fs::read_to_string(&entry).into_diagnostic()?; - let schema = - match Schema::parse(&buf, Some(&entry)).wrap_err("Failed parsing schema file") { - Ok(schema) => schema, - Err(e) => { - eprintln!("{e:?}"); - continue; - } - }; - - let formatted = schema.to_string(); - - if buf != formatted { - fs::write(entry, &formatted).into_diagnostic()?; +fn format(args: FmtArgs) -> anyhow::Result<()> { + for file in project_or_files(args.project_dir, args.files)? { + let buf = fs::read_to_string(&file)?; + let schema = match Schema::parse(&buf, Some(&file)).wrap_err("Failed parsing schema file") { + Ok(schema) => schema, + Err(e) => { + eprintln!("{e:?}"); + continue; } + }; + + let formatted = schema.to_string(); + + if buf != formatted { + fs::write(file, &formatted)?; } } Ok(()) } + +fn doc(args: DocArgs) -> anyhow::Result<()> { + let project = mabo_project::load(project_dir(args.project_dir)?)?; + + for file in project.files { + let content = std::fs::read_to_string(&file)?; + let schema = mabo_parser::Schema::parse(&content, Some(&file))?; + let schema = mabo_compiler::simplify_schema(&schema); + let docs = mabo_doc::render_schema(&mabo_doc::Opts {}, &schema)?; + + write_doc_output(&docs, &args.out_dir.join(docs.name))?; + } + + Ok(()) +} + +fn write_doc_output(output: &mabo_doc::Output<'_>, parent: &Path) -> anyhow::Result<()> { + let path = parent.join(output.name); + let file = parent.join(&output.file); + + fs::create_dir_all(file.parent().unwrap())?; + fs::write(file, &output.content)?; + + for module in &output.modules { + write_doc_output(module, &path)?; + } + + Ok(()) +} + +fn project_or_files( + project: Option, + patterns: Vec, +) -> anyhow::Result> { + Ok(if patterns.is_empty() { + mabo_project::load(project_dir(project)?)?.files + } else { + let mut files = Vec::new(); + for pattern in patterns { + for entry in glob::glob(&pattern).context("failed parsing glob pattern")? { + let entry = entry.context("failed reading entry")?; + if !entry.extension().map_or(false, |ext| ext == "mabo") { + continue; + } + files.push(entry); + } + } + files + }) +} + +fn project_dir(arg: Option) -> anyhow::Result { + arg.map_or_else( + || std::env::current_dir().context("failed finding current directory"), + Ok, + ) +} diff --git a/crates/mabo-compiler/Cargo.toml b/crates/mabo-compiler/Cargo.toml index bfa1b0b..b0404c8 100644 --- a/crates/mabo-compiler/Cargo.toml +++ b/crates/mabo-compiler/Cargo.toml @@ -12,11 +12,11 @@ license.workspace = true [dependencies] anstream.workspace = true anstyle.workspace = true +mabo-parser = { path = "../mabo-parser" } miette.workspace = true schemars = { version = "0.8.16", optional = true } serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } -mabo-parser = { path = "../mabo-parser" } thiserror.workspace = true [dev-dependencies] diff --git a/crates/mabo-go/Cargo.toml b/crates/mabo-go/Cargo.toml index e32caa5..0fe0aed 100644 --- a/crates/mabo-go/Cargo.toml +++ b/crates/mabo-go/Cargo.toml @@ -10,14 +10,14 @@ repository.workspace = true license.workspace = true [dependencies] +anyhow.workspace = true clap.workspace = true heck = "0.4.1" -miette = { workspace = true, features = ["fancy-no-backtrace"] } mabo-compiler = { path = "../mabo-compiler" } mabo-parser = { path = "../mabo-parser" } mabo-project = { path = "../mabo-project" } +miette = { workspace = true, features = ["fancy-no-backtrace"] } mimalloc.workspace = true -anyhow.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/mabo-lsp/Cargo.toml b/crates/mabo-lsp/Cargo.toml index ff99fc4..9ade9e9 100644 --- a/crates/mabo-lsp/Cargo.toml +++ b/crates/mabo-lsp/Cargo.toml @@ -17,15 +17,15 @@ line-index = "0.1.1" log = { version = "0.4.20", features = ["kv_unstable_std", "std"] } lsp-server = "0.7.6" lsp-types = { version = "0.95.0", features = ["proposed"] } -ouroboros = "0.18.2" -parking_lot = "0.12.1" -ropey = "1.6.1" -serde.workspace = true -serde_json.workspace = true mabo-compiler = { path = "../mabo-compiler" } mabo-meta = { path = "../mabo-meta" } mabo-parser = { path = "../mabo-parser" } mabo-project = { path = "../mabo-project" } +ouroboros = "0.18.2" +parking_lot = "0.12.1" +ropey = "1.6.1" +serde_json.workspace = true +serde.workspace = true time = { version = "0.3.31", features = ["formatting", "local-offset", "macros"] } [lints] diff --git a/crates/mabo-parser/Cargo.toml b/crates/mabo-parser/Cargo.toml index 5616114..119e7c3 100644 --- a/crates/mabo-parser/Cargo.toml +++ b/crates/mabo-parser/Cargo.toml @@ -15,8 +15,8 @@ rustc-args = ["--cfg", "docsrs"] [dependencies] anstream.workspace = true anstyle.workspace = true -miette.workspace = true mabo-derive = { path = "../mabo-derive" } +miette.workspace = true winnow = "0.5.33" [dev-dependencies]