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 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 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 let default_config = Config::default();
47
48 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 #[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 std::fs::create_dir_all(proj_dirs.config_dir())?;
70
71 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 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 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 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#[allow(unused)]
148impl Config {
149 pub fn get_matrix_options(
151 &self,
152 _screen_size: (u16, u16),
153 ) -> DigitalRainOptions {
154 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}