install-framework-gui 1.0.0

[Install Framework] GUI interface powered by iced
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::collections::HashMap;
use std::path::Path;
use install_framework_core::interface::Interface;
use install_framework_core::interface::Installer;
use install_framework_core::interface::InstallMethod;
use install_framework_core::interface::PostInstall;
use install_framework_core::interface::PostUninstall;
use install_framework_core::interface::Component;
use install_framework_base::interface::BaseInterface;
use async_channel::Sender;
use async_channel::Receiver;

use crate::interpreter::ThreadedInterpreter;
use crate::messages::RenderMessage;
use crate::messages::ThreadMessage;
use crate::messages::Page;
use crate::error::GuiError;
use crate::ext::SenderSync;
use crate::ext::ReceiverSync;

pub struct ThreadLinker
{
    sender: Sender<RenderMessage>,
    receiver: Receiver<ThreadMessage>
}

impl ThreadLinker
{
    pub fn new(sender: Sender<RenderMessage>, receiver: Receiver<ThreadMessage>) -> ThreadLinker
    {
        return ThreadLinker
        {
            sender: sender,
            receiver: receiver
        };
    }

    pub fn recv(&mut self) -> Result<ThreadMessage, GuiError>
    {
        match self.receiver.recv_sync()
        {
            Ok(v) => return Ok(v),
            Err(e) => return Err(GuiError::channel_recv(e))
        };
    }

    pub fn send(&mut self, msg: RenderMessage) -> Result<(), GuiError>
    {
        if let Err(e) = self.sender.send_sync(msg)
        {
            return Err(GuiError::channel_send(e));
        }
        return Ok(());
    }

    pub fn clone_sender(&self) -> Sender<RenderMessage>
    {
        return self.sender.clone();
    }

    pub fn clone_receiver(&self) -> Receiver<ThreadMessage>
    {
        return self.receiver.clone();
    }
}

pub struct GuiInterface
{
    base: BaseInterface<ThreadedInterpreter, GuiError>,
    thread: ThreadLinker,
    uninstall: bool,
    method: InstallMethod
}

impl GuiInterface
{
    pub fn new(thread: ThreadLinker) -> GuiInterface
    {
        return GuiInterface
        {
            base: BaseInterface::new(ThreadedInterpreter::new(thread.clone_sender(), thread.clone_receiver())),
            thread: thread,
            uninstall: false,
            method: InstallMethod::SystemInstall
        };
    }

    fn wait_next_or_error(&mut self) -> Result<Option<(InstallMethod, bool)>, GuiError>
    {
        loop
        {
            match self.thread.recv()?
            {
                ThreadMessage::Next(data) => return Ok(data),
                ThreadMessage::Terminate => return Err(GuiError::Interupted),
                _ => return Err(GuiError::IllegalMessage)
            }
        }
    }

    fn wait_install_components(&mut self) -> Result<Vec<Component>, GuiError>
    {
        loop
        {
            match self.thread.recv()?
            {
                ThreadMessage::InstallComponents(data) => return Ok(data),
                ThreadMessage::Terminate => return Err(GuiError::Interupted),
                _ => return Err(GuiError::IllegalMessage)
            }
        }
    }

    fn wait_terminate(&mut self) -> Result<(), GuiError>
    {
        loop
        {
            if let Ok(v) = self.thread.recv()
            {
                match v
                {
                    ThreadMessage::Terminate => return Ok(()),
                    _ => return Err(GuiError::IllegalMessage)
                }
            }
            return Ok(());
        }
    }
}

impl Interface for GuiInterface
{
    type ErrorType = GuiError;

    fn welcome(&mut self, name: &'static str, version: &'static str, author: &'static str) -> Result<(), GuiError>
    {
        self.base.set_static_info(name, version, author);
        self.thread.send(RenderMessage::SwitchPage(Page::Welcome(name, version, author)))?;
        match self.wait_next_or_error()?
        {
            Some((method, uninstall)) =>
            {
                self.method = method;
                self.uninstall = uninstall;
                return Ok(());
            },
            None => return Err(GuiError::IllegalMessage)
        };
    }

    fn get_install_method(&mut self) -> Result<InstallMethod, GuiError>
    {
        return Ok(self.method);
    }

    fn should_uninstall(&self) -> Result<bool, GuiError>
    {
        return Ok(self.uninstall);
    }

    fn run_install(&mut self, installer: &mut dyn Installer, dir: &Path, method: InstallMethod, resources: &HashMap<&'static str, &'static [u8]>) -> Result<(), GuiError>
    {
        self.thread.send(RenderMessage::SwitchPage(Page::Processing))?;
        let components = self.base.get_components(installer, resources)?;
        let copy = components.clone();
        let state = self.base.get_installation_state(components, installer, dir)?;
        self.thread.send(RenderMessage::SwitchPage(Page::ComponentView(copy, state, false)))?;
        let comps = self.wait_install_components()?;
        self.thread.send(RenderMessage::SwitchPage(Page::Processing))?;
        for v in comps
        {
            self.base.install_component(&v, installer, dir, method, resources)?;
        }
        return Ok(());
    }

    fn run_post_install(&mut self, post: &mut dyn PostInstall, dir: &Path) -> Result<(), GuiError>
    {
        self.base.run_post_install(post, dir)?;
        return Ok(());
    }

    fn run_uninstall(&mut self, installer: &mut dyn Installer, dir: &Path, method: InstallMethod, resources: &HashMap<&'static str, &'static [u8]>) -> Result<(), GuiError>
    {
        self.thread.send(RenderMessage::SwitchPage(Page::Processing))?;
        let components = self.base.get_components(installer, resources)?;
        let copy = components.clone();
        let state = self.base.get_installation_state(components, installer, dir)?;
        self.thread.send(RenderMessage::SwitchPage(Page::ComponentView(copy, state, true)))?;
        let comps = self.wait_install_components()?;
        self.thread.send(RenderMessage::SwitchPage(Page::Processing))?;
        for v in comps
        {
            self.base.uninstall_component(&v, installer, dir, method)?;
        }
        return Ok(());
    }

    fn run_post_uninstall(&mut self, post: &mut dyn PostUninstall, dir: &Path) -> Result<(), GuiError>
    {
        self.base.run_post_uninstall(post, dir)?;
        return Ok(());
    }

    fn error(&mut self, e: GuiError) -> i32
    {
        let (_, code) = e.translate();
        if let Err(e) = self.thread.send(RenderMessage::SwitchPage(Page::Error(e)))
        {
            return handle_low_level_error(&e);
        }
        if let Err(e) = self.wait_terminate()
        {
            return handle_low_level_error(&e);
        }
        return code;
    }

    fn finish(&mut self) -> i32
    {
        let msg =
        {
            if self.uninstall
            {
                String::from("Uninstall complete!")
            }
            else
            {
                String::from("Install complete!")
            }
        };
        if let Err(e) = self.thread.send(RenderMessage::SwitchPage(Page::Finish(msg)))
        {
            return handle_low_level_error(&e);
        }
        if let Err(e) = self.wait_terminate()
        {
            return handle_low_level_error(&e);
        }
        return 0;
    }
}

fn handle_low_level_error(e: &GuiError) -> i32
{
    let (s, exit) = e.translate();
    eprintln!("{}", s);
    return exit;
}