Skip to main content

pipi/generator/executer/
inmem.rs

1use std::{
2    collections::BTreeMap,
3    path::{Path, PathBuf},
4    sync::Mutex,
5};
6
7use super::Executer;
8use crate::{generator, settings::Settings};
9
10pub struct Inmem {
11    pub source_path: PathBuf,
12    pub file_store: Mutex<BTreeMap<PathBuf, String>>,
13    pub template_engine: generator::template::Template,
14}
15
16impl Inmem {
17    #[must_use]
18    pub fn new(source: &Path) -> Self {
19        Self::with_template_engine(source, generator::template::Template::default())
20    }
21
22    #[must_use]
23    pub fn with_template_engine(
24        source: &Path,
25        template_engine: generator::template::Template,
26    ) -> Self {
27        Self {
28            source_path: source.to_path_buf(),
29            file_store: Mutex::new(BTreeMap::default()),
30            template_engine,
31        }
32    }
33
34    pub fn get_file_content(&self, path: &Path) -> Option<String> {
35        self.file_store
36            .lock()
37            .ok()
38            .and_then(|store| store.get(path).cloned())
39    }
40}
41
42impl Executer for Inmem {
43    fn copy_file(&self, file_path: &Path) -> super::Result<PathBuf> {
44        let file_content = fs_extra::file::read_to_string(self.source_path.join(file_path))?;
45        self.file_store
46            .lock()
47            .unwrap()
48            .insert(file_path.to_path_buf(), file_content);
49        Ok(file_path.to_path_buf())
50    }
51
52    fn create_file(&self, path: &Path, content: String) -> super::Result<PathBuf> {
53        self.file_store
54            .lock()
55            .unwrap()
56            .insert(path.to_path_buf(), content);
57        Ok(path.to_path_buf())
58    }
59
60    fn copy_dir(&self, directory_path: &Path) -> super::Result<()> {
61        let directory_content = fs_extra::dir::get_dir_content(directory_path)?;
62        for file in directory_content.files {
63            let mut store = self.file_store.lock().unwrap();
64            store.insert(PathBuf::from(&file), fs_extra::file::read_to_string(file)?);
65        }
66        Ok(())
67    }
68
69    fn copy_template(&self, file_path: &Path, settings: &Settings) -> super::Result<()> {
70        let copied_path = self.copy_file(file_path)?;
71
72        if self.template_engine.is_template(&copied_path) {
73            let template_content = {
74                let store = self.file_store.lock().unwrap();
75                store.get(&copied_path).cloned()
76            };
77
78            if let Some(content) = template_content {
79                let rendered_content = self.template_engine.render(&content, settings)?;
80                self.file_store
81                    .lock()
82                    .unwrap()
83                    .insert(file_path.to_path_buf(), rendered_content);
84                Ok(())
85            } else {
86                Err(super::Error::msg("Template content not found"))
87            }
88        } else {
89            Err(super::Error::msg("File is not a template"))
90        }
91    }
92
93    fn copy_template_dir(&self, _path: &Path, _data: &Settings) -> super::Result<()> {
94        Ok(())
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use tree_fs::{Tree, TreeBuilder};
101
102    use super::*;
103
104    fn init_in_memory_store() -> (Inmem, Tree) {
105        let tree = TreeBuilder::default()
106            .drop(true)
107            .add("test/foo.txt", "bar")
108            .add("test/bar.txt.t", "crate: {{settings.package_name}}")
109            .create()
110            .expect("Failed to create mock data");
111        (Inmem::new(&tree.root), tree)
112    }
113
114    #[test]
115    fn can_copy_file() {
116        let (store, source_dir) = init_in_memory_store();
117        let test_file_path = source_dir.root.join("test").join("foo.txt");
118
119        let copied_path = store.copy_file(&test_file_path).unwrap();
120
121        assert_eq!(copied_path, test_file_path);
122        assert_eq!(
123            store
124                .file_store
125                .lock()
126                .unwrap()
127                .get(&test_file_path)
128                .unwrap(),
129            "bar"
130        );
131    }
132
133    #[test]
134    fn test_copy_directory() {
135        let (store, source_dir) = init_in_memory_store();
136        let dir_path = source_dir.root.join("test");
137
138        store.copy_dir(&dir_path).unwrap();
139
140        let file1_path = dir_path.join("foo.txt");
141        let file2_path = dir_path.join("bar.txt.t");
142
143        assert_eq!(
144            store.file_store.lock().unwrap().get(&file1_path).unwrap(),
145            "bar"
146        );
147        assert_eq!(
148            store.file_store.lock().unwrap().get(&file2_path).unwrap(),
149            "crate: {{settings.package_name}}"
150        );
151    }
152
153    #[test]
154    fn can_copy_template_file() {
155        let (store, source_dir) = init_in_memory_store();
156        let test_file_path = source_dir.root.join("test").join("bar.txt.t");
157
158        let settings = Settings {
159            package_name: "pipi-app".to_string(),
160            ..Default::default()
161        };
162
163        store
164            .copy_template(&test_file_path, &settings)
165            .expect("copy template");
166
167        assert_eq!(
168            store
169                .file_store
170                .lock()
171                .unwrap()
172                .get(&test_file_path)
173                .unwrap(),
174            "crate: pipi-app"
175        );
176    }
177}