1mod cli_sub_command;
2mod error;
3mod impl_metadata;
4mod impl_sql_fun_home;
5mod init_args;
6mod log_args;
7mod metadata_args;
8mod postgres;
9
10use std::{collections::HashMap, fmt::Display, path::PathBuf};
11
12use clap::Parser;
13
14use self::log_args::LoggingArgs;
15pub use self::{
16 cli_sub_command::CliSubCommand, error::SqlFunArgsError, init_args::InitializeArgs,
17 metadata_args::MetadataArgs, postgres::PostgresArgs,
18};
19
20use crate::{
21 HighlighterTheme, PostgresExtensionsCollection, SqlDialect, SqlFunMetadata, TerminalColor,
22 metadata::EngineVersion,
23};
24
25#[derive(Debug)]
26pub enum ConfigurationKind {
27 EngineVersion,
28}
29
30impl Display for ConfigurationKind {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 ConfigurationKind::EngineVersion => write!(f, "Database engine version"),
34 }
35 }
36}
37
38#[derive(clap::Parser, Debug, serde::Serialize, serde::Deserialize, Clone)]
40#[clap(name = "sql-fun", version, about, long_about = None)]
41pub struct SqlFunArgs {
42 #[command(flatten)]
43 metadata_args: MetadataArgs,
44
45 #[command(flatten)]
46 postgres_args: PostgresArgs,
47
48 #[command(flatten)]
49 log_args: LoggingArgs,
50
51 #[clap(long, env = Self::SQL_FUN_OUTPUT_FORMAT, default_value = "text")]
53 output_format: String,
54
55 #[clap(long, env = Self::SQL_FUN_TERM_COLOR, default_value = "auto")]
57 term_color: TerminalColor,
58
59 #[clap(long, env = Self::SQL_FUN_HIGHLIGHTER_THEME)]
67 highlighter_theme: Option<PathBuf>,
68
69 #[clap(long)]
71 verbose: bool,
72
73 #[clap(long, env = Self::SQL_FUN_NO_BUILTIN)]
75 no_builtin_tables: bool,
76
77 #[clap(subcommand)]
79 #[serde(skip)]
80 command: Option<CliSubCommand>,
81}
82
83impl SqlFunArgs {
84 #[must_use]
86 pub fn is_builtin(&self) -> bool {
87 matches!(self.command, Some(CliSubCommand::Initialize(_)))
88 }
89
90 pub fn sql_dialect(&self, metadata: &SqlFunMetadata) -> Result<SqlDialect, SqlFunArgsError> {
96 self.metadata_args.sql_dialect(metadata)
97 }
98
99 pub fn cte_catalog_dir(
105 &self,
106 metadata: &SqlFunMetadata,
107 ) -> Result<Option<PathBuf>, SqlFunArgsError> {
108 self.metadata_args.cte_catalog_dir(metadata)
109 }
110
111 #[must_use]
113 pub fn command(&self) -> &Option<CliSubCommand> {
114 &self.command
115 }
116
117 pub fn postgres_version(
123 &self,
124 metadata: &SqlFunMetadata,
125 ) -> Result<EngineVersion, SqlFunArgsError> {
126 self.postgres_args.postgres_version(metadata)
127 }
128
129 pub fn postgres_search_path(
135 &self,
136 metadata: &SqlFunMetadata,
137 ) -> Result<Vec<String>, SqlFunArgsError> {
138 self.postgres_args.postgres_search_path(metadata)
139 }
140
141 pub fn postgres_extensions(
148 &self,
149 metadata: &SqlFunMetadata,
150 ) -> Result<PostgresExtensionsCollection, SqlFunArgsError> {
151 self.postgres_args.postgres_extensions(metadata)
152 }
153
154 pub fn builtin_info_dir(&self, metadata: &SqlFunMetadata) -> Result<PathBuf, SqlFunArgsError> {
160 if let Some(arg) = self.postgres_args.builtin_schema_dir() {
161 Ok(arg.clone())
162 } else {
163 let home = self.sql_fun_home()?;
164 let postgres_version = self.postgres_version(metadata)?;
165 Ok(postgres_version.definition_base_path(home))
166 }
167 }
168
169 pub fn postgres_extensions_dir(
175 &self,
176 metadata: &SqlFunMetadata,
177 ) -> Result<PathBuf, SqlFunArgsError> {
178 if let Some(ext_dir) = self.postgres_args.postgres_extensions_dir() {
179 Ok(ext_dir.clone())
180 } else {
181 let home = self.sql_fun_home()?;
182 let dialect = self.sql_dialect(metadata)?;
183 let ver = self.postgres_version(metadata)?;
184 Ok(home
185 .join(dialect.to_string())
186 .join(ver.to_string())
187 .join("extensions"))
188 }
189 }
190
191 const SQL_FUN_OUTPUT_FORMAT: &str = "SQL_FUN_OUTPUT_FORMAT";
192 const SQL_FUN_NO_BUILTIN: &str = "SQL_FUN_NO_BUILTIN";
193 const SQL_FUN_TERM_COLOR: &str = "SQL_FUN_TERM_COLOR";
194 const SQL_FUN_HIGHLIGHTER_THEME: &str = "SQL_FUN_HIGHLIGHTER_THEME";
195
196 pub fn environments(&self) -> Result<HashMap<String, String>, SqlFunArgsError> {
203 let mut result = HashMap::new();
204 let metadata_file = self.metadata_file()?;
205 let metadata = SqlFunMetadata::load_from(&metadata_file)?;
206
207 self.metadata_args.get_envs(&mut result, &metadata)?;
208 self.postgres_args.get_envs(&mut result, self, &metadata)?;
209 self.log_args.get_envs(&mut result);
210
211 result.insert(
212 Self::SQL_FUN_OUTPUT_FORMAT.to_string(),
213 self.output_format.clone(),
214 );
215
216 Ok(result)
217 }
218
219 #[must_use]
221 pub fn term_color(&self) -> TerminalColor {
222 self.term_color.get_value()
223 }
224
225 pub fn highlighter_theme(&self) -> Result<HighlighterTheme, SqlFunArgsError> {
239 let term_color = self.term_color();
240 if term_color.is_never() {
241 Ok(HighlighterTheme::default())
242 } else {
243 let theme_path = if let Some(theme_path) = &self.highlighter_theme {
244 if theme_path.is_absolute() {
245 theme_path.clone()
246 } else {
247 let mut base = self.sql_fun_home()?;
248 base.push("highlighter");
249 base.push(theme_path);
250 base
251 }
252 } else {
253 let mut path = self.sql_fun_home()?;
254 path.push("highlighter/default.toml");
255 path
256 };
257 Ok(HighlighterTheme::from_toml(theme_path)?)
258 }
259 }
260}
261
262#[derive(clap::Parser, Debug, Clone, serde::Serialize, serde::Deserialize)]
264pub struct DaemonControlArgs {
265 daemon_name: String,
267 daemon_args: Vec<String>,
269}
270
271impl SqlFunArgs {
272 #[must_use]
274 pub fn parse_args() -> Self {
275 let mut args = SqlFunArgs::parse();
276 if args.verbose {
277 args.log_args.set_log_level("debug");
278 }
279 args
280 }
281
282 pub fn try_from_env() -> Result<Self, SqlFunArgsError> {
288 let mut args = SqlFunArgs::try_parse_from(Vec::<String>::default())?;
289 if args.verbose {
290 args.log_args.set_log_level("debug");
291 }
292 Ok(args)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298
299 use clap::Parser;
300 use testresult::TestResult;
301
302 use crate::SqlFunArgs;
303
304 #[test]
305 pub fn test_schema_file() -> TestResult {
306 let args = SqlFunArgs::try_parse_from(vec![
307 "sqlfun",
308 "--sql-fun-home",
309 "../sql-fun-home",
310 "subcmd",
311 ])?;
312 let metadata_file = args.metadata_file()?;
313 let expect_file = metadata_file.with_file_name("sqlfun.develop.sql");
314 let mut created = false;
315 if !expect_file.exists() {
316 std::fs::write(&expect_file, "-- dummy sql file")?;
317 created = true;
318 }
319 let schema_file = args.schema_file(&metadata_file)?;
320 if created {
321 std::fs::remove_file(&expect_file)?;
322 }
323 assert_eq!(schema_file, expect_file);
324 Ok(())
325 }
326}