baobao_codegen/adapters/
cli.rs

1//! CLI framework adapter abstraction.
2//!
3//! This module defines the [`CliAdapter`] trait for abstracting CLI framework-specific
4//! code generation (clap, argh, boune, commander, etc.).
5
6use baobao_core::{ArgType, Version};
7
8use crate::builder::CodeFragment;
9
10/// Dependency specification for an adapter.
11#[derive(Debug, Clone)]
12pub struct Dependency {
13    /// Package/crate name
14    pub name: String,
15    /// Version specification (e.g., "1.0", "^0.5.0", `{ version = "4", features = ["derive"] }`)
16    pub version: String,
17    /// Whether this is a dev dependency
18    pub dev: bool,
19}
20
21impl Dependency {
22    /// Create a new runtime dependency.
23    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
24        Self {
25            name: name.into(),
26            version: version.into(),
27            dev: false,
28        }
29    }
30
31    /// Create a new dev dependency.
32    pub fn dev(name: impl Into<String>, version: impl Into<String>) -> Self {
33        Self {
34            name: name.into(),
35            version: version.into(),
36            dev: true,
37        }
38    }
39}
40
41/// Import specification for generated code.
42#[derive(Debug, Clone)]
43pub struct ImportSpec {
44    /// Module/package path
45    pub module: String,
46    /// Symbols to import (empty = import module itself)
47    pub symbols: Vec<String>,
48    /// Whether this is a type-only import (TypeScript)
49    pub type_only: bool,
50}
51
52impl ImportSpec {
53    /// Create a new import specification.
54    pub fn new(module: impl Into<String>) -> Self {
55        Self {
56            module: module.into(),
57            symbols: Vec::new(),
58            type_only: false,
59        }
60    }
61
62    /// Add a symbol to import.
63    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
64        self.symbols.push(symbol.into());
65        self
66    }
67
68    /// Add multiple symbols to import.
69    pub fn symbols(mut self, symbols: impl IntoIterator<Item = impl Into<String>>) -> Self {
70        self.symbols.extend(symbols.into_iter().map(Into::into));
71        self
72    }
73
74    /// Mark as type-only import (for TypeScript).
75    pub fn type_only(mut self) -> Self {
76        self.type_only = true;
77        self
78    }
79}
80
81/// Framework-agnostic CLI application info.
82#[derive(Debug, Clone)]
83pub struct CliInfo {
84    /// Application name
85    pub name: String,
86    /// Application version
87    pub version: Version,
88    /// Application description
89    pub description: Option<String>,
90    /// Top-level commands
91    pub commands: Vec<CommandMeta>,
92    /// Whether any command uses async
93    pub is_async: bool,
94}
95
96/// Framework-agnostic command metadata.
97#[derive(Debug, Clone)]
98pub struct CommandMeta {
99    /// Command name (e.g., "hello", "db")
100    pub name: String,
101    /// Pascal case name (e.g., "Hello", "Db")
102    pub pascal_name: String,
103    /// Snake case name (e.g., "hello", "db")
104    pub snake_name: String,
105    /// Command description
106    pub description: String,
107    /// Positional arguments
108    pub args: Vec<ArgMeta>,
109    /// Optional flags
110    pub flags: Vec<FlagMeta>,
111    /// Whether this command has subcommands
112    pub has_subcommands: bool,
113    /// Subcommands (if any)
114    pub subcommands: Vec<SubcommandMeta>,
115}
116
117/// Framework-agnostic positional argument metadata.
118#[derive(Debug, Clone)]
119pub struct ArgMeta {
120    /// Argument name
121    pub name: String,
122    /// Snake case name for field
123    pub field_name: String,
124    /// Argument type
125    pub arg_type: ArgType,
126    /// Whether this argument is required
127    pub required: bool,
128    /// Default value (if any)
129    pub default: Option<String>,
130    /// Argument description
131    pub description: Option<String>,
132}
133
134/// Framework-agnostic flag metadata.
135#[derive(Debug, Clone)]
136pub struct FlagMeta {
137    /// Flag name (long form, e.g., "verbose")
138    pub name: String,
139    /// Snake case name for field
140    pub field_name: String,
141    /// Short flag character (e.g., 'v')
142    pub short: Option<char>,
143    /// Flag type
144    pub flag_type: ArgType,
145    /// Default value (if any)
146    pub default: Option<String>,
147    /// Flag description
148    pub description: Option<String>,
149}
150
151/// Framework-agnostic subcommand metadata.
152#[derive(Debug, Clone)]
153pub struct SubcommandMeta {
154    /// Subcommand name
155    pub name: String,
156    /// Pascal case name
157    pub pascal_name: String,
158    /// Snake case name
159    pub snake_name: String,
160    /// Subcommand description
161    pub description: String,
162    /// Whether this subcommand has its own subcommands
163    pub has_subcommands: bool,
164}
165
166/// Info needed to generate command dispatch logic.
167#[derive(Debug, Clone)]
168pub struct DispatchInfo {
169    /// Parent command name (pascal case)
170    pub parent_name: String,
171    /// Subcommands to dispatch to
172    pub subcommands: Vec<SubcommandMeta>,
173    /// Handler module path prefix
174    pub handler_path: String,
175    /// Whether dispatch is async
176    pub is_async: bool,
177}
178
179/// Trait for CLI framework adapters.
180///
181/// Implement this trait to support a specific CLI framework (clap, boune, etc.).
182pub trait CliAdapter {
183    /// Adapter name for identification.
184    fn name(&self) -> &'static str;
185
186    /// Dependencies required by this adapter.
187    fn dependencies(&self) -> Vec<Dependency>;
188
189    /// Generate the main CLI entry point structure/definition.
190    fn generate_cli(&self, info: &CliInfo) -> Vec<CodeFragment>;
191
192    /// Generate a command definition (args struct, command object, etc.).
193    fn generate_command(&self, info: &CommandMeta) -> Vec<CodeFragment>;
194
195    /// Generate subcommand enum/routing for a parent command.
196    fn generate_subcommands(&self, info: &CommandMeta) -> Vec<CodeFragment>;
197
198    /// Generate dispatch logic for routing to handlers.
199    fn generate_dispatch(&self, info: &DispatchInfo) -> Vec<CodeFragment>;
200
201    /// Imports needed for CLI code.
202    fn imports(&self) -> Vec<ImportSpec>;
203
204    /// Imports needed for a specific command.
205    fn command_imports(&self, info: &CommandMeta) -> Vec<ImportSpec>;
206
207    /// Map an argument type to the adapter's type string.
208    fn map_arg_type(&self, arg_type: ArgType) -> &'static str;
209
210    /// Map an optional argument type to the adapter's type string.
211    fn map_optional_type(&self, arg_type: ArgType) -> String;
212}