1#![expect(
5 missing_docs,
6 reason = "bin crate; internal lib shim exists only to support integration tests"
7)]
8
9use clap::{Parser, Subcommand, ValueEnum};
10
11#[cfg(feature = "builtin-sqlite")]
12mod config;
13
14#[cfg(feature = "builtin-sqlite")]
15mod runtime;
16
17#[cfg(feature = "builtin-sqlite")]
18mod codegen;
19
20#[cfg(feature = "mcp")]
21#[expect(
22 clippy::needless_pass_by_value,
23 reason = "rmcp #[tool(aggr)] requires by-value params"
24)]
25mod mcp;
26
27#[derive(Clone, Copy, ValueEnum)]
28pub(crate) enum ParseOutput {
29 Summary,
31 Text,
33 Json,
35}
36
37#[derive(Clone, Copy, Default, ValueEnum)]
38pub(crate) enum FmtOutput {
39 #[default]
41 Formatted,
42 Bytecode,
44 DocTree,
46}
47
48#[derive(Parser)]
49#[command(
50 name = "syntaqlite",
51 about = "SQL formatting and analysis tools",
52 version
53)]
54pub(crate) struct Cli {
55 #[cfg(feature = "builtin-sqlite")]
58 #[arg(short = 'c', long = "config", global = true)]
59 pub(crate) config: Option<String>,
60
61 #[cfg(feature = "builtin-sqlite")]
63 #[arg(long = "no-config", global = true, conflicts_with = "config")]
64 pub(crate) no_config: bool,
65
66 #[cfg(feature = "builtin-sqlite")]
68 #[arg(long = "dialect")]
69 pub(crate) dialect_path: Option<String>,
70
71 #[cfg(feature = "builtin-sqlite")]
75 #[arg(long, requires = "dialect_path")]
76 pub(crate) dialect_name: Option<String>,
77
78 #[cfg(feature = "builtin-sqlite")]
80 #[arg(long)]
81 pub(crate) sqlite_version: Option<String>,
82
83 #[cfg(feature = "builtin-sqlite")]
86 #[arg(long)]
87 pub(crate) sqlite_cflag: Vec<String>,
88
89 #[command(subcommand)]
90 pub(crate) command: Command,
91}
92
93#[derive(Subcommand)]
94pub(crate) enum Command {
95 #[cfg(feature = "builtin-sqlite")]
97 Parse {
98 files: Vec<String>,
100 #[arg(short = 'e', long = "expression", conflicts_with = "files")]
102 expression: Option<String>,
103 #[arg(short, long, value_enum, default_value_t = ParseOutput::Text)]
105 output: ParseOutput,
106 },
107 #[cfg(feature = "builtin-sqlite")]
109 Fmt {
110 files: Vec<String>,
112 #[arg(short = 'e', long = "expression", conflicts_with = "files")]
114 expression: Option<String>,
115 #[arg(short = 'w', long)]
117 line_width: Option<usize>,
118 #[arg(short = 't', long)]
120 indent_width: Option<usize>,
121 #[arg(short = 'k', long, value_enum)]
123 keyword_case: Option<runtime::KeywordCasing>,
124 #[arg(short = 'i', long)]
126 in_place: bool,
127 #[arg(long, conflicts_with = "in_place")]
129 check: bool,
130 #[arg(long)]
132 semicolons: Option<bool>,
133 #[arg(short, long, value_enum, default_value_t = FmtOutput::Formatted)]
135 output: FmtOutput,
136 },
137 #[cfg(feature = "builtin-sqlite")]
139 Validate {
140 files: Vec<String>,
142 #[arg(short = 'e', long = "expression", conflicts_with = "files")]
144 expression: Option<String>,
145 #[arg(long)]
147 schema: Vec<String>,
148 #[arg(short = 'A', long = "allow")]
150 allow: Vec<String>,
151 #[arg(short = 'W', long = "warn")]
153 warn: Vec<String>,
154 #[arg(short = 'D', long = "deny")]
156 deny: Vec<String>,
157 #[arg(long = "experimental-lang")]
159 lang: Option<runtime::HostLanguage>,
160 },
161 #[cfg(feature = "builtin-sqlite")]
163 Lsp,
164 #[cfg(feature = "mcp")]
166 Mcp,
167 #[cfg(feature = "builtin-sqlite")]
169 Dialect(codegen::DialectArgs),
170 Version,
172 #[cfg(feature = "builtin-sqlite")]
173 #[command(flatten)]
174 DialectTool(codegen::ToolCommand),
175}
176
177#[cfg(feature = "builtin-sqlite")]
179pub fn run(name: &str, dialect: Option<syntaqlite::any::AnyDialect>) {
180 let cli =
181 Cli::try_parse_from(std::iter::once(name.to_string()).chain(std::env::args().skip(1)))
182 .unwrap_or_else(|e| e.exit());
183
184 let result = runtime::dispatch(cli, dialect);
185
186 if let Err(e) = result {
187 eprintln!("error: {e}");
188 std::process::exit(1);
189 }
190}