spinne_core/
config.rs

1use std::{fs, path::PathBuf};
2
3use serde_json::Value;
4use spinne_logger::Logger;
5
6#[derive(Debug, PartialEq, Clone)]
7pub struct ConfigValues {
8    pub exclude: Option<Vec<String>>,
9    pub include: Option<Vec<String>>,
10    pub entry_points: Option<Vec<String>>,
11}
12
13/// Represents the config file
14///
15/// The config file is a JSON file that contains the configuration for the project.
16/// If the config file is not found, the default values will be used.
17pub struct Config {
18    pub path: PathBuf,
19}
20
21impl Config {
22    /// Reads the config file and returns values
23    pub fn read(path: PathBuf) -> Option<ConfigValues> {
24        let config = fs::read_to_string(path);
25
26        if config.is_err() {
27            Logger::error("Failed to read config file");
28            return None;
29        }
30
31        match serde_json::from_str::<Value>(&config.unwrap()) {
32            Ok(value) => {
33                let exclude_value = value.get("exclude");
34                let include_value = value.get("include");
35                let entry_points_value = value.get("entry_points");
36
37                let exclude = match exclude_value {
38                    Some(value) => Some(Self::get_array_of_strings(value)),
39                    None => None,
40                };
41
42                let include = match include_value {
43                    Some(value) => Some(Self::get_array_of_strings(value)),
44                    None => None,
45                };
46
47                let entry_points = match entry_points_value {
48                    Some(value) => Some(Self::get_array_of_strings(value)),
49                    None => None,
50                };
51
52                Some(ConfigValues {
53                    exclude,
54                    include,
55                    entry_points,
56                })
57            }
58            Err(err) => {
59                Logger::error("Failed to parse config file");
60                Logger::error(&err.to_string());
61                return None;
62            }
63        }
64    }
65
66    /// Maps a Value to an array of strings
67    fn get_array_of_strings(value: &Value) -> Vec<String> {
68        value
69            .as_array()
70            .unwrap_or(&vec![])
71            .iter()
72            .map(|v| v.as_str().unwrap_or("").to_string())
73            .collect::<Vec<String>>()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use crate::util::test_utils::create_mock_project;
80
81    use super::*;
82
83    #[test]
84    fn test_config_read() {
85        let temp_dir = create_mock_project(&vec![(
86            "spinne.json",
87            r#"{"exclude": ["test.tsx"], "include": ["test.tsx"], "entry_points": ["src/index.tsx"]}"#,
88        )]);
89        let config = Config::read(temp_dir.path().join("spinne.json"));
90
91        assert_eq!(
92            config,
93            Some(ConfigValues {
94                exclude: Some(vec!["test.tsx".to_string()]),
95                include: Some(vec!["test.tsx".to_string()]),
96                entry_points: Some(vec!["src/index.tsx".to_string()])
97            })
98        );
99    }
100
101    #[test]
102    fn test_config_read_no_config() {
103        let temp_dir = create_mock_project(&vec![]);
104        let config = Config::read(temp_dir.path().join("spinne.json"));
105
106        assert_eq!(config, None);
107    }
108
109    #[test]
110    fn test_config_read_invalid_config() {
111        let temp_dir = create_mock_project(&vec![("spinne.json", r#"{"]ht["te)}"#)]);
112        let config = Config::read(temp_dir.path().join("spinne.json"));
113
114        assert_eq!(config, None);
115    }
116
117    #[test]
118    fn test_config_without_include() {
119        let temp_dir = create_mock_project(&vec![("spinne.json", r#"{"exclude": ["test.tsx"]}"#)]);
120        let config = Config::read(temp_dir.path().join("spinne.json"));
121
122        assert_eq!(
123            config,
124            Some(ConfigValues {
125                exclude: Some(vec!["test.tsx".to_string()]),
126                include: None,
127                entry_points: None
128            })
129        );
130    }
131
132    #[test]
133    fn test_config_without_exclude() {
134        let temp_dir = create_mock_project(&vec![("spinne.json", r#"{"include": ["test.tsx"]}"#)]);
135        let config = Config::read(temp_dir.path().join("spinne.json"));
136
137        assert_eq!(
138            config,
139            Some(ConfigValues {
140                exclude: None,
141                include: Some(vec!["test.tsx".to_string()]),
142                entry_points: None
143            })
144        );
145    }
146
147    #[test]
148    fn test_config_without_entry_points() {
149        let temp_dir = create_mock_project(&vec![("spinne.json", r#"{"include": ["test.tsx"]}"#)]);
150        let config = Config::read(temp_dir.path().join("spinne.json"));
151
152        assert_eq!(
153            config,
154            Some(ConfigValues {
155                exclude: None,
156                include: Some(vec!["test.tsx".to_string()]),
157                entry_points: None
158            })
159        );
160    }
161
162    #[test]
163    fn test_config_with_only_entry_points() {
164        let temp_dir = create_mock_project(&vec![(
165            "spinne.json",
166            r#"{"entry_points": ["src/index.tsx", "src/components/index.ts"]}"#,
167        )]);
168        let config = Config::read(temp_dir.path().join("spinne.json"));
169
170        assert_eq!(
171            config,
172            Some(ConfigValues {
173                exclude: None,
174                include: None,
175                entry_points: Some(vec![
176                    "src/index.tsx".to_string(),
177                    "src/components/index.ts".to_string()
178                ])
179            })
180        );
181    }
182}