1mod error;
2
3pub use error::Error as ConfigError;
4use error::{Error, Result};
5use serde::{Deserialize, Serialize};
6use std::{
7 fs,
8 path::{Path, PathBuf},
9};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Config {
14 pub db_url: String,
16
17 pub namespace: String,
19
20 pub database: String,
22
23 pub migrations_dir: PathBuf,
25}
26
27impl Config {
28 pub fn builder() -> ConfigBuilder {
30 ConfigBuilder::default()
31 }
32
33 pub fn from_file(file: &Path) -> Result<Self> {
34 let contents = fs::read_to_string(file).map_err(|_| Error::FileUnreadable)?;
35 let parsed: toml::Value =
36 toml::from_str(&contents).map_err(|_| Error::FileFormatUnrecognized)?;
37 let conf = parsed.get("sir-eel").ok_or(Error::FileFormatUnrecognized)?;
38 let db_url = conf
39 .get("db_url")
40 .and_then(|v| v.as_str())
41 .ok_or(Error::MissingProperty("Missing 'db_url'".to_string()))?
42 .to_string();
43 let database = conf
44 .get("database")
45 .and_then(|v| v.as_str())
46 .ok_or(Error::MissingProperty("Missing 'database'".to_string()))?
47 .to_string();
48 let namespace = conf
49 .get("namespace")
50 .and_then(|v| v.as_str())
51 .ok_or(Error::MissingProperty("Missing 'namespace'".to_string()))?
52 .to_string();
53 let migrations_dir = conf
54 .get("migrations_dir")
55 .and_then(|v| v.as_str())
56 .map(|dir| PathBuf::from(dir.to_string()))
57 .ok_or(Error::MissingProperty(
58 "Missing 'migrations_dir'".to_string(),
59 ))?;
60 Ok(Self {
61 db_url,
62 database,
63 namespace,
64 migrations_dir,
65 })
66 }
67}
68
69#[derive(Default)]
71pub struct ConfigBuilder {
72 db_url: Option<String>,
73 namespace: Option<String>,
74 database: Option<String>,
75 migrations_dir: Option<PathBuf>,
76}
77
78impl ConfigBuilder {
79 pub fn db_url(mut self, url: impl Into<String>) -> Self {
81 self.db_url = Some(url.into());
82 self
83 }
84
85 pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
87 self.namespace = Some(namespace.into());
88 self
89 }
90
91 pub fn database(mut self, database: impl Into<String>) -> Self {
93 self.database = Some(database.into());
94 self
95 }
96
97 pub fn migration_dir(mut self, dir: impl Into<PathBuf>) -> Self {
99 self.migrations_dir = Some(dir.into());
100 self
101 }
102
103 pub fn build(self) -> Result<Config> {
105 Ok(Config {
106 db_url: self
107 .db_url
108 .ok_or(Error::MissingProperty("Missing 'db_url'".into()))?,
109 namespace: self
110 .namespace
111 .ok_or(Error::MissingProperty("Missing 'namespace'".into()))?,
112 database: self
113 .database
114 .ok_or(Error::MissingProperty("Missing 'database'".into()))?,
115 migrations_dir: self.migrations_dir.unwrap_or(PathBuf::from("migrations")),
116 })
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_builder_ok() -> Result<()> {
126 let config = Config::builder()
127 .db_url("rocksdb://test")
128 .database("testdb")
129 .namespace("testns")
130 .build()?;
131
132 assert_eq!(config.db_url, "rocksdb://test");
133
134 Ok(())
135 }
136
137 #[test]
138 fn test_builder_err_missing_db_url() -> Result<()> {
139 let res = Config::builder()
140 .database("testdb")
141 .namespace("testns")
142 .build();
143
144 assert!(matches!(
145 res,
146 Err(Error::MissingProperty(msg)) if msg == "Missing 'db_url'"
147 ));
148 Ok(())
149 }
150
151 #[test]
152 fn test_builder_err_missing_database() -> Result<()> {
153 let res = Config::builder()
154 .db_url("rocksdb://testdata")
155 .namespace("testns")
156 .build();
157
158 assert!(matches!(
159 res,
160 Err(Error::MissingProperty(msg)) if msg == "Missing 'database'"
161 ));
162 Ok(())
163 }
164
165 #[test]
166 fn test_builder_err_missing_namespace() -> Result<()> {
167 let res = Config::builder()
168 .db_url("rocksdb://testdata")
169 .database("testdb")
170 .build();
171
172 assert!(matches!(
173 res,
174 Err(Error::MissingProperty(msg)) if msg == "Missing 'namespace'"
175 ));
176 Ok(())
177 }
178
179 #[test]
180 fn test_from_file_ok() -> Result<()> {
181 let filepath = Path::new("sir-eel.test.toml");
182 let config = Config::from_file(filepath)?;
183
184 assert_eq!(config.db_url, "rocksdb://testdata");
185 assert_eq!(config.database, "testdatabase");
186 assert_eq!(config.namespace, "testnamespace");
187 assert_eq!(config.migrations_dir, PathBuf::from("migrations"));
188
189 Ok(())
190 }
191}