install-framework-base 1.0.0

[Install Framework] Official generic interface implementation
Documentation
// Copyright 2021 Yuri6037

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

use std::path::Path;
use std::path::PathBuf;
use std::marker::PhantomData; //Because rust is a peace of shit
use install_framework_core::command::Interpreter;
use install_framework_core::command::InstallCommand;

use crate::error::Error;
use crate::cache::Cache;

pub struct StatusInterpreter<'a, TError: Error>
{
    cache: &'a mut Cache<TError>,
    content_dir: &'a Path,
    bin_dir: &'a Path,
    installed: bool,
    useless: PhantomData<TError>
}

impl <'a, TError: Error> StatusInterpreter<'a, TError>
{
    pub fn new(content_dir: &'a Path, bin_dir: &'a Path, cache: &'a mut Cache<TError>) -> StatusInterpreter<'a, TError>
    {
        return StatusInterpreter
        {
            cache: cache,
            content_dir: content_dir,
            bin_dir: bin_dir,
            installed: true,
            useless: PhantomData
        };
    }

    pub fn is_installed(&self) -> bool
    {
        return self.installed;
    }

    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 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 !dstf.exists()
        {
            self.installed = false;
        }
        return Ok(());
    }

    fn install_resource(&mut self, path: &'static str, folder: usize) -> Result<(), TError::ErrorType>
    {
        let dst;

        if folder == 0
        {
            dst = self.content_dir;
        }
        else
        {
            dst = Path::new(self.cache.get(folder)?);
        }
        let name =
        {
            if let Some(id) = path.rfind('/')
            {
                &path[id + 1..]
            }
            else
            {
                path
            }
        };
        let dstf = dst.join(name);
        if !dstf.exists()
        {
            self.installed = false;
        }
        return Ok(());
    }
}

impl <TError: Error> Interpreter<InstallCommand> for StatusInterpreter<'_, 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::InstallCached(p, f) => self.install_cached(p, f)?,
            InstallCommand::InstallResource(p, f) => self.install_resource(p, f)?,
            InstallCommand::CreateFolder(s) =>
            {
                let dir = self.content_dir.join(Path::new(&s));
                if !dir.exists()
                {
                    self.installed = false;
                }
                self.cache.insert(resid, dir);
            },
            InstallCommand::AddToPath(s) =>
            {
                let dir = self.bin_dir.join(Path::new(&s));
                if !dir.exists()
                {
                    self.installed = false;
                }
            },
            _ =>
            {
                //Do nothing
            }
        }
        return Ok(());
    }

    fn progress(&mut self, _: usize, _: usize) -> Result<(), Self::ErrorType>
    {
        return Ok(());
    }
}