carch-core 5.3.4

Core library for carch, providing script management and UI components.
Documentation
use std::collections::HashMap;
use std::path::PathBuf;

use log::info;
use ratatui::layout::Rect;

use super::state::{
    App, AppMode, DescriptionState, FocusedPanel, HelpState, MultiSelectState, PreviewState,
    SearchState, StatefulList,
};
use crate::ui::state::{ScriptItem, UiOptions};
use crate::ui::theme::Theme;

impl<'a> App<'a> {
    pub fn new(options: &UiOptions) -> App<'a> {
        let theme = match options.theme.as_str() {
            "catppuccin-mocha" => Theme::catppuccin_mocha(),
            "dracula" => Theme::dracula(),
            "gruvbox" => Theme::gruvbox(),
            "nord" => Theme::nord(),
            "rose-pine" => Theme::rose_pine(),
            _ => Theme::catppuccin_mocha(),
        };

        App {
            mode: if options.is_root { AppMode::RootWarning } else { AppMode::Normal },
            quit: false,
            focused_panel: FocusedPanel::Categories,
            log_mode: false,
            modules_dir: PathBuf::new(),
            theme,
            theme_locked: options.theme_locked,

            scripts: StatefulList::new(),
            categories: StatefulList::new(),
            all_scripts: HashMap::new(),

            script_panel_area: Rect::default(),
            preview: PreviewState::default(),
            search: SearchState::default(),
            multi_select: MultiSelectState::default(),
            help: HelpState::default(),
            description: DescriptionState::default(),
            run_script_popup: None,
            script_execution_queue: Vec::new(),
        }
    }

    pub fn cycle_theme(&mut self) {
        self.theme = match self.theme.name.as_str() {
            "Catppuccin Mocha" => Theme::dracula(),
            "Dracula" => Theme::gruvbox(),
            "Gruvbox" => Theme::nord(),
            "Nord" => Theme::rose_pine(),
            "Rosé Pine" => Theme::catppuccin_mocha(),
            _ => Theme::catppuccin_mocha(),
        }
    }

    pub fn toggle_description_popup(&mut self) {
        if self.mode == AppMode::Description {
            self.mode = AppMode::Normal;
            self.description.content = None;
            if self.log_mode {
                info!("Closed description popup");
            }
        } else if let Some(selected_script) = self.get_selected_script() {
            let desc_path = self.modules_dir.join(&selected_script.category).join("desc.toml");

            if self.log_mode {
                info!(
                    "Attempting to show description for script: {}/{}",
                    selected_script.category, selected_script.name
                );
                info!("Description file path: {}", desc_path.display());
            }

            if desc_path.exists() {
                if let Ok(content) = std::fs::read_to_string(&desc_path) {
                    if let Ok(table) = content.parse::<toml::Table>() {
                        let script_path = PathBuf::from(&selected_script.name);
                        let script_name_without_ext =
                            script_path.file_stem().and_then(|s| s.to_str());

                        if let Some(name) = script_name_without_ext {
                            if let Some(desc) = table
                                .get(name)
                                .and_then(|v| v.as_table())
                                .and_then(|t| t.get("description"))
                                .and_then(|v| v.as_str())
                            {
                                self.description.content = Some(desc.to_string());
                                self.mode = AppMode::Description;
                                if self.log_mode {
                                    info!(
                                        "Successfully loaded description and entered description mode."
                                    );
                                }
                            } else if self.log_mode {
                                info!(
                                    "No description found for script '{}' in desc.toml",
                                    selected_script.name
                                );
                            }
                        }
                    } else if self.log_mode {
                        info!("Failed to parse desc.toml at {}", desc_path.display());
                    }
                } else if self.log_mode {
                    info!("Failed to read desc.toml at {}", desc_path.display());
                }
            } else if self.log_mode {
                info!("desc.toml not found at {}", desc_path.display());
            }
        }
    }

    pub fn get_selected_script(&self) -> Option<&ScriptItem> {
        self.scripts.state.selected().map(|i| &self.scripts.items[i])
    }
}