pipi/generator/executer/
inmem.rs1use 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}