upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/config.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-09-01 00:00:00 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2023-09-13 09:24:49 +0000
commit6423baebd92e45c9be85157c443dff42e65d8d14 (patch)
tree6548edfd80d0cd9d1267378ebe816ec95e394137 /src/config.rs
parent5c5feaa732363e32e2a980a887fa42b4394b1a5e (diff)
refactor: rebuild app skeleton
Create skeleton for a complete rebuild of the prototype as a production ready product. Includes design patterns for: - dependency injection - unit testing with dependency mocking - integration testing - error handling - config storage BREAKING-CHANGE: ground-up redesign with incompatible protocol standards
Diffstat (limited to 'src/config.rs')
-rw-r--r--src/config.rs152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..b26dea0
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,152 @@
1use std::{fs::File, io::BufReader};
2
3use anyhow::{anyhow, Context, Result};
4use directories::ProjectDirs;
5#[cfg(test)]
6use mockall::*;
7use serde::{self, Deserialize, Serialize};
8
9#[derive(Default)]
10#[allow(clippy::module_name_repetitions)]
11pub struct ConfigManager;
12
13#[cfg_attr(test, automock)]
14#[allow(clippy::module_name_repetitions)]
15pub trait ConfigManagement {
16 fn load(&self) -> Result<MyConfig>;
17 fn save(&self, cfg: &MyConfig) -> Result<()>;
18}
19
20pub fn get_dirs() -> Result<ProjectDirs> {
21 ProjectDirs::from("", "CodeCollaboration", "ngit").ok_or(anyhow!(
22 "should find operating system home directories with rust-directories crate"
23 ))
24}
25
26impl ConfigManagement for ConfigManager {
27 fn load(&self) -> Result<MyConfig> {
28 let config_path = get_dirs()?.config_dir().join("config.json");
29 if config_path.exists() {
30 let file =
31 File::open(config_path).context("should open application configuration file")?;
32 let reader = BufReader::new(file);
33 let config: MyConfig = serde_json::from_reader(reader)
34 .context("should read config from config file with serde_json")?;
35 Ok(config)
36 } else {
37 Ok(MyConfig::default())
38 }
39 }
40 fn save(&self, cfg: &MyConfig) -> Result<()> {
41 let dirs = get_dirs()?;
42 let config_path = dirs.config_dir().join("config.json");
43 let file = if config_path.exists() {
44 std::fs::OpenOptions::new()
45 .create(true)
46 .write(true)
47 .truncate(true)
48 .open(config_path)
49 .context(
50 "should open application configuration file with write and truncate options",
51 )?
52 } else {
53 std::fs::create_dir_all(dirs.config_dir())
54 .context("should create application config directories")?;
55 std::fs::File::create(config_path).context("should create application config file")?
56 };
57 serde_json::to_writer_pretty(file, cfg)
58 .context("should write configuration to config file with serde_json")
59 }
60}
61
62#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
63#[allow(clippy::module_name_repetitions)]
64pub struct MyConfig {
65 pub version: u8,
66 pub users: Vec<UserRef>,
67}
68
69#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
70pub struct UserRef {
71 pub nsec: String,
72}
73
74#[cfg(test)]
75mod tests {
76 use anyhow::Result;
77 use serial_test::serial;
78 use test_utils::*;
79
80 use super::*;
81
82 mod load {
83 use super::*;
84
85 #[test]
86 #[serial]
87 fn when_config_file_doesnt_exist_defaults_are_returned() -> Result<()> {
88 with_fresh_config(|| {
89 assert_eq!(ConfigManager.load()?, MyConfig::default());
90
91 Ok(())
92 })
93 }
94
95 #[test]
96 #[serial]
97 fn when_config_file_exists_it_is_returned() -> Result<()> {
98 with_fresh_config(|| {
99 let c = ConfigManager;
100 let new_config = MyConfig {
101 version: 255,
102 ..MyConfig::default()
103 };
104 c.save(&new_config)?;
105 assert_eq!(c.load()?, new_config);
106
107 Ok(())
108 })
109 }
110 }
111
112 mod save {
113 use super::*;
114
115 #[test]
116 #[serial]
117 fn when_config_file_doesnt_config_is_saved() -> Result<()> {
118 with_fresh_config(|| {
119 let c = ConfigManager;
120 let new_config = MyConfig {
121 version: 255,
122 ..MyConfig::default()
123 };
124 c.save(&new_config)?;
125 assert_eq!(c.load()?, new_config);
126
127 Ok(())
128 })
129 }
130
131 #[test]
132 #[serial]
133 fn when_config_file_exists_new_config_is_saved() -> Result<()> {
134 with_fresh_config(|| {
135 let c = ConfigManager;
136 let config = MyConfig {
137 version: 255,
138 ..MyConfig::default()
139 };
140 c.save(&config)?;
141 let new_config = MyConfig {
142 version: 254,
143 ..MyConfig::default()
144 };
145 c.save(&new_config)?;
146 assert_eq!(c.load()?, new_config);
147
148 Ok(())
149 })
150 }
151 }
152}