1use 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 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 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 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}