Yoda 0.11.4

Browser for Gemini Protocol
mod about;
mod action;
mod database;
mod widget;
pub mod window;

use about::About;
use action::Action;
use widget::Widget;
use window::Window;

use crate::Profile;
use adw::{AboutDialog, Application, prelude::AdwDialogExt};
use anyhow::Result;
use gtk::{
    FileLauncher,
    gio::{Cancellable, File},
    prelude::GtkWindowExt,
};
use sqlite::Transaction;
use std::{rc::Rc, sync::Arc};

pub struct Browser {
    pub action: Rc<Action>,
    pub widget: Rc<Widget>,
    pub window: Rc<Window>,
}

impl Browser {
    // Constructors

    /// Build new `Self`
    pub fn build(profile: &Arc<Profile>) -> Browser {
        // Init components
        let action = Rc::new(Action::new());
        let window = Rc::new(Window::build(profile, &action));

        // Init widget
        let widget = Rc::new(Widget::new(
            &window,
            &[
                // action groups
                (
                    // browser
                    &action.id,
                    action.simple_action_group.clone(),
                ),
                (
                    // window
                    &window.action.id,
                    window.action.simple_action_group.clone(),
                ),
                (
                    // tab
                    &window.tab.action.id,
                    window.tab.action.simple_action_group.clone(),
                ),
            ],
        ));

        // Connect events
        action.about.connect_activate({
            let window = window.clone();
            move || AboutDialog::about().present(Some(&window.g_box))
        });

        action.close.connect_activate({
            let widget = widget.clone();
            move || widget.application_window.close()
        });

        action.debug.connect_activate({
            let widget = widget.clone();
            move || {
                widget.application_window.emit_enable_debugging(true);
            }
        });

        action.escape.connect_activate({
            let widget = widget.clone();
            let window = window.clone();
            move |_, _| {
                window.escape();
                widget.application_window.set_focus(gtk::Window::NONE);
            }
        });

        action.profile.connect_activate({
            let profile = profile.clone();
            move || {
                FileLauncher::new(Some(&File::for_path(profile.config_path.as_path()))).launch(
                    adw::Window::NONE,
                    Cancellable::NONE,
                    |result| {
                        if let Err(error) = result {
                            println!("{error}")
                        }
                    },
                ); // @TODO move out?
            }
        });

        // Return new activated `Self`
        Self {
            action,
            widget,
            window,
        }
    }

    // Actions

    pub fn clean(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
        for record in database::select(transaction, app_id)? {
            database::delete(transaction, record.id)?;
            // Delegate clean action to childs
            self.window.clean(transaction, record.id)?;
            self.widget.clean(transaction, record.id)?;
            /* @TODO
            self.header.clean(transaction, &record.id)?; */
        }
        Ok(())
    }

    pub fn restore(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
        for record in database::select(transaction, app_id)? {
            // Delegate restore action to childs
            self.widget.restore(transaction, record.id)?;
            self.window.restore(transaction, record.id)?;
            /* @TODO
            self.header.restore(transaction, &record.id)?; */
        }
        Ok(())
    }

    pub fn save(&self, transaction: &Transaction, app_id: i64) -> Result<()> {
        let id = database::insert(transaction, app_id)?;
        // Delegate save action to childs
        self.widget.save(transaction, id)?;
        self.window.save(transaction, id)?;
        /* @TODO
        self.header.save(transaction, &id)?; */
        Ok(())
    }

    pub fn init(&self, application: Option<&Application>) -> &Self {
        // Assign browser window to this application
        self.widget.application_window.set_application(application); // @TODO
        // Init main window
        self.window.init();
        self
    }

    pub fn present(&self) -> &Self {
        self.widget.application_window.present();
        self
    }
}

// Tools
pub fn migrate(tx: &Transaction) -> Result<()> {
    // Migrate self components
    database::init(tx)?;

    // Delegate migration to childs
    /* @TODO
    header::migrate(tx)?; */
    window::migrate(tx)?;
    widget::migrate(tx)?;

    // Success
    Ok(())
}