prax_cli/
config.rs

1//! CLI configuration handling.
2
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5
6use crate::error::CliResult;
7
8/// Default config file name (lives in project root)
9pub const CONFIG_FILE_NAME: &str = "prax.toml";
10
11/// Default Prax directory name
12pub const PRAX_DIR: &str = "prax";
13
14/// Default schema file name (relative to prax directory)
15pub const SCHEMA_FILE_NAME: &str = "schema.prax";
16
17/// Default schema file path (relative to project root)
18pub const SCHEMA_FILE_PATH: &str = "prax/schema.prax";
19
20/// Default migrations directory (relative to project root)
21pub const MIGRATIONS_DIR: &str = "prax/migrations";
22
23/// Prax CLI configuration
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(default)]
26pub struct Config {
27    /// Database configuration
28    pub database: DatabaseConfig,
29
30    /// Generator configuration
31    pub generator: GeneratorConfig,
32
33    /// Migration configuration
34    pub migrations: MigrationConfig,
35
36    /// Seed configuration
37    pub seed: SeedConfig,
38}
39
40impl Default for Config {
41    fn default() -> Self {
42        Self {
43            database: DatabaseConfig::default(),
44            generator: GeneratorConfig::default(),
45            migrations: MigrationConfig::default(),
46            seed: SeedConfig::default(),
47        }
48    }
49}
50
51impl Config {
52    /// Load configuration from a file
53    pub fn load(path: &Path) -> CliResult<Self> {
54        let content = std::fs::read_to_string(path)?;
55        let config: Config = toml::from_str(&content)?;
56        Ok(config)
57    }
58
59    /// Save configuration to a file
60    pub fn save(&self, path: &Path) -> CliResult<()> {
61        let content = toml::to_string_pretty(self)?;
62        std::fs::write(path, content)?;
63        Ok(())
64    }
65
66    /// Create a default config for a specific provider
67    pub fn default_for_provider(provider: &str) -> Self {
68        let mut config = Self::default();
69        config.database.provider = provider.to_string();
70        config
71    }
72}
73
74/// Database configuration
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(default)]
77pub struct DatabaseConfig {
78    /// Database provider (postgresql, mysql, sqlite)
79    pub provider: String,
80
81    /// Database connection URL
82    pub url: Option<String>,
83
84    /// Shadow database URL (for safe migrations)
85    pub shadow_url: Option<String>,
86
87    /// Direct database URL (bypasses connection pooling)
88    pub direct_url: Option<String>,
89
90    /// Path to seed file
91    pub seed_path: Option<PathBuf>,
92}
93
94impl Default for DatabaseConfig {
95    fn default() -> Self {
96        Self {
97            provider: "postgresql".to_string(),
98            url: None,
99            shadow_url: None,
100            direct_url: None,
101            seed_path: None,
102        }
103    }
104}
105
106/// Generator configuration
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(default)]
109pub struct GeneratorConfig {
110    /// Output directory for generated code
111    pub output: String,
112
113    /// Features to enable (serde, graphql, etc.)
114    pub features: Option<Vec<String>>,
115
116    /// Custom prelude imports
117    pub prelude: Option<Vec<String>>,
118}
119
120impl Default for GeneratorConfig {
121    fn default() -> Self {
122        Self {
123            output: "./src/generated".to_string(),
124            features: None,
125            prelude: None,
126        }
127    }
128}
129
130/// Migration configuration
131#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(default)]
133pub struct MigrationConfig {
134    /// Directory for migration files
135    pub directory: String,
136
137    /// Migration table name
138    pub table_name: String,
139
140    /// Schema for migration table (PostgreSQL only)
141    pub schema: Option<String>,
142}
143
144impl Default for MigrationConfig {
145    fn default() -> Self {
146        Self {
147            directory: MIGRATIONS_DIR.to_string(),
148            table_name: "_prax_migrations".to_string(),
149            schema: None,
150        }
151    }
152}
153
154/// Seed configuration
155#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(default)]
157pub struct SeedConfig {
158    /// Path to seed script
159    pub script: Option<PathBuf>,
160
161    /// Run seed automatically after migrations
162    pub auto_seed: bool,
163
164    /// Environment-specific seeding
165    /// Key: environment name, Value: whether to seed in that environment
166    pub environments: std::collections::HashMap<String, bool>,
167}
168
169impl Default for SeedConfig {
170    fn default() -> Self {
171        let mut environments = std::collections::HashMap::new();
172        environments.insert("development".to_string(), true);
173        environments.insert("test".to_string(), true);
174        environments.insert("staging".to_string(), false);
175        environments.insert("production".to_string(), false);
176
177        Self {
178            script: None,
179            auto_seed: false,
180            environments,
181        }
182    }
183}
184
185impl SeedConfig {
186    /// Check if seeding should run for the given environment
187    pub fn should_seed(&self, environment: &str) -> bool {
188        self.environments.get(environment).copied().unwrap_or(false)
189    }
190}