1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use clap::ValueEnum;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum NameCase {
10 Snake,
11 Camel,
12 Pascal,
13}
14
15impl NameCase {
16 pub fn is_snake(self) -> bool {
18 matches!(self, NameCase::Snake)
19 }
20
21 pub fn is_camel(self) -> bool {
23 matches!(self, NameCase::Camel)
24 }
25
26 pub fn is_pascal(self) -> bool {
28 matches!(self, NameCase::Pascal)
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
34#[serde(rename_all = "lowercase")]
35pub enum FileFormat {
36 Json,
37 Yaml,
38 Yml,
39}
40
41impl Default for FileFormat {
42 fn default() -> Self {
43 FileFormat::Json
44 }
45}
46
47pub fn default_migration_filename_pattern() -> String {
49 "%04v_%m".to_string()
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct VespertideConfig {
56 pub models_dir: PathBuf,
57 pub migrations_dir: PathBuf,
58 pub table_naming_case: NameCase,
59 pub column_naming_case: NameCase,
60 #[serde(default)]
61 pub model_format: FileFormat,
62 #[serde(default)]
63 pub migration_format: FileFormat,
64 #[serde(default = "default_migration_filename_pattern")]
65 pub migration_filename_pattern: String,
66}
67
68impl Default for VespertideConfig {
69 fn default() -> Self {
70 Self {
71 models_dir: PathBuf::from("models"),
72 migrations_dir: PathBuf::from("migrations"),
73 table_naming_case: NameCase::Snake,
74 column_naming_case: NameCase::Snake,
75 model_format: FileFormat::Json,
76 migration_format: FileFormat::Json,
77 migration_filename_pattern: default_migration_filename_pattern(),
78 }
79 }
80}
81
82impl VespertideConfig {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn models_dir(&self) -> &Path {
89 &self.models_dir
90 }
91
92 pub fn migrations_dir(&self) -> &Path {
94 &self.migrations_dir
95 }
96
97 pub fn table_case(&self) -> NameCase {
99 self.table_naming_case
100 }
101
102 pub fn column_case(&self) -> NameCase {
104 self.column_naming_case
105 }
106
107 pub fn model_format(&self) -> FileFormat {
109 self.model_format
110 }
111
112 pub fn migration_format(&self) -> FileFormat {
114 self.migration_format
115 }
116
117 pub fn migration_filename_pattern(&self) -> &str {
119 &self.migration_filename_pattern
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn default_values_are_snake_and_standard_paths() {
129 let cfg = VespertideConfig::default();
130 assert_eq!(cfg.models_dir, PathBuf::from("models"));
131 assert_eq!(cfg.migrations_dir, PathBuf::from("migrations"));
132 assert!(cfg.table_case().is_snake());
133 assert!(cfg.column_case().is_snake());
134 }
135
136 #[test]
137 fn overrides_work_via_struct_update() {
138 let cfg = VespertideConfig {
139 models_dir: PathBuf::from("custom_models"),
140 migrations_dir: PathBuf::from("custom_migrations"),
141 table_naming_case: NameCase::Camel,
142 column_naming_case: NameCase::Pascal,
143 ..Default::default()
144 };
145
146 assert_eq!(cfg.models_dir(), Path::new("custom_models"));
147 assert_eq!(cfg.migrations_dir(), Path::new("custom_migrations"));
148 assert!(cfg.table_case().is_camel());
149 assert!(cfg.column_case().is_pascal());
150 }
151}