use std::vec::Vec;
use std::path::Path;
use std::path::PathBuf;
use std::collections::HashMap;
use std::string::String;
use std::io;
use install_framework_core::command::Interpreter;
use install_framework_core::command::InstallCommand;
use install_framework_core::interface::InstallMethod;
use super::base;
use crate::cache::Cache;
use crate::error::Error;
use crate::interface::SimpleInterpreter;
use crate::platform;
fn auto_remove(path: &Path) -> io::Result<()>
{
if path.exists()
{
if path.is_dir()
{
return std::fs::remove_dir_all(path);
}
else
{
return std::fs::remove_file(path);
}
}
return Ok(());
}
fn is_empty(path: &Path) -> bool
{
if let Ok(mut v) = path.read_dir()
{
return v.next().is_none();
}
return true;
}
pub struct UninstallInterpreter<'a, TError: Error>
{
interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>,
cache: &'a mut Cache<TError>,
props: &'a HashMap<String, String>,
content_dir: &'a Path,
bin_dir: &'a Path,
delete_dirs: Vec<PathBuf>,
#[cfg(target_os = "linux")]
install_dir: &'a Path,
#[cfg(not(target_os = "macos"))]
method: InstallMethod
}
impl <'a, TError: Error> UninstallInterpreter<'a, TError>
{
#[cfg(target_os = "linux")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, install_dir: &'a Path, method: InstallMethod, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>) -> UninstallInterpreter<'a, TError>
{
return UninstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
content_dir: content_dir,
bin_dir: bin_dir,
delete_dirs: Vec::new(),
install_dir: install_dir,
method: method
}
}
#[cfg(target_os = "windows")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, method: InstallMethod, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>) -> UninstallInterpreter<'a, TError>
{
return UninstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
content_dir: content_dir,
bin_dir: bin_dir,
delete_dirs: Vec::new(),
method: method
}
}
#[cfg(target_os = "macos")]
pub fn new(interpreter: &'a mut dyn SimpleInterpreter<TError::ErrorType>, cache: &'a mut Cache<TError>, content_dir: &'a Path, bin_dir: &'a Path, props: &'a HashMap<String, String>) -> UninstallInterpreter<'a, TError>
{
return UninstallInterpreter
{
interpreter: interpreter,
cache: cache,
props: props,
content_dir: content_dir,
bin_dir: bin_dir,
delete_dirs: Vec::new()
}
}
fn download_file(&mut self, resid: usize, filename: String) -> Result<(), TError::ErrorType>
{
let outfile = self.cache.get_path(Path::new(&filename))?;
self.cache.insert(resid, outfile);
return Ok(());
}
fn unpack_cached(&mut self, resid: usize, path: String) -> Result<(), TError::ErrorType>
{
if !path.ends_with(".zip")
{
return Err(TError::generic(String::from("Only ZIP archives are supported")));
}
let computed1 = self.cache.parse_string(&path[..path.len() - 4])?;
let outpath = self.cache.get_path(Path::new(&computed1))?;
self.cache.insert(resid, outpath);
return Ok(());
}
fn extract_resource(&mut self, resid: usize, path: &'static str) -> Result<(), TError::ErrorType>
{
let name =
{
if let Some(id) = path.rfind('/')
{
&path[id + 1..]
}
else
{
path
}
};
let outpath = self.cache.get_path(Path::new(name))?;
self.cache.insert(resid, outpath);
return Ok(());
}
fn user_input(&mut self, resid: usize, prop: String, message: String) -> Result<(), TError::ErrorType>
{
return base::user_input(self.interpreter, &mut self.cache, &self.props, resid, prop, message);
}
fn create_folder(&mut self, resid: usize, name: String) -> Result<(), TError::ErrorType>
{
let dir = self.content_dir.join(name);
if dir.exists()
{
if is_empty(&dir)
{
if let Err(e) = std::fs::remove_dir_all(&dir)
{
return Err(TError::io(e));
}
}
else
{
self.delete_dirs.push(dir.clone());
}
}
self.cache.insert(resid, dir);
return Ok(());
}
fn install_cached(&mut self, path: String, folder: usize) -> Result<(), TError::ErrorType>
{
let src = PathBuf::from(self.cache.parse_string(&path)?);
let dst;
let dstf;
if folder == 0
{
dst = self.content_dir;
}
else
{
dst = Path::new(self.cache.get(folder)?);
}
if let Some(name) = src.file_name()
{
dstf = dst.join(name);
}
else
{
dstf = dst.join(&src);
}
if let Err(e) = auto_remove(&dstf)
{
return Err(TError::io(e));
}
let mut drained = Vec::new();
self.delete_dirs.retain(|v|
{
if is_empty(&v)
{
drained.push(v.clone());
return false;
}
return true;
});
for v in drained
{
if let Err(e) = auto_remove(&v)
{
return Err(TError::io(e));
}
}
return Ok(());
}
fn install_resource(&mut self, path: &'static str, folder: usize) -> Result<(), TError::ErrorType>
{
let dst;
if folder == 0
{
dst = self.content_dir;
let name =
{
if let Some(id) = path.rfind('/')
{
&path[id + 1..]
}
else
{
path
}
};
let dstf = dst.join(name);
if dstf.exists()
{
if let Err(e) = std::fs::remove_file(dstf)
{
return Err(TError::io(e));
}
}
}
return Ok(());
}
fn add_to_path(&mut self, path: String) -> Result<(), TError::ErrorType>
{
let src = self.content_dir.join(path);
let name =
{
if let Some(n) = src.file_name()
{
n
}
else
{
src.as_os_str()
}
};
let dst = self.bin_dir.join(name);
if dst.exists()
{
#[cfg(unix)]
{
if let Err(e) = std::fs::remove_file(dst)
{
return Err(TError::io(e));
}
}
#[cfg(windows)]
{
use std::ffi::OsString;
let mut dst1 = OsString::from(dst.as_os_str());
dst1.push(".bat");
if let Err(e) = std::fs::remove_file(dst1)
{
return Err(TError::io(e));
}
}
}
return Ok(());
}
fn add_shortcut(&mut self, path: String) -> Result<(), TError::ErrorType>
{
let file;
#[cfg(target_os="linux")]
{
let mut name = platform::common::get_target_desktop_name(&path);
if path.ends_with(".AppImage")
{
let mut copy = name.clone();
copy.push_str(".icon");
let icon = self.content_dir.join(copy);
if icon.exists()
{
if let Err(e) = std::fs::remove_file(icon)
{
return Err(TError::io(e));
}
}
}
let dir = platform::common::get_desktop_folder(self.install_dir, self.method);
name.push_str(".desktop");
file = dir.join(name);
}
#[cfg(windows)]
{
let dir = platform::common::get_desktop_folder(self.method);
let mut name = platform::common::get_target_desktop_name(&path);
name.push_str(".lnk");
file = dir.join(name);
}
if file.exists()
{
if let Err(e) = std::fs::remove_file(file)
{
return Err(TError::io(e));
}
}
return Ok(());
}
}
impl <TError: Error> Interpreter<InstallCommand> for UninstallInterpreter<'_, TError>
{
type ErrorType = TError::ErrorType;
fn execute(&mut self, resid: usize, cmd: InstallCommand) -> Result<(), TError::ErrorType>
{
match cmd
{
InstallCommand::DownloadFile(f, _, _) => self.download_file(resid, f)?,
InstallCommand::UnpackCached(p) => self.unpack_cached(resid, p)?,
InstallCommand::ExtractResource(p) => self.extract_resource(resid, p)?,
InstallCommand::UserInput(p, m) => self.user_input(resid, p, m)?,
InstallCommand::AddToPath(p) => self.add_to_path(p)?,
InstallCommand::CreateFolder(n) => self.create_folder(resid, n)?,
InstallCommand::InstallCached(p, f) => self.install_cached(p, f)?,
InstallCommand::InstallResource(p, f) => self.install_resource(p, f)?,
InstallCommand::AddShortcut(p) => self.add_shortcut(p)?
}
return Ok(());
}
fn progress(&mut self, cur: usize, max: usize) -> Result<(), Self::ErrorType>
{
self.interpreter.update_step((cur as f64 / max as f64) as f32)?;
return Ok(());
}
}