use std::path::Path;
use std::path::PathBuf;
use std::string::String;
use std::collections::HashMap;
use std::io;
use std::io::Write;
use std::fs::File;
use install_framework_core::command::CommandQueue;
use install_framework_core::interface::Installer;
use install_framework_core::interface::Component;
use install_framework_core::interface::PostInstall;
use install_framework_core::interface::PostUninstall;
use install_framework_core::interface::InstallMethod;
use crate::interpreter::InitInterpreter;
use crate::interpreter::StatusInterpreter;
use crate::interpreter::InstallInterpreter;
use crate::interpreter::UninstallInterpreter;
use crate::error::Error;
use crate::cache::Cache;
pub trait SimpleInterpreter<ErrorType>
{
fn read_user_input_text(&mut self, message: &str) -> Result<String, ErrorType>;
fn begin_step(&mut self, message: &str) -> Result<(), ErrorType>;
fn update_step(&mut self, percent: f32) -> Result<(), ErrorType>;
fn end_step(&mut self) -> Result<(), ErrorType>;
fn begin_substep(&mut self, message: &str) -> Result<(), ErrorType>;
fn update_substep(&mut self, percent: f32) -> Result<(), ErrorType>;
fn end_substep(&mut self) -> Result<(), ErrorType>;
}
fn install_dir_is_empty(install_dir: &Path) -> bool
{
let files = match std::fs::read_dir(install_dir)
{
Ok(v) => v,
Err(_) => return false
};
for f in files
{
let ff = match f
{
Ok(v) => v,
Err(_) => return false
};
let useless = ff.file_name();
let name = useless.to_string_lossy();
let t = match ff.file_type()
{
Ok(v) => v,
Err(_) => return false
};
if name != "manifest.txt" && !t.is_dir()
{
return false;
}
if t.is_dir() && !install_dir_is_empty(&ff.path())
{
return false;
}
}
return true
}
#[derive(Clone)]
pub struct InstallationState
{
pub manifest: String,
pub installed_components: Vec<Component>,
pub non_installed_components: Vec<Component>
}
pub struct BaseInterface<TInterpreter: SimpleInterpreter<TError::ErrorType>, TError: Error>
{
interpreter: TInterpreter,
cache: Cache<TError>,
installed: bool,
props: HashMap<String, String>,
name: &'static str,
version: &'static str,
author: &'static str,
last_res_id: usize
}
impl <TInterpreter: SimpleInterpreter<TError::ErrorType>, TError: Error> BaseInterface<TInterpreter, TError>
{
pub fn new(interpreter: TInterpreter) -> BaseInterface<TInterpreter, TError>
{
return BaseInterface
{
interpreter: interpreter,
cache: Cache::new(),
installed: false,
props: HashMap::new(),
name: "Unknown",
version: "Unknown",
author: "Unknown",
last_res_id: 0
}
}
pub fn set_prop(&mut self, key: String, value: String)
{
self.props.insert(key, value);
}
pub fn set_static_info(&mut self, name: &'static str, version: &'static str, author: &'static str)
{
self.name = name;
self.version = version;
self.author = author;
}
pub fn get_components(&mut self, installer: &mut dyn Installer, resources: &HashMap<&'static str, &'static [u8]>) -> Result<Vec<Component>, TError::ErrorType>
{
self.interpreter.begin_step("Initializing...")?;
let mut commands = CommandQueue::new(self.last_res_id);
installer.init(&mut commands);
self.last_res_id = commands.get_last_resource_id();
let mut interpreter = InitInterpreter::new(&mut self.interpreter, &mut self.cache, &self.props, resources);
commands.run(&mut interpreter)?;
let path = &self.cache.get_path(Path::new("."))?;
let v = installer.get_components(path);
self.interpreter.end_step()?;
return Ok(v);
}
pub fn run_post_uninstall(&mut self, post: &mut dyn PostUninstall, install_dir: &Path) -> Result<(), TError::ErrorType>
{
let (bin, content) = self.get_target_dirs(install_dir);
let cache = self.cache.get_path(Path::new("."))?;
post.post_uninstall(&cache, &content, &bin);
return Ok(());
}
pub fn run_post_install(&mut self, post: &mut dyn PostInstall, install_dir: &Path) -> Result<(), TError::ErrorType>
{
let (bin, content) = self.get_target_dirs(install_dir);
let cache = self.cache.get_path(Path::new("."))?;
post.post_install(&cache, &content, &bin);
return Ok(());
}
fn setup_install(&self, content_dir: &Path, bin_dir: &Path) -> io::Result<()>
{
if !content_dir.exists()
{
std::fs::create_dir(&content_dir)?;
}
if !bin_dir.exists()
{
std::fs::create_dir(&bin_dir)?;
}
let manifest = content_dir.join("manifest.txt");
let mut f = File::create(manifest)?;
writeln!(f, "Installer Name: {}", self.name)?;
writeln!(f, "Installer Version: {}", self.version)?;
writeln!(f, "Installer Author: {}", self.author)?;
return Ok(());
}
pub fn get_target_dirs(&self, install_dir: &Path) -> (PathBuf, PathBuf)
{
#[cfg(windows)]
let bin = install_dir.join(self.name).join("bin");
#[cfg(unix)]
let bin = install_dir.join("bin");
let content = install_dir.join(self.name);
return (bin, content);
}
pub fn get_installation_state(&mut self, components: Vec<Component>, installer: &mut dyn Installer, install_dir: &Path) -> Result<Option<InstallationState>, TError::ErrorType>
{
let (bin, content) = self.get_target_dirs(install_dir);
let manifest = content.join("manifest.txt");
let manifest_data;
match std::fs::read_to_string(manifest)
{
Ok(v) => manifest_data = v,
Err(_) => return Ok(None)
}
let mut installed_components = Vec::new();
let mut non_installed_components = Vec::new();
for c in components
{
let mut interpreter: StatusInterpreter<TError> = StatusInterpreter::new(&content, &bin, &mut self.cache);
let mut commands = CommandQueue::new(self.last_res_id);
installer.install_component(&c.name, &mut commands);
self.last_res_id = commands.get_last_resource_id();
commands.run(&mut interpreter)?;
if interpreter.is_installed()
{
installed_components.push(c);
}
else
{
non_installed_components.push(c);
}
}
return Ok(Some(InstallationState
{
manifest: manifest_data,
installed_components: installed_components,
non_installed_components: non_installed_components
}));
}
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>
{
self.interpreter.begin_step(&format!("Installing component {}...", component.name))?;
let (bin, content) = self.get_target_dirs(install_dir);
if !self.installed
{
if let Err(e) = self.setup_install(&content, &bin)
{
return Err(TError::io(e));
}
self.installed = true;
}
#[cfg(target_os = "linux")]
let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, install_dir, method, &content, &bin, &self.props, resources);
#[cfg(target_os = "windows")]
let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, method, &content, &bin, &self.props, resources);
#[cfg(target_os = "macos")]
let mut interpreter = InstallInterpreter::new(&mut self.interpreter, &mut self.cache, &content, &bin, &self.props, resources);
let mut commands = CommandQueue::new(self.last_res_id);
installer.install_component(&component.name, &mut commands);
self.last_res_id = commands.get_last_resource_id();
commands.run(&mut interpreter)?;
self.interpreter.end_step()?;
return Ok(());
}
pub fn uninstall_component(&mut self, component: &Component, installer: &mut dyn Installer, install_dir: &Path, method: InstallMethod) -> Result<(), TError::ErrorType>
{
self.interpreter.begin_step(&format!("Uninstalling component {}...", component.name))?;
let (bin, content) = self.get_target_dirs(install_dir);
#[cfg(target_os = "linux")]
let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, install_dir, method, &content, &bin, &self.props);
#[cfg(target_os = "windows")]
let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, method, &content, &bin, &self.props);
#[cfg(target_os = "macos")]
let mut interpreter = UninstallInterpreter::new(&mut self.interpreter, &mut self.cache, &content, &bin, &self.props);
let mut commands = CommandQueue::new(self.last_res_id);
installer.install_component(&component.name, &mut commands);
self.last_res_id = commands.get_last_resource_id();
commands.run(&mut interpreter)?;
if install_dir_is_empty(&content)
{
if let Err(e) = std::fs::remove_dir_all(content)
{
return Err(TError::io(e));
}
#[cfg(windows)]
{
use crate::platform;
platform::winpath::remove_path::<TError>(&bin, method)?;
}
}
self.interpreter.end_step()?;
return Ok(());
}
}