ITADN

Add serde support for Command and builder types

#6299Openmacisamuele 创建于 2026-03-08
C-enhancementS-triage
M
macisamuelecommented
### Please complete the following tasks - [x] I have searched the [discussions](https://github.com/clap-rs/clap/discussions) - [x] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues ### Clap Version 4.5.60 ### Describe your use case A concrete use case is building a **proxy binary** that fronts multiple specialized binaries behind a single CLI entry point, where each specific binary becomes a subcommand of the proxy. The proxy needs to provide shell completions, help text, and argument validation (acceptable if not complete) without shelling out to the underlying binaries on every invocation. However, directly linking the specific binaries (or even just their clap derives) into the proxy is not viable because: - **Binary size**: some binaries pull in heavy dependencies (e.g. C++ bindings) that would bloat the proxy far beyond what a thin dispatcher needs - **Traceability**: the actual work must still be performed by the specific binary for auditing and process-tracking purposes - **Scalability**: as the number of proxied binaries grows, linking them all becomes impractical With serde support, each binary can export its `Command` definition to a file (JSON, TOML, etc.) at build time. The proxy binary then deserializes these specs, mounts them as subcommands, and gets full CLI integration (help, completions, validation) for free — without any source-level dependency on the specific binaries. ## Related - #5109 — "Dynamic subcommands": requested loading command definitions from files at runtime, noted `clap_serde` appears deprecated ### Describe the solution you'd like Add an optional `serde` cargo feature that conditionally derives `serde::Serialize` and `serde::Deserialize` on `Command` and all related subtypes (`Arg`, `ArgGroup`, `ArgAction`, `ArgPredicate`, `ValueRange`, `ValueHint`, `PossibleValue`, `StyledStr`, `Str`, `OsStr`, `Id`, etc.). Fields that are inherently non-serializable (trait objects like `ValueParser`, type-erased `Extensions`, function pointers) would be skipped with `#[serde(skip)]`. ## Example usage ```rust // In each specific binary: export the CLI spec let cmd = Command::new("my-tool") .version("1.0") .arg(Arg::new("input").long("input").action(ArgAction::Set)); let json = serde_json::to_string_pretty(&cmd)?; std::fs::write("my-tool.cli.json", &json)?; // In the proxy binary: reconstruct and mount as subcommands let spec = std::fs::read_to_string("my-tool.cli.json")?; let subcmd: Command = serde_json::from_str(&spec)?; let proxy = Command::new("proxy") .subcommand(subcmd) .get_matches(); ``` ### Alternatives, if applicable _No response_ ### Additional Context There is currently no built-in way to serialize/deserialize `Command`, `Arg`, `ArgGroup`, and related builder types. This makes it difficult to build tooling that needs to inspect, transform, or reconstruct CLI definitions outside of the original binary. Third-party crates like [`clap-serde`](https://docs.rs/clap-serde) and [`clap-serde-derive`](https://docs.rs/clap-serde-derive/) attempt to fill this gap but appear unmaintained and don't provide comprehensive coverage of clap's builder types.
4 条评论