1use baobao_core::{ArgType, Version};
7use baobao_ir::{DefaultValue, Input, InputKind, InputType};
8
9use crate::builder::CodeFragment;
10
11pub fn input_type_to_arg_type(input_type: InputType) -> ArgType {
16 match input_type {
17 InputType::String => ArgType::String,
18 InputType::Int => ArgType::Int,
19 InputType::Float => ArgType::Float,
20 InputType::Bool => ArgType::Bool,
21 InputType::Path => ArgType::Path,
22 }
23}
24
25#[derive(Debug, Clone)]
30pub struct IRArgMeta {
31 pub name: String,
33 pub field_name: String,
35 pub arg_type: ArgType,
37 pub required: bool,
39 pub default: Option<DefaultValue>,
41 pub description: Option<String>,
43 pub choices: Option<Vec<String>>,
45}
46
47impl IRArgMeta {
48 pub fn from_input(input: &Input, field_name: impl Into<String>) -> Self {
50 Self {
51 name: input.name.clone(),
52 field_name: field_name.into(),
53 arg_type: input_type_to_arg_type(input.ty),
54 required: input.required,
55 default: input.default.clone(),
56 description: input.description.clone(),
57 choices: input.choices.clone(),
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
67pub struct IRFlagMeta {
68 pub name: String,
70 pub field_name: String,
72 pub short: Option<char>,
74 pub flag_type: ArgType,
76 pub default: Option<DefaultValue>,
78 pub description: Option<String>,
80 pub choices: Option<Vec<String>>,
82}
83
84impl IRFlagMeta {
85 pub fn from_input(input: &Input, field_name: impl Into<String>) -> Self {
87 let short = if let InputKind::Flag { short } = &input.kind {
88 *short
89 } else {
90 None
91 };
92
93 Self {
94 name: input.name.clone(),
95 field_name: field_name.into(),
96 short,
97 flag_type: input_type_to_arg_type(input.ty),
98 default: input.default.clone(),
99 description: input.description.clone(),
100 choices: input.choices.clone(),
101 }
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct Dependency {
108 pub name: String,
110 pub version: String,
112 pub dev: bool,
114}
115
116impl Dependency {
117 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
119 Self {
120 name: name.into(),
121 version: version.into(),
122 dev: false,
123 }
124 }
125
126 pub fn dev(name: impl Into<String>, version: impl Into<String>) -> Self {
128 Self {
129 name: name.into(),
130 version: version.into(),
131 dev: true,
132 }
133 }
134}
135
136#[derive(Debug, Clone)]
138pub struct ImportSpec {
139 pub module: String,
141 pub symbols: Vec<String>,
143 pub type_only: bool,
145}
146
147impl ImportSpec {
148 pub fn new(module: impl Into<String>) -> Self {
150 Self {
151 module: module.into(),
152 symbols: Vec::new(),
153 type_only: false,
154 }
155 }
156
157 pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
159 self.symbols.push(symbol.into());
160 self
161 }
162
163 pub fn symbols(mut self, symbols: impl IntoIterator<Item = impl Into<String>>) -> Self {
165 self.symbols.extend(symbols.into_iter().map(Into::into));
166 self
167 }
168
169 pub fn type_only(mut self) -> Self {
171 self.type_only = true;
172 self
173 }
174}
175
176#[derive(Debug, Clone)]
178pub struct CliInfo {
179 pub name: String,
181 pub version: Version,
183 pub description: Option<String>,
185 pub commands: Vec<CommandMeta>,
187 pub is_async: bool,
189}
190
191#[derive(Debug, Clone)]
193pub struct CommandMeta {
194 pub name: String,
196 pub pascal_name: String,
198 pub snake_name: String,
200 pub description: String,
202 pub args: Vec<ArgMeta>,
204 pub flags: Vec<FlagMeta>,
206 pub has_subcommands: bool,
208 pub subcommands: Vec<SubcommandMeta>,
210}
211
212#[derive(Debug, Clone)]
214pub struct ArgMeta {
215 pub name: String,
217 pub field_name: String,
219 pub arg_type: ArgType,
221 pub required: bool,
223 pub default: Option<String>,
225 pub description: Option<String>,
227}
228
229#[derive(Debug, Clone)]
231pub struct FlagMeta {
232 pub name: String,
234 pub field_name: String,
236 pub short: Option<char>,
238 pub flag_type: ArgType,
240 pub default: Option<String>,
242 pub description: Option<String>,
244}
245
246#[derive(Debug, Clone)]
248pub struct SubcommandMeta {
249 pub name: String,
251 pub pascal_name: String,
253 pub snake_name: String,
255 pub description: String,
257 pub has_subcommands: bool,
259}
260
261#[derive(Debug, Clone)]
263pub struct DispatchInfo {
264 pub parent_name: String,
266 pub subcommands: Vec<SubcommandMeta>,
268 pub handler_path: String,
270 pub is_async: bool,
272}
273
274pub trait CliAdapter {
278 fn name(&self) -> &'static str;
280
281 fn dependencies(&self) -> Vec<Dependency>;
283
284 fn generate_cli(&self, info: &CliInfo) -> Vec<CodeFragment>;
286
287 fn generate_command(&self, info: &CommandMeta) -> Vec<CodeFragment>;
289
290 fn generate_subcommands(&self, info: &CommandMeta) -> Vec<CodeFragment>;
292
293 fn generate_dispatch(&self, info: &DispatchInfo) -> Vec<CodeFragment>;
295
296 fn imports(&self) -> Vec<ImportSpec>;
298
299 fn command_imports(&self, info: &CommandMeta) -> Vec<ImportSpec>;
301
302 fn map_arg_type(&self, arg_type: ArgType) -> &'static str;
304
305 fn map_optional_type(&self, arg_type: ArgType) -> String;
307}