0918nobita / cli-compose

Composable, strict CLI framework with static analysis for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

cli-compose

Composable, strict CLI framework with static analysis for Rust

Test Clippy Rustfmt codecov

Please note that this is still at an early stage of development. Hence this may contain bugs, unimplemented features, or unstable features.

Implementation goals

Directory structure

project
├ opts
│ ├ src
│ │ └ lib.rs
│ └ Cargo.toml
├ src
│ └ main.rs
├ build.rs
└ Cargo.toml

Cargo.toml

[workspace]
members = [ "opts" ]

[package]
name = "example-cli"
edition = "2021"

[dependencies]
opts = { path = "./opts" }

[dependencies.cli-compose]
git = "https://github.com/0918nobita/cli-compose"
package = "cli-compose"
features = [ "runtime" ]

[build-dependencies.cli-compose]
git = "https://github.com/0918nobita/cli-compose"
package = "cli-compose"
features = [ "codegen" ]

opts/Cargo.toml

[package]
name = "opts"
edition = "2021"

[dependencies.cli-compose]
git = "https://github.com/0918nobita/cli-compose"
package = "cli-compose"
features = [ "schema" ]

opts/src/lib.rs

use cli_compose::schema::{ArgOpt, FromKebabStr, SingleSelect, Opt, PosArg};

// All derivers treat doc comments as help messages.

/// Source file path
#[derive(Debug, PosArg)]
pub struct Input(String);

/// Reads source code from stdin
#[derive(Debug, Opt)]
#[opt(long = "stdin")] // overrides its long name
pub struct StdinOpt;

/// Settings related to input
#[derive(SingleSelect)]
pub enum InputGroup {
    Input(Input),
    StdinOpt(StdinOpt),
}

/// Source file format
#[derive(Debug, ArgOpt, FromKebabStr)]
#[arg_opt(use_default)]
pub enum InputFormat {
    Json,
    Yaml,
}

impl Default for InputFormat {
    fn default() -> Self {
        InputFormat::Json
    }
}

/// Output file path
#[derive(Debug, ArgOpt)]
#[arg_opt(short = 'o')] // configures its short name
pub struct Output(String);

/// Outputs to stdout
#[derive(Debug, Opt)]
#[opt(long = "stdout")]
pub struct StdoutOpt;

/// Settings related to output
#[derive(SingleSelect)]
pub enum OutputGroup {
    Output(Output),
    StdoutOpt(StdoutOpt),
}

#[derive(Opt)]
pub struct Verbose;

#[derive(Cli)]
#[cli(
    name = "example",
    version = from_crate,
    desc = from_crate
)]
pub struct ExampleCli;

build.rs

use cli_compose::codegen::define_cli;
use opts::{Cli, InputFormat, InputGroup, OutputGroup, Verbose};

fn main() {
    define_cli::<Cli>("opts")
        .unwrap()
        .member::<InputGroup>()
        .member::<InputFormat>()
        .member::<OutputGroup>()
        .member::<Verbose>()
        .build("ExampleCliResult")
        .unwrap();
    }
}

src/main.rs

use cli_compose::runtime::{use_cli, AsCli};

// includes `$OUT_DIR/cli_compose/example_cli.rs`
// and imports `ExampleCli` struct
use_cli! { example_cli::ExampleCli }

pub fn main() {
    let res = Cli::parse(std::env::args()); // res: ExampleCliResult

    println!("Input: {:?}", res.input);
    println!("InputFormat: {:?}", res.input_format);
    println!("Output: {:?}", res.output);
    println!("Verbose: {:?}", res.verbose);
}

Results

$ cargo run -- --verbose --stdin --stdout

Input: Stdin(StdinOpt)
InputFormat: Json
Output: Stdout(StdoutOpt)
Verbose: Some(Verbose)

$ cargo run -- --input-format yaml -o output.txt input.yaml

Input: File("input.txt")
InputFormat: Yaml
Output: File("output.txt")
Verbose: None

Example program to test features already implemented

cargo run -p example

About

Composable, strict CLI framework with static analysis for Rust

License:MIT License


Languages

Language:Rust 96.4%Language:Dhall 3.6%