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