Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions docs/netsuke-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -1348,13 +1348,13 @@ entire CLI specification.
Rust

```rust
use clap::{Parser, Subcommand};

use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli { /// Path to the Netsuke manifest file to use.
struct Cli {
/// Path to the Netsuke manifest file to use.
#[arg(short, long, value_name = "FILE", default_value = "Netsukefile")]
file: PathBuf,

Expand All @@ -1371,26 +1371,38 @@ struct Cli { /// Path to the Netsuke manifest file to use.
verbose: bool,

#[command(subcommand)]
command: Option<Commands>, }
command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands { /// Build specified targets (or default targets if none are
given) [default]. Build { /// Write the generated Ninja manifest to this path
and retain it.
#[arg(long, value_name = "FILE")]
emit: Option<PathBuf>,
enum Commands {
/// Build specified targets (or default targets if none are given).
/// This is the default subcommand.
Build(BuildArgs),

/// A list of specific targets to build. targets: Vec<String>, },

/// Remove build artefacts and intermediate files. Clean {},
/// Remove build artefacts and intermediate files.
Clean,

/// Display the build dependency graph in DOT format for visualisation.
Graph {},
Graph,

/// Write the Ninja manifest to `FILE` without invoking Ninja. Manifest {
/// Output path for the generated Ninja file.
/// Write the Ninja manifest to `FILE` without invoking Ninja.
Manifest {
/// Output path for the generated Ninja file.
#[arg(value_name = "FILE")]
file: PathBuf, }, }
file: PathBuf,
},
}

#[derive(Args)]
struct BuildArgs {
/// Write the generated Ninja manifest to this path and retain it.
#[arg(long, value_name = "FILE")]
emit: Option<PathBuf>,

/// A list of specific targets to build.
targets: Vec<String>,
}
```

*Note: The* `Build` *command is wrapped in an* `Option<Commands>` *and will be
Expand Down
26 changes: 15 additions & 11 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! This module defines the [`Cli`] structure and its subcommands.
//! It mirrors the design described in `docs/netsuke-design.md`.

use clap::{Parser, Subcommand};
use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;

/// Maximum number of jobs accepted by the CLI.
Expand Down Expand Up @@ -71,27 +71,31 @@ impl Cli {
#[must_use]
fn with_default_command(mut self) -> Self {
if self.command.is_none() {
self.command = Some(Commands::Build {
self.command = Some(Commands::Build(BuildArgs {
emit: None,
targets: Vec::new(),
});
}));
}
self
}
}

/// Arguments accepted by the `build` command.
#[derive(Debug, Args, PartialEq, Eq, Clone)]
pub struct BuildArgs {
/// Write the generated Ninja manifest to this path and retain it.
#[arg(long, value_name = "FILE")]
pub emit: Option<PathBuf>,

/// A list of specific targets to build.
pub targets: Vec<String>,
}

