parsql_cli/
config.rs

1//! Configuration handling for parsql CLI
2
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5use std::fs;
6use std::path::Path;
7
8#[derive(Debug, Serialize, Deserialize, Default)]
9pub struct Config {
10    #[serde(default)]
11    pub migrations: MigrationConfig,
12    
13    #[serde(default)]
14    pub database: Option<DatabaseConfig>,
15    
16    #[serde(skip)]
17    pub database_url: Option<String>,
18}
19
20#[derive(Debug, Serialize, Deserialize)]
21pub struct MigrationConfig {
22    #[serde(default = "default_migrations_dir")]
23    pub directory: String,
24    
25    #[serde(default = "default_table_name")]
26    pub table_name: String,
27    
28    #[serde(default = "default_true")]
29    pub transaction_per_migration: bool,
30    
31    #[serde(default)]
32    pub allow_out_of_order: bool,
33    
34    #[serde(default = "default_true")]
35    pub verify_checksums: bool,
36    
37    #[serde(default)]
38    pub auto_create_table: Option<bool>,
39}
40
41#[derive(Debug, Serialize, Deserialize)]
42pub struct DatabaseConfig {
43    pub url: String,
44}
45
46impl MigrationConfig {
47    pub fn to_parsql_migrations_config(&self) -> parsql_migrations::config::MigrationConfig {
48        let mut config = parsql_migrations::config::MigrationConfig::default();
49        config.table.table_name = self.table_name.clone();
50        config.transaction_per_migration = self.transaction_per_migration;
51        config.verify_checksums = self.verify_checksums;
52        config.allow_out_of_order = self.allow_out_of_order;
53        config.auto_create_table = self.auto_create_table.unwrap_or(true);
54        config
55    }
56}
57
58impl Default for MigrationConfig {
59    fn default() -> Self {
60        Self {
61            directory: default_migrations_dir(),
62            table_name: default_table_name(),
63            transaction_per_migration: true,
64            allow_out_of_order: false,
65            verify_checksums: true,
66            auto_create_table: Some(true),
67        }
68    }
69}
70
71fn default_migrations_dir() -> String {
72    "migrations".to_string()
73}
74
75fn default_table_name() -> String {
76    "parsql_migrations".to_string()
77}
78
79fn default_true() -> bool {
80    true
81}
82
83pub fn load_config(path: &str) -> Result<Config> {
84    let config_path = Path::new(path);
85    
86    if !config_path.exists() {
87        // Return default config if file doesn't exist
88        return Ok(Config::default());
89    }
90    
91    let contents = fs::read_to_string(config_path)
92        .with_context(|| format!("Failed to read config file: {}", path))?;
93    
94    let mut config: Config = toml::from_str(&contents)
95        .with_context(|| format!("Failed to parse config file: {}", path))?;
96    
97    // If database URL is in config, use it
98    if let Some(ref db_config) = config.database {
99        config.database_url = Some(db_config.url.clone());
100    }
101    
102    Ok(config)
103}
104
105impl Config {
106    pub fn to_parsql_migration_config(&self) -> parsql_migrations::MigrationConfig {
107        let mut config = parsql_migrations::MigrationConfig::default();
108        
109        config.table.table_name = self.migrations.table_name.clone();
110        config.transaction_per_migration = self.migrations.transaction_per_migration;
111        config.allow_out_of_order = self.migrations.allow_out_of_order;
112        config.verify_checksums = self.migrations.verify_checksums;
113        
114        if let Some(auto_create) = self.migrations.auto_create_table {
115            config.auto_create_table = auto_create;
116        }
117        
118        config
119    }
120
121    /// Create a default config with custom migrations directory (useful for testing)
122    pub fn default_with_directory(directory: &str) -> Self {
123        let mut config = Config::default();
124        config.migrations.directory = directory.to_string();
125        config
126    }
127}