1use std::path::Path;
22use std::path::PathBuf;
23use std::string::String;
24use std::collections::HashMap;
25use std::io;
26use std::io::Write;
27use std::fs::File;
28use install_framework_core::command::CommandQueue;
29use install_framework_core::interface::Installer;
30use install_framework_core::interface::Component;
31use install_framework_core::interface::PostInstall;
32use install_framework_core::interface::PostUninstall;
33use install_framework_core::interface::InstallMethod;
34
35use crate::interpreter::InitInterpreter;
36use crate::interpreter::StatusInterpreter;
37use crate::interpreter::InstallInterpreter;
38use crate::interpreter::UninstallInterpreter;
39use crate::error::Error;
40use crate::cache::Cache;
41
42pub trait SimpleInterpreter<ErrorType>
43{
44 fn read_user_input_text(&mut self, message: &str) -> Result<String, ErrorType>;
45 fn begin_step(&mut self, message: &str) -> Result<(), ErrorType>;
46 fn update_step(&mut self, percent: f32) -> Result<(), ErrorType>;
47 fn end_step(&mut self) -> Result<(), ErrorType>;
48 fn begin_substep(&mut self, message: &str) -> Result<(), ErrorType>;
49 fn update_substep(&mut self, percent: f32) -> Result<(), ErrorType>;
50 fn end_substep(&mut self) -> Result<(), ErrorType>;
51}
52
53fn install_dir_is_empty(install_dir: &Path) -> bool
54{
55 let files = match std::fs::read_dir(install_dir)
56 {
57 Ok(v) => v,
58 Err(_) => return false
59 };
60 for f in files
61 {
62 let ff = match f
63 {
64 Ok(v) => v,
65 Err(_) => return false
66 };
67 let useless = ff.file_name();
68 let name = useless.to_string_lossy();
69 let t = match ff.file_type()
70 {
71 Ok(v) => v,
72 Err(_) => return false
73 };
74 if name != "manifest.txt" && !t.is_dir()
75 {
76 return false;
77 }
78 if t.is_dir() && !install_dir_is_empty(&ff.path())
79 {
80 return false;
81 }
82 }
83 return true
84}
85
86#[derive(Clone)]
87pub struct InstallationState
88{
89 pub manifest: String,
90 pub installed_components: Vec<Component>,
91 pub non_installed_components: Vec<Component>
92}
93
94pub struct BaseInterface<TInterpreter: SimpleInterpreter<TError::ErrorType>, TError: Error>
95{
96 interpreter: TInterpreter,
97 cache: Cache<TError>,
98 installed: bool,
99 props: HashMap<String, String>,
100 name: &'static str,
101 version: &'static str,
102 author: &'static str,
103 last_res_id: usize
104}
105
106impl <TInterpreter: SimpleInterpreter<TError::ErrorType>, TError: Error> BaseInterface<TInterpreter, TError>
107{
108 pub fn new(interpreter: TInterpreter) -> BaseInterface<TInterpreter, TError>
109 {
110 return BaseInterface
111 {
112 interpreter: interpreter,
113 cache: Cache::new(),
114 installed: false,
115 props: HashMap::new(),
116 name: "Unknown",
117 version: "Unknown",
118 author: "Unknown",
119 last_res_id: 0
120 }
121 }
122
123 pub fn set_prop(&mut self, key: String, value: String)
124 {
125 self.props.insert(key, value);
126 }
127
128 pub fn set_static_info(&mut self, name: &'static str, version: &'static str, author: &'static str)
129 {
130 self.name = name;
131 self.version = version;
132 self.author = author;
133 }
134
135 pub fn get_components(&mut self, installer: &mut dyn Installer, resources: &HashMap<&'static str, &'static [u8]>) -> Result<Vec<Component>, TError::ErrorType>
136 {
137 self.interpreter.begin_step("Initializing...")?;
138 let mut commands = CommandQueue::new(self.last_res_id);
139 installer.init(&mut commands);
140 self.last_res_id = commands.get_last_resource_id();
141 let mut interpreter = InitInterpreter::new(&mut self.interpreter, &mut self.cache, &self.props, resources);
142 commands.run(&mut interpreter)?;
143 let path = &self.cache.get_path(Path::new("."))?;
144 let v = installer.get_components(path);
145 self.interpreter.end_step()?;
146 return Ok(v);
147 }
148
149 pub fn run_post_uninstall(&mut self, post: &mut dyn PostUninstall, install_dir: &Path) -> Result<(), TError::ErrorType>
150 {
151 let (bin, content) = self.get_target_dirs(install_dir);
152 let cache = self.cache.get_path(Path::new("."))?;
153 post.post_uninstall(&cache, &content, &bin);
154 return Ok(());
155 }
156
157 pub fn run_post_install(&mut self, post: &mut dyn PostInstall, install_dir: &Path) -> Result<(), TError::ErrorType>
158 {
159 let (bin, content) = self.get_target_dirs(install_dir);
160 let cache = self.cache.get_path(Path::new("."))?;
161 post.post_install(&cache, &content, &bin);
162 return Ok(());
163 }
164
165 fn setup_install(&self, content_dir: &Path, bin_dir: &Path) -> io::Result<()>
166 {
167 if !content_dir.exists()
168 {
169 std::fs::create_dir(&content_dir)?;
170 }
171 if !bin_dir.exists()
172 {
173 std::fs::create_dir(&bin_dir)?;
174 }
175 let manifest = content_dir.join("manifest.txt");
176 let mut f = File::create(manifest)?;
177 writeln!(f, "Installer Name: {}", self.name)?;
178 writeln!(f, "Installer Version: {}", self.version)?;
179 writeln!(f, "Installer Author: {}", self.author)?;
180 return Ok(());
181 }
182
183 pub fn get_target_dirs(&self, install_dir: &Path) -> (PathBuf, PathBuf)
184 {
185 #[cfg(windows)]
186 let bin = install_dir.join(self.name).join("bin");
187 #[cfg(unix)]
188 let bin = install_dir.join("bin");
189 let content = install_dir.join(self.name);
190 return (bin, content);
191 }
192
193 pub fn get_installation_state(&mut self, components: Vec<Component>, installer: &mut dyn Installer, install_dir: &Path) -> Result<Option<InstallationState>, TError::ErrorType>
194 {
195 let (bin, content) = self.get_target_dirs(install_dir);
196 let manifest = content.join("manifest.txt");
197 let manifest_data;
198 match std::fs::read_to_string(manifest)
199 {
200 Ok(v) => manifest_data = v,
201 Err(_) => return Ok(None)
202 }
203 let mut installed_components = Vec::new();
204 let mut non_installed_components = Vec::new();
205 for c in components
206 {
207 let mut interpreter: StatusInterpreter<TError> = StatusInterpreter::new(&content, &bin, &mut self.cache);
208 let mut commands = CommandQueue::new(self.last_res_id);
209 installer.install_component(&c.name, &mut commands);
210 self.last_res_id = commands.get_last_resource_id();
211 commands.run(&mut interpreter)?;
212 if interpreter.is_installed()
213 {
214 installed_components.push(c);
215 }
216 else
217 {
218 non_installed_components.push(c);
219 }
220 }
221 return Ok(Some(InstallationState
222 {
223 manifest: manifest_data,
224 installed_components: installed_components,
225 non_installed_components: non_installed_components
226 }));
227 }
228
229 pub fn install_component(&mut self, component: &Component, installer: &mut dyn Installer, install_dir: &Path, method: InstallMethod, resources: &HashMap<&'static str, &'static [u8]>) -> Result<(), TError::ErrorType>
230 {
231 self.interpreter.begin_step(&format!("Installing component {}...", component.name))?;
232 let (bin, content) = self.get_target_dirs(install_dir);
233 if !self.installed
234 {
235 if let Err(e) = self.setup_install(&content, &bin)
236 {
237 return Err(TError::io(e));
238 }
239 self.installed = true;
240 }
241 #[cfg(target_os = "linux")]
242 let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, install_dir, method, &content, &bin, &self.props, resources);
243 #[cfg(target_os = "windows")]
244 let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, method, &content, &bin, &self.props, resources);
245 #[cfg(target_os = "macos")]
246 let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, &content, &bin, &self.props, resources);
247 let mut commands = CommandQueue::new(self.last_res_id);
248 installer.install_component(&component.name, &mut commands);
249 self.last_res_id = commands.get_last_resource_id();
250 commands.run(&mut interpreter)?;
251 self.interpreter.end_step()?;
252 return Ok(());
253 }
254
255 pub fn uninstall_component(&mut self, component: &Component, installer: &mut dyn Installer, install_dir: &Path, method: InstallMethod) -> Result<(), TError::ErrorType>
256 {
257 self.interpreter.begin_step(&format!("Uninstalling component {}...", component.name))?;
258 let (bin, content) = self.get_target_dirs(install_dir);
259 #[cfg(target_os = "linux")]
260 let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, install_dir, method, &content, &bin, &self.props);
261 #[cfg(target_os = "windows")]
262 let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, method, &content, &bin, &self.props);
263 #[cfg(target_os = "macos")]
264 let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, &content, &bin, &self.props);
265 let mut commands = CommandQueue::new(self.last_res_id);
266 installer.install_component(&component.name, &mut commands);
267 self.last_res_id = commands.get_last_resource_id();
268 commands.run(&mut interpreter)?;
269 if install_dir_is_empty(&content)
270 {
271 if let Err(e) = std::fs::remove_dir_all(content)
272 {
273 return Err(TError::io(e));
274 }
275 #[cfg(windows)]
276 {
277 use crate::platform;
278 platform::winpath::remove_path::<TError>(&bin, method)?;
279 }
280 }
281 self.interpreter.end_step()?;
282 return Ok(());
283 }
284}