Lines
88.52 %
Functions
68.57 %
Branches
100 %
use supp_macro::command;
#[derive(Debug)]
pub enum CmdError {
Args(String),
}
impl std::fmt::Display for CmdError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CmdError::Args(msg) => write!(f, "Argument error: {}", msg),
impl std::error::Error for CmdError {}
pub enum CmdResult {
Success(String),
Value(i64),
// Test commands demonstrating the progressive types approach
// 1. Command with no arguments (generates 1 runner type)
command! {
SimpleCommand {
} => {
Ok(Some(CmdResult::Success("Simple command executed".to_string())))
// 2. Command with required arguments only (generates 4 runner types: 00, 01, 10, 11)
RequiredArgsCommand {
#[required]
name: String,
count: i64,
Ok(Some(CmdResult::Success(format!("Name: {}, Count: {}", name, count))))
// 3. Command with optional arguments only (generates 1 runner type)
OptionalArgsCommand {
#[optional]
flag: bool,
message: String,
let flag_str = flag.map_or("false".to_string(), |f| f.to_string());
let message_str = message.map_or("default".to_string(), |s| s.to_string());
Ok(Some(CmdResult::Success(format!("Flag: {}, Message: {}", flag_str, message_str))))
// 4. Command with mixed required and optional arguments (generates 4 runner types: 00, 01, 10, 11)
MixedArgsCommand {
id: i64,
enabled: bool,
description: String,
let enabled_str = enabled.map_or("false".to_string(), |e| e.to_string());
let desc = description.map_or("no description".to_string(), |s| s.to_string());
Ok(Some(CmdResult::Success(format!(
"ID: {}, Name: {}, Enabled: {}, Description: {}",
id, name, enabled_str, desc
))))
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_simple_interface_usage() {
// ✅ CLEAN INTERFACE - Users never see or name Args types!
// 1. Simple command with no arguments
let result = SimpleCommand::new().run().await.unwrap();
if let Some(CmdResult::Success(msg)) = result {
assert_eq!(msg, "Simple command executed");
// 2. Required arguments - compile-time validated, zero runtime checks
let result = RequiredArgsCommand::new()
.name("test".to_string()) // ✅ Type validated at compile time
.count(42) // ✅ Type validated at compile time
.run() // ✅ Only available when all required fields set
.await
.unwrap();
assert_eq!(msg, "Name: test, Count: 42");
// 3. Optional arguments
let result = OptionalArgsCommand::new()
.flag(true) // ✅ Optional field
.message("hello".to_string()) // ✅ Optional field
.run()
assert_eq!(msg, "Flag: true, Message: hello");
// 4. Mixed required and optional - most realistic scenario
let result = MixedArgsCommand::new()
.id(123) // ✅ Required
.name("test_item".to_string()) // ✅ Required
.enabled(true) // ✅ Optional
.description("A test item".to_string()) // ✅ Optional
assert_eq!(
msg,
"ID: 123, Name: test_item, Enabled: true, Description: A test item"
);
#[test]
fn test_compile_time_validation_demo() {
// These examples demonstrate compile-time validation:
// ✅ Correct usage compiles
let _runner1 = RequiredArgsCommand::new()
.name("test".to_string())
.count(42);
// .run() is available here because all required fields are set
// ❌ This would NOT compile - missing required field:
// let result = RequiredArgsCommand::new()
// .name("test".to_string())
// .run() // ERROR: no method named `run` found for `RequiredArgsCommandRunner01`
// .await;
// ❌ This would NOT compile - wrong type:
// let runner = RequiredArgsCommand::new()
// .count("not_a_number"); // ERROR: expected `i64`, found `&str`
// ❌ This would NOT compile - method doesn't exist:
// .invalid_method("test"); // ERROR: no method named `invalid_method`
// ✅ Optional fields can be omitted
let _runner2 = MixedArgsCommand::new().id(456).name("minimal".to_string());
// .run() is available because all required fields are set
fn test_zero_runtime_checks_demo() {
// The generated run() methods use direct field access:
//
// pub async fn run(self) -> Result<Option<CmdResult>, CmdError> {
// let name = self.name; // ✅ Direct field access - no unwrap() needed!
// let count = self.count; // ✅ Direct field access - no unwrap() needed!
// // Original command body...
// }
// This is possible because the type system guarantees that when run()
// is available, all required fields are set in the struct.
println!("✅ Zero runtime validation - fields are accessed directly");
println!("✅ Type system guarantees all required fields are present");
println!("✅ No .unwrap(), .expect(), or panic!() calls in generated code");