/// Available top-level commands for Netsuke.
#[derive(Debug, Subcommand, PartialEq, Eq, Clone)]
pub enum Commands {
/// Build specified targets (or default targets if none are given) [default].
Build {
/// Write the generated Ninja manifest to this path and retain it.
#[arg(long, value_name = "FILE")]
emit: Option<PathBuf>,

/// A list of specific targets to build.
targets: Vec<String>,
},
Build(BuildArgs),

/// Remove build artifacts and intermediate files.
Clean,
Expand Down
14 changes: 7 additions & 7 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! handles command execution. It now delegates build requests to the Ninja
//! subprocess, streaming its output back to the user.

use crate::cli::{Cli, Commands};
use crate::cli::{BuildArgs, Cli, Commands};
use crate::{ir::BuildGraph, manifest, ninja_gen};
use anyhow::{Context, Result};
use serde_json;
Expand Down Expand Up @@ -69,15 +69,15 @@ impl BuildTargets {
///
/// Returns an error if manifest generation or the Ninja process fails.
pub fn run(cli: &Cli) -> Result<()> {
let command = cli.command.clone().unwrap_or(Commands::Build {
let command = cli.command.clone().unwrap_or(Commands::Build(BuildArgs {
emit: None,
targets: Vec::new(),
});
}));
match command {
Commands::Build { targets, emit } => {
Commands::Build(args) => {
let ninja = generate_ninja(cli)?;
let targets = BuildTargets::new(targets);
if let Some(path) = emit {
let targets = BuildTargets::new(args.targets);
if let Some(path) = args.emit {
write_and_log(&path, &ninja)?;
run_ninja(Path::new("ninja"), cli, &path, &targets)?;
} else {
Expand Down Expand Up @@ -140,7 +140,7 @@ fn write_and_log(path: &Path, content: &NinjaContent) -> Result<()> {
/// directory: None,
/// jobs: None,
/// verbose: false,
/// command: Some(Commands::Build { emit: None, targets: vec![] }),
/// command: Some(Commands::Build(BuildArgs { emit: None, targets: vec![] })),
/// };
/// let ninja = generate_ninja(&cli).expect("generate");
/// assert!(ninja.as_str().contains("rule"));
Expand Down
10 changes: 5 additions & 5 deletions tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@
//! using `rstest` for parameterised coverage of success and error scenarios.
use clap::Parser;
use clap::error::ErrorKind;
use netsuke::cli::{Cli, Commands};
use netsuke::cli::{BuildArgs, Cli, Commands};
use rstest::rstest;
use std::path::PathBuf;

#[rstest]
#[case(vec!["netsuke"], PathBuf::from("Netsukefile"), None, None, false, Commands::Build { emit: None, targets: Vec::new() })]
#[case(vec!["netsuke"], PathBuf::from("Netsukefile"), None, None, false, Commands::Build(BuildArgs { emit: None, targets: Vec::new() }))]
#[case(
vec!["netsuke", "--file", "alt.yml", "-C", "work", "-j", "4", "build", "a", "b"],
PathBuf::from("alt.yml"),
Some(PathBuf::from("work")),
Some(4),
false,
Commands::Build { emit: None, targets: vec!["a".into(), "b".into()] },
Commands::Build(BuildArgs { emit: None, targets: vec!["a".into(), "b".into()] }),
)]
#[case(vec!["netsuke", "--verbose"], PathBuf::from("Netsukefile"), None, None, true, Commands::Build { emit: None, targets: Vec::new() })]
#[case(vec!["netsuke", "--verbose"], PathBuf::from("Netsukefile"), None, None, true, Commands::Build(BuildArgs { emit: None, targets: Vec::new() }))]
#[case(
vec!["netsuke", "build", "--emit", "out.ninja", "a"],
PathBuf::from("Netsukefile"),
None,
None,
false,
Commands::Build { emit: Some(PathBuf::from("out.ninja")), targets: vec!["a".into()] },
Commands::Build(BuildArgs { emit: Some(PathBuf::from("out.ninja")), targets: vec!["a".into()] }),
)]
#[case(
vec!["netsuke", "manifest", "out.ninja"],
Expand Down
18 changes: 9 additions & 9 deletions tests/runner_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use netsuke::cli::{Cli, Commands};
use netsuke::cli::{BuildArgs, Cli, Commands};
use netsuke::runner::{BuildTargets, run, run_ninja};
use rstest::rstest;
use serial_test::serial;
Expand All @@ -16,10 +16,10 @@ fn run_exits_with_manifest_error_on_invalid_version() {
directory: None,
jobs: None,
verbose: false,
command: Some(Commands::Build {
command: Some(Commands::Build(BuildArgs {
emit: None,
targets: vec![],
}),
})),
};

let result = run(&cli);
Expand All @@ -35,10 +35,10 @@ fn run_ninja_not_found() {
directory: None,
jobs: None,
verbose: false,
command: Some(Commands::Build {
command: Some(Commands::Build(BuildArgs {
emit: None,
targets: vec![],
}),
})),
};
let targets = BuildTargets::new(vec![]);
let err = run_ninja(
Expand Down Expand Up @@ -71,10 +71,10 @@ fn run_executes_ninja_without_persisting_file() {
directory: Some(temp.path().to_path_buf()),
jobs: None,
verbose: false,
command: Some(Commands::Build {
command: Some(Commands::Build(BuildArgs {
emit: None,
targets: vec![],
}),
})),
};

let result = run(&cli);
Expand Down Expand Up @@ -111,10 +111,10 @@ fn run_build_with_emit_keeps_file() {
directory: Some(temp.path().to_path_buf()),
jobs: None,
verbose: false,
command: Some(Commands::Build {
command: Some(Commands::Build(BuildArgs {
emit: Some(emit_path.clone()),
targets: vec![],
}),
})),
};

let result = run(&cli);
Expand Down
8 changes: 4 additions & 4 deletions tests/steps/cli_steps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use crate::CliWorld;
use clap::Parser;
use cucumber::{given, then, when};
use netsuke::cli::{Cli, Commands};
use netsuke::cli::{BuildArgs, Cli, Commands};
use std::path::PathBuf;

fn apply_cli(world: &mut CliWorld, args: &str) {
Expand All @@ -16,10 +16,10 @@ fn apply_cli(world: &mut CliWorld, args: &str) {
match Cli::try_parse_from(tokens) {
Ok(mut cli) => {
if cli.command.is_none() {
cli.command = Some(Commands::Build {
cli.command = Some(Commands::Build(BuildArgs {
emit: None,
targets: Vec::new(),
});
}));
}
world.cli = Some(cli);
world.cli_error = None;
Expand All @@ -34,7 +34,7 @@ fn apply_cli(world: &mut CliWorld, args: &str) {
fn extract_build(world: &CliWorld) -> Option<(&Vec<String>, &Option<PathBuf>)> {
let cli = world.cli.as_ref()?;
match cli.command.as_ref()? {
Commands::Build { targets, emit } => Some((targets, emit)),
Commands::Build(args) => Some((&args.targets, &args.emit)),
_ => None,
}
}
Expand Down
Loading