Skip to main content

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