diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..dfa49b1 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +on: + push: + branches: ['main'] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + MISE_EXPERIMENTAL: 1 + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: jdx/mise-action@v2 + - run: mise r ci diff --git a/.mise.toml b/.mise.toml index 322a934..99f0365 100644 --- a/.mise.toml +++ b/.mise.toml @@ -31,5 +31,11 @@ raw = true [tasks.test] run = 'cargo test --all --all-features' -[tasks.insta] -run = 'cargo test --all --all-features' +[tasks.lint] +run = 'cargo clippy --all --all-features -- -D warnings' + +[tasks.format-check] +run = 'cargo fmt --all -- --check' + +[tasks.ci] +depends = ['test', 'lint', 'format-check'] diff --git a/cli/src/cli/generate/markdown.rs b/cli/src/cli/generate/markdown.rs index 492c043..cf1a618 100644 --- a/cli/src/cli/generate/markdown.rs +++ b/cli/src/cli/generate/markdown.rs @@ -1,3 +1,4 @@ +use std::fmt::{Display, Formatter}; use std::fs; use std::fs::File; use std::io::Write; @@ -162,6 +163,24 @@ enum UsageMdDirective { Plain(String), } +impl Display for UsageMdDirective { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + UsageMdDirective::Load { file } => { + write!(f, "", file.display()) + } + UsageMdDirective::Title => write!(f, ""), + UsageMdDirective::UsageOverview => write!(f, ""), + UsageMdDirective::GlobalArgs => write!(f, ""), + UsageMdDirective::GlobalFlags => write!(f, ""), + UsageMdDirective::CommandIndex => write!(f, ""), + UsageMdDirective::Commands => write!(f, ""), + UsageMdDirective::EndToken => write!(f, ""), + UsageMdDirective::Plain(line) => write!(f, "{}", line), + } + } +} + struct UsageMdContext { plain: bool, spec: Option, @@ -191,13 +210,13 @@ impl UsageMdDirective { UsageMdDirective::Load { file } => { let file = context::prepend_load_root(file); ctx.spec = Some(fs::read_to_string(&file).into_diagnostic()?.parse()?); - ctx.push(format!("", file.display())); + ctx.push(self.to_string()); } UsageMdDirective::Title => { ensure!(ctx.spec.is_some(), "spec must be loaded before title"); ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push(format!("")); + ctx.push(self.to_string()); ctx.push(format!("# {name}", name = spec.name)); ctx.push(format!("")); } @@ -205,7 +224,7 @@ impl UsageMdDirective { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push("".to_string()); + ctx.push(self.to_string()); ctx.push("```".to_string()); ctx.push(format!("{bin} [flags] [args]", bin = spec.bin)); ctx.push("```".to_string()); @@ -215,7 +234,7 @@ impl UsageMdDirective { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push("".to_string()); + ctx.push(self.to_string()); let args = spec.cmd.args.iter().filter(|a| !a.hide).collect::>(); if !args.is_empty() { for arg in args { @@ -236,7 +255,7 @@ impl UsageMdDirective { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push("".to_string()); + ctx.push(self.to_string()); let flags = spec .cmd .flags @@ -261,14 +280,14 @@ impl UsageMdDirective { UsageMdDirective::CommandIndex => { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push(format!("")); + ctx.push(self.to_string()); print_commands_index(&ctx, &[&spec.cmd])?; ctx.push(format!("")); } UsageMdDirective::Commands => { ctx.plain = false; let spec = ctx.spec.as_ref().unwrap(); - ctx.push(format!("")); + ctx.push(self.to_string()); print_commands(&ctx, &[&spec.cmd])?; ctx.push(format!("")); } diff --git a/examples/MISE_README.md b/examples/MISE_README.md index 1a40531..5533edc 100644 --- a/examples/MISE_README.md +++ b/examples/MISE_README.md @@ -1,5 +1,4 @@ - # mise diff --git a/src/parse/spec.rs b/src/parse/spec.rs index 8e6be11..0c294c9 100644 --- a/src/parse/spec.rs +++ b/src/parse/spec.rs @@ -1,11 +1,13 @@ -use crate::context::get_load_root; -use crate::error::UsageErr; -use crate::parse::cmd::SchemaCmd; -use kdl::{KdlDocument, KdlEntry, KdlNode}; use std::fmt::{Display, Formatter}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::str::FromStr; -use xx::file; + +use kdl::{KdlDocument, KdlEntry, KdlNode}; + +use xx::{context, file}; + +use crate::error::UsageErr; +use crate::parse::cmd::SchemaCmd; #[derive(Debug, Default)] pub struct Spec { @@ -63,7 +65,7 @@ fn get_string_prop(node: &KdlNode, name: &str) -> Option { impl FromStr for Spec { type Err = miette::Error; - fn from_str(input: &str) -> miette::Result { + fn from_str(input: &str) -> Result { let kdl: KdlDocument = input .parse() .map_err(|err: kdl::KdlError| UsageErr::KdlError(err))?; @@ -81,15 +83,16 @@ impl FromStr for Spec { schema.cmd.subcommands.insert(node.name.to_string(), node); } "include" => { - let file = match get_string_prop(node, "file").map(PathBuf::from) { - Some(file) if file.is_relative() => get_load_root().join(file), - Some(file) => file.to_path_buf(), - None => Err(UsageErr::InvalidInput( - node.to_string(), - *node.span(), - input.to_string(), - ))?, - }; + let file = get_string_prop(node, "file") + .map(context::prepend_load_root) + .ok_or_else(|| { + UsageErr::InvalidInput( + node.to_string(), + *node.span(), + input.to_string(), + ) + })?; + ensure!(file.exists(), "File not found: {}", file.display()); info!("include: {}", file.display()); let (spec, _) = split_script(&file)?; schema.merge(spec.parse()?);