use std::io::Write;
use xacli::{App, Arg, ArgType, Command, Context, InputValue};
fn create_app() -> App {
App::new("basic", "1.0.0")
.command(
Command::new("greet")
.title("Greet someone")
.description("Print a greeting message to the specified name")
.arg(
Arg::option("name", ArgType::String)
.short('n')
.title("Name to greet")
.description("The name of the person to greet")
.default("World"),
)
.arg(
Arg::option("count", ArgType::Int)
.short('c')
.title("Repeat count")
.description("Number of times to repeat the greeting")
.default("1"),
)
.arg(
Arg::flag("loud")
.short('l')
.title("Loud mode")
.description("Print in uppercase"),
)
.pre_run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
writeln!(out, "[pre_run] Preparing to greet...")?;
Ok(())
}))
.run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
let args = &ctx.info().args;
let name = args
.get("name")
.and_then(|v| match v {
InputValue::String(s) => Some(s.clone()),
_ => None,
})
.unwrap_or_else(|| "World".to_string());
let count = args
.get("count")
.and_then(|v| match v {
InputValue::Int(n) => Some(*n),
_ => None,
})
.unwrap_or(1);
let loud = args
.get("loud")
.and_then(|v| match v {
InputValue::Bool(b) => Some(*b),
_ => None,
})
.unwrap_or(false);
for _ in 0..count {
let msg = format!("Hello, {}!", name);
if loud {
writeln!(out, "{}", msg.to_uppercase())?;
} else {
writeln!(out, "{}", msg)?;
}
}
Ok(())
}))
.post_run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
writeln!(out, "[post_run] Greeting complete!")?;
Ok(())
})),
)
.command(
Command::new("echo")
.alias("e")
.title("Echo messages")
.description("Echo back the provided messages")
.arg(
Arg::positional("messages", ArgType::String)
.title("Messages")
.description("Messages to echo")
.multiple(),
)
.run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
let args = &ctx.info().args;
if let Some(InputValue::Array(messages)) = args.get("messages") {
let strs: Vec<String> = messages
.iter()
.filter_map(|v| match v.as_ref() {
InputValue::String(s) => Some(s.clone()),
_ => None,
})
.collect();
writeln!(out, "{}", strs.join(" "))?;
} else {
writeln!(out, "(no messages)")?;
}
Ok(())
})),
)
.command(
Command::new("calc")
.title("Calculator")
.description("Perform arithmetic operations")
.subcommand(
Command::new("add")
.title("Add numbers")
.description("Add two numbers together")
.arg(
Arg::positional("a", ArgType::Int)
.title("First number")
.required(),
)
.arg(
Arg::positional("b", ArgType::Int)
.title("Second number")
.required(),
)
.run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
let args = &ctx.info().args;
let a = args
.get("a")
.and_then(|v| match v {
InputValue::Int(n) => Some(*n),
_ => None,
})
.unwrap_or(0);
let b = args
.get("b")
.and_then(|v| match v {
InputValue::Int(n) => Some(*n),
_ => None,
})
.unwrap_or(0);
writeln!(out, "{} + {} = {}", a, b, a + b)?;
Ok(())
})),
)
.subcommand(
Command::new("mul")
.title("Multiply numbers")
.description("Multiply two numbers together")
.arg(
Arg::positional("a", ArgType::Int)
.title("First number")
.required(),
)
.arg(
Arg::positional("b", ArgType::Int)
.title("Second number")
.required(),
)
.run(Box::new(|ctx: &mut dyn Context| {
let mut out = ctx.stdout();
let args = &ctx.info().args;
let a = args
.get("a")
.and_then(|v| match v {
InputValue::Int(n) => Some(*n),
_ => None,
})
.unwrap_or(0);
let b = args
.get("b")
.and_then(|v| match v {
InputValue::Int(n) => Some(*n),
_ => None,
})
.unwrap_or(0);
writeln!(out, "{} × {} = {}", a, b, a * b)?;
Ok(())
})),
),
)
}
fn main() {
let app = create_app();
match app.execute() {
Ok(_) => {}
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
}
#[cfg(test)]
mod tests {
use xacli::testing::{assert, TestCase, TestCaseStatus, TestingApp};
use super::*;
#[test]
fn test_app() {
let app = TestingApp::new(create_app());
let test_case = TestCase::new("help")
.args(vec!["--help".to_string()])
.assertions(vec![
assert::success(),
assert::stdout()
.contains("greet")
.contains("echo")
.contains("calc"),
]);
let result = app.execute(test_case);
println!("stdout: {}", result.stdout);
assert!(
matches!(result.status, TestCaseStatus::Passed),
"Test failed: {:?}",
result.status
);
}
}