meta_ast/interface/
args.rs1use std::str::FromStr;
2
3use clap::Parser;
4
5#[derive(Parser)]
7#[command(name = "meta-ast", version, about = "Polyglot static analyzer")]
8pub enum Cli {
9 Inspect(InspectArgs),
16
17 Graph(GraphArgs),
24
25 #[cfg(feature = "metacall-deploy")]
31 Deploy(DeployArgs),
32}
33
34fn parse_format(s: &str) -> Result<crate::output::OutputFormat, String> {
35 match s.to_lowercase().as_str() {
36 "json" => Ok(crate::output::OutputFormat::Json),
37 "yaml" | "yml" => Ok(crate::output::OutputFormat::Yaml),
38 _ => Err(format!("invalid format '{s}': expected 'json' or 'yaml'")),
39 }
40}
41
42fn parse_language(s: &str) -> Result<crate::language::LangId, String> {
43 let normalized = s.to_lowercase();
44 crate::language::LangId::from_str(&normalized).map_err(|_| {
45 let all = crate::language::LangId::all();
46 let names: Vec<_> = all.iter().map(|l| l.as_ref()).collect();
47 format!(
48 "invalid language '{s}': expected one of {}",
49 names.join(", ")
50 )
51 })
52}
53
54#[derive(Parser)]
55pub struct InspectArgs {
56 pub path: std::path::PathBuf,
58
59 #[arg(short, long)]
61 pub output: Option<std::path::PathBuf>,
62
63 #[arg(short, long, value_parser = parse_language)]
65 pub language: Option<crate::language::LangId>,
66
67 #[arg(short = 'f', long, default_value = "json", value_parser = parse_format)]
69 pub format: crate::output::OutputFormat,
70}
71
72#[derive(Parser)]
73pub struct GraphArgs {
74 pub path: std::path::PathBuf,
76
77 #[arg(short, long)]
79 pub output: Option<std::path::PathBuf>,
80
81 #[arg(short, long, value_parser = parse_language)]
83 pub language: Option<crate::language::LangId>,
84
85 #[arg(short = 'f', long, default_value = "json", value_parser = parse_format)]
87 pub format: crate::output::OutputFormat,
88
89 #[arg(long)]
91 pub html: bool,
92
93 #[cfg(feature = "dataflow")]
95 #[arg(long)]
96 pub datagraph: bool,
97
98 #[cfg(feature = "watch")]
100 #[arg(long)]
101 pub watch: bool,
102
103 #[cfg(feature = "watch")]
105 #[arg(long, default_value = "200")]
106 pub watch_debounce: u64,
107}
108
109#[cfg(feature = "metacall-deploy")]
110#[derive(Parser)]
111pub struct DeployArgs {
112 pub path: std::path::PathBuf,
114
115 #[arg(short = 'f', long, default_value = "json", value_parser = parse_format)]
117 pub format: crate::output::OutputFormat,
118
119 #[arg(long)]
121 pub check: bool,
122
123 #[arg(short, long, default_value = ".")]
125 pub out: std::path::PathBuf,
126
127 #[arg(long, default_value_t = crate::deploy::cut::DEFAULT_MAX_POD_SIZE)]
129 pub max_pod_size: usize,
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn parse_language_valid_lowercase() {
138 assert_eq!(
139 parse_language("python"),
140 Ok(crate::language::LangId::Python)
141 );
142 assert_eq!(
143 parse_language("typescript"),
144 Ok(crate::language::LangId::TypeScript)
145 );
146 }
147
148 #[test]
149 fn parse_language_case_insensitive() {
150 assert_eq!(
151 parse_language("Python"),
152 Ok(crate::language::LangId::Python)
153 );
154 assert_eq!(
155 parse_language("TYPESCRIPT"),
156 Ok(crate::language::LangId::TypeScript)
157 );
158 }
159
160 #[test]
161 fn parse_language_invalid_returns_all_names() {
162 let err = parse_language("pytho").unwrap_err();
163 for id in crate::language::LangId::all() {
164 let name: &str = id.as_ref();
165 assert!(
166 err.contains(name),
167 "error {err:?} should list the valid name {name:?}"
168 );
169 }
170 }
171
172 #[test]
173 fn parse_language_empty_returns_err() {
174 assert!(parse_language("").is_err());
175 }
176}