1mod commands;
29
30use clap::{Parser, Subcommand};
31use tracing_subscriber::EnvFilter;
32
33use commands::{
34 add_model, bench, compare, import_pytorch, inspect, latency, new_project, record, replay, run,
35 test, validate_model, visualize,
36};
37
38const BRAND: &str = "clankeRS";
39
40#[derive(Parser)]
42#[command(name = "clankers", about = "clankeRS — Rust robotics SDK CLI", version)]
43pub struct Cli {
44 #[command(subcommand)]
45 command: Commands,
46}
47
48#[derive(Subcommand)]
50pub enum Commands {
51 New {
53 name: String,
54 #[arg(long, default_value = "basic-node")]
55 template: String,
56 },
57 Run,
59 Test,
61 Inspect { file: String },
63 Replay {
65 file: String,
66 #[arg(long)]
67 node: Option<String>,
68 },
69 Latency { file: String },
71 Compare { expected: String, actual: String },
73 Record {
75 #[arg(long, default_value = "logs/run.mcap")]
76 output: String,
77 },
78 Bench {
80 #[arg(long)]
81 model: String,
82 #[arg(long, default_value = "onnxruntime")]
83 backend: String,
84 #[arg(long)]
85 input: Option<String>,
86 #[arg(long, default_value = "50")]
87 warmup: u32,
88 #[arg(long, default_value = "1000")]
89 iters: u32,
90 },
91 ValidateModel {
93 #[arg(long)]
94 pytorch: Option<String>,
95 #[arg(long)]
96 checkpoint: Option<String>,
97 #[arg(long)]
98 onnx: String,
99 #[arg(long, default_value = "sample_data/policy_inputs/")]
100 samples: String,
101 #[arg(long, default_value = "0.001")]
102 tolerance: f32,
103 },
104 ImportPytorch {
106 #[arg(long)]
107 model: String,
108 #[arg(long)]
109 checkpoint: String,
110 #[arg(long)]
111 output: String,
112 #[arg(long)]
113 opset: Option<u32>,
114 },
115 AddModel { path: String },
117 Visualize { file: String },
119 Demo {
121 #[arg(default_value = "camera-perception")]
122 name: String,
123 },
124}
125
126pub async fn run() -> anyhow::Result<()> {
128 tracing_subscriber::fmt()
129 .with_env_filter(
130 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
131 )
132 .init();
133
134 let cli = Cli::parse();
135
136 match cli.command {
137 Commands::New { name, template } => new_project::execute(&name, &template)?,
138 Commands::Run => run::execute()?,
139 Commands::Test => test::execute().await?,
140 Commands::Inspect { file } => inspect::execute(&file)?,
141 Commands::Replay { file, node } => replay::execute(&file, node.as_deref()).await?,
142 Commands::Latency { file } => latency::execute(&file).await?,
143 Commands::Compare { expected, actual } => compare::execute(&expected, &actual)?,
144 Commands::Bench {
145 model,
146 backend,
147 input,
148 warmup,
149 iters,
150 } => bench::execute(&model, &backend, input.as_deref(), warmup, iters)?,
151 Commands::Record { output } => record::execute(&output).await?,
152 Commands::ValidateModel {
153 pytorch,
154 checkpoint,
155 onnx,
156 samples,
157 tolerance,
158 } => validate_model::execute(
159 pytorch.as_deref(),
160 checkpoint.as_deref(),
161 &onnx,
162 &samples,
163 tolerance,
164 )?,
165 Commands::ImportPytorch {
166 model,
167 checkpoint,
168 output,
169 opset,
170 } => import_pytorch::execute(&model, &checkpoint, &output, opset)?,
171 Commands::AddModel { path } => add_model::execute(&path)?,
172 Commands::Visualize { file } => visualize::execute(&file)?,
173 Commands::Demo { name } => commands::demo::execute(&name).await?,
174 }
175
176 Ok(())
177}
178
179pub fn print_banner(msg: &str) {
181 println!("{BRAND} {msg}");
182}