tarts/
config.rs

1use crate::{
2    boids::{BoidsOptions, BoidsOptionsBuilder},
3    crab::{CrabOptions, CrabOptionsBuilder},
4    cube::{CubeOptions, CubeOptionsBuilder},
5    error::{ConfigError, Result, TartsError},
6    life::{ConwayLifeOptions, ConwayLifeOptionsBuilder},
7    maze::{MazeOptions, MazeOptionsBuilder},
8    rain::digital_rain::{DigitalRainOptions, DigitalRainOptionsBuilder},
9};
10use directories::ProjectDirs;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Serialize, Deserialize)]
14#[serde(deny_unknown_fields)]
15pub struct Config {
16    #[serde(default)]
17    matrix: DigitalRainOptions,
18    #[serde(default)]
19    life: ConwayLifeOptions,
20    #[serde(default)]
21    maze: MazeOptions,
22    #[serde(default)]
23    boids: BoidsOptions,
24    #[serde(default)]
25    cube: CubeOptions,
26    #[serde(default)]
27    crab: CrabOptions,
28}
29
30impl Config {
31    // Generate and save default config
32    pub fn save_default_config() -> Result<()> {
33        let proj_dirs = ProjectDirs::from("", "", "tarts").ok_or_else(|| {
34            TartsError::Config(ConfigError::MissingField(
35                "Could not determine config directory".into(),
36            ))
37        })?;
38
39        // Create config directory if it doesn't exist
40        let config_dir = proj_dirs.config_dir();
41        std::fs::create_dir_all(config_dir)?;
42
43        let config_path = config_dir.join("tarts.toml");
44
45        // Create default config
46        let default_config = Config::default();
47
48        // Convert to TOML and save
49        let contents = toml::to_string(&default_config)
50            .map_err(|e| TartsError::Config(ConfigError::SerializeFormat(e)))?;
51        std::fs::write(config_path, contents)?;
52
53        Ok(())
54    }
55
56    // Modify your existing load method to create default if missing
57    #[allow(dead_code)]
58    pub fn load_old() -> Result<Self> {
59        let proj_dirs = ProjectDirs::from("", "", "tarts").ok_or_else(|| {
60            TartsError::Config(ConfigError::MissingField(
61                "Could not determine config directory".into(),
62            ))
63        })?;
64
65        let config_path = proj_dirs.config_dir().join("tarts.toml");
66
67        if !config_path.exists() {
68            // Create config directory if it doesn't exist
69            std::fs::create_dir_all(proj_dirs.config_dir())?;
70
71            // Create and save default config
72            let default_config = Config::default();
73            let contents = toml::to_string(&default_config)
74                .map_err(|e| TartsError::Config(ConfigError::SerializeFormat(e)))?;
75            std::fs::write(&config_path, contents)?;
76            return Ok(default_config);
77        }
78
79        let contents = std::fs::read_to_string(config_path)?;
80        toml::from_str(&contents)
81            .map_err(|e| TartsError::Config(ConfigError::DeserializeFormat(e)))
82    }
83
84    #[allow(dead_code)]
85    pub fn load() -> Result<Self> {
86        let proj_dirs = ProjectDirs::from("", "", "tarts").ok_or_else(|| {
87            eprintln!("Failed to get project directory");
88            TartsError::Config(ConfigError::MissingField(
89                "Could not determine config directory".into(),
90            ))
91        })?;
92
93        println!("Config dir: {:?}", proj_dirs.config_dir());
94        let config_path = proj_dirs.config_dir().join("tarts.toml");
95        println!("Config path: {:?}", config_path);
96
97        if !config_path.exists() {
98            println!("Config file doesn't exist, creating default");
99
100            // Create config directory if it doesn't exist
101            match std::fs::create_dir_all(proj_dirs.config_dir()) {
102                Ok(_) => println!("Created config directory"),
103                Err(e) => println!("Failed to create config directory: {}", e),
104            }
105
106            // Create default config using builders explicitly
107            let default_config = Config {
108                matrix: DigitalRainOptionsBuilder::default().build().unwrap(),
109                life: ConwayLifeOptionsBuilder::default().build().unwrap(),
110                maze: MazeOptionsBuilder::default().build().unwrap(),
111                boids: BoidsOptionsBuilder::default().build().unwrap(),
112                cube: CubeOptionsBuilder::default().build().unwrap(),
113                crab: CrabOptionsBuilder::default().build().unwrap(),
114            };
115
116            println!("Default cube options: {:?}", default_config.cube);
117
118            // Serialize and write
119            let contents = toml::to_string(&default_config).map_err(|e| {
120                println!("Failed to serialize config: {}", e);
121                TartsError::Config(ConfigError::SerializeFormat(e))
122            })?;
123
124            println!("TOML contents: {}", contents);
125
126            std::fs::write(&config_path, &contents)?;
127            println!("Wrote config file successfully");
128
129            return Ok(default_config);
130        }
131
132        println!("Config file exists, loading it");
133        let contents = std::fs::read_to_string(&config_path)?;
134        println!("Loaded contents: {}", contents);
135
136        let config = toml::from_str(&contents).map_err(|e| {
137            println!("Failed to parse config: {}", e);
138            TartsError::Config(ConfigError::DeserializeFormat(e))
139        })?;
140
141        println!("Parsed config successfully");
142        Ok(config)
143    }
144}
145
146// FIXIT: need to do something with this mess
147#[allow(unused)]
148impl Config {
149    // Add these methods
150    pub fn get_matrix_options(
151        &self,
152        _screen_size: (u16, u16),
153    ) -> DigitalRainOptions {
154        // If default is needed, create it, otherwise use stored config
155        self.matrix.clone()
156    }
157
158    pub fn get_life_options(&self, _screen_size: (u16, u16)) -> ConwayLifeOptions {
159        self.life.clone()
160    }
161
162    pub fn get_maze_options(&self, _screen_size: (u16, u16)) -> MazeOptions {
163        self.maze.clone()
164    }
165
166    pub fn get_boids_options(&self, screen_size: (u16, u16)) -> BoidsOptions {
167        let mut options = self.boids.clone();
168        options.screen_size = screen_size;
169        options
170    }
171
172    pub fn get_cube_options(&self) -> CubeOptions {
173        self.cube.clone()
174    }
175
176    pub fn get_crab_options(&self) -> CrabOptions {
177        self.crab.clone()
178    }
179}
180
181impl Default for Config {
182    fn default() -> Self {
183        Config {
184            matrix: DigitalRainOptionsBuilder::default().build().unwrap(),
185            life: ConwayLifeOptionsBuilder::default().build().unwrap(),
186            maze: MazeOptionsBuilder::default().build().unwrap(),
187            boids: BoidsOptionsBuilder::default().build().unwrap(),
188            cube: CubeOptionsBuilder::default().build().unwrap(),
189            crab: CrabOptionsBuilder::default().build().unwrap(),
190        }
191    }
192}