install_framework_base/
interface.rs

1// Copyright 2021 Yuri6037
2
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19// IN THE SOFTWARE.
20
21use 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}