1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![allow(unused)]
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
mod bench;
mod discover;
mod expand;
mod fmt;
mod lint;
mod plugin_new;
mod run;
mod watch;
#[derive(Parser)]
#[command(name = "cargo-tupa")]
#[command(about = "Tupã Rust-DSL pipeline tooling", long_about = None)]
struct Cli {
/// Path to Cargo.toml (default: current directory)
#[arg(short, long, value_name = "manifest", global = true)]
manifest_path: Option<PathBuf>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Build and typecheck the pipeline (no execution)
Check {
/// Enable verbose output
#[arg(short, long)]
verbose: bool,
},
/// Execute the pipeline with JSON input
Run {
/// JSON input file (or read stdin)
#[arg(short, long)]
input: Option<PathBuf>,
/// Enable parallel execution
#[arg(long)]
parallel: bool,
/// Write step metrics to JSON file
#[arg(long)]
metrics_output: Option<PathBuf>,
},
/// Format Rust-DSL pipeline code
Fmt {
/// Format specific file (default: all src/**/*.rs)
#[arg(short, long)]
file: Option<PathBuf>,
},
/// Lint Rust-DSL pipeline for issues
Lint {
/// Lint specific file (default: all pipeline files)
#[arg(short, long)]
file: Option<PathBuf>,
/// Output results as JSON
#[arg(long)]
json: bool,
},
/// Generate a new plugin scaffold
PluginNew {
/// Output filename (default: my_plugin.rs)
#[arg(value_name = "FILENAME")]
filename: Option<String>,
},
/// Expand pipeline! macro to generated Rust code
Expand {
/// Enable pretty-print (indentation)
#[arg(long)]
pretty: bool,
/// Specific file to expand (default: all src/**/*.rs)
#[arg(short, long)]
file: Option<PathBuf>,
},
/// Discover the binary target name from Cargo.toml ([[bin]] or src/main.rs)
Discover,
/// Profile pipeline execution: measure per-step timing over multiple runs
Bench {
/// Number of runs (default: 10)
#[arg(short, long, default_value_t = 10)]
runs: usize,
/// Write benchmark report as JSON
#[arg(long)]
json_output: Option<PathBuf>,
},
/// Watch for source changes and re-run pipeline automatically
Watch {
/// JSON input file (or read stdin)
#[arg(short, long)]
input: Option<PathBuf>,
/// Enable parallel execution
#[arg(long)]
parallel: bool,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Check { verbose: _ } => {
println!("✅ Pipeline typecheck OK (Rust compiler)");
Ok(())
}
Commands::Run {
input,
parallel,
metrics_output,
} => run::run(&cli.manifest_path, input, parallel, metrics_output),
Commands::Fmt { file } => fmt::format_pipeline(file),
Commands::Lint { file, json } => lint::lint(file, json),
Commands::PluginNew { filename } => plugin_new::run(filename),
Commands::Expand { pretty, file } => expand::expand_pipeline_block(file, pretty),
Commands::Discover => discover::discover_binary_target(
&cli.manifest_path
.unwrap_or_else(|| PathBuf::from("Cargo.toml")),
)
.map(|name| {
println!("{}", name);
Ok(())
})?,
Commands::Bench { runs, json_output } => bench::bench(cli.manifest_path, runs, json_output),
Commands::Watch { input, parallel } => watch::watch(input, parallel),
}
}