morpheus_bootloader/tui/iso_manager/
ui.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! ISO Manager UI
//!
//! Main UI component for the ISO manager.

use super::renderer;
use super::state::{Action, IsoManagerState, ViewMode};
use crate::tui::input::Keyboard;
use crate::tui::renderer::Screen;
use morpheus_core::iso::IsoStorageManager;

/// ISO Manager TUI component
pub struct IsoManager {
    state: IsoManagerState,
    storage: IsoStorageManager,
}

impl IsoManager {
    /// Create a new ISO manager
    ///
    /// # Arguments
    /// * `esp_start_lba` - Start LBA of ESP partition
    /// * `disk_size_lba` - Total disk size in LBAs
    pub fn new(esp_start_lba: u64, disk_size_lba: u64) -> Self {
        let storage = IsoStorageManager::new(esp_start_lba, disk_size_lba);
        let mut state = IsoManagerState::new();
        state.load_from_manager(&storage);

        Self { state, storage }
    }

    /// Create with existing storage manager
    pub fn with_storage(storage: IsoStorageManager) -> Self {
        let mut state = IsoManagerState::new();
        state.load_from_manager(&storage);

        Self { state, storage }
    }

    /// Get reference to storage manager
    pub fn storage(&self) -> &IsoStorageManager {
        &self.storage
    }

    /// Get mutable reference to storage manager
    pub fn storage_mut(&mut self) -> &mut IsoStorageManager {
        &mut self.storage
    }

    /// Reload ISO list from storage
    pub fn refresh(&mut self) {
        self.state.load_from_manager(&self.storage);
        self.state.clear_error();
    }

    /// Run the ISO manager UI
    ///
    /// Returns when user presses ESC or selects boot action.
    /// Returns Some(index) if user wants to boot an ISO.
    pub fn run(&mut self, screen: &mut Screen, keyboard: &mut Keyboard) -> Option<usize> {
        screen.clear();
        renderer::render(screen, &self.state);

        loop {
            if let Some(key) = keyboard.poll_key_with_delay() {
                let action = self.state.handle_key(key.scan_code, key.unicode_char);

                match action {
                    Action::None => {
                        // Just re-render
                        renderer::render(screen, &self.state);
                    }
                    Action::Back => {
                        return None;
                    }
                    Action::Boot(idx) => {
                        return Some(idx);
                    }
                    Action::Delete(idx) => {
                        self.handle_delete(idx);
                        screen.clear();
                        renderer::render(screen, &self.state);
                    }
                    Action::Refresh => {
                        self.refresh();
                        screen.clear();
                        renderer::render(screen, &self.state);
                    }
                }
            }
        }
    }

    /// Handle delete action
    fn handle_delete(&mut self, idx: usize) {
        match self.storage.remove_entry(idx) {
            Ok(()) => {
                self.state.load_from_manager(&self.storage);
                self.state.clear_error();
            }
            Err(_) => {
                self.state.set_error("Failed to delete ISO");
            }
        }
    }

    /// Get read context for booting an ISO
    pub fn get_boot_context(
        &self,
        idx: usize,
    ) -> Result<morpheus_core::iso::IsoReadContext, morpheus_core::iso::IsoError> {
        self.storage.get_read_context(idx)
    }

    /// Check if an ISO is ready to boot
    pub fn is_bootable(&self, idx: usize) -> bool {
        if let Some(entry) = self.storage.get(idx) {
            entry.manifest.is_complete()
        } else {
            false
        }
    }

    /// Get ISO count
    pub fn count(&self) -> usize {
        self.storage.count()
    }

    /// Get ISO name by index
    pub fn get_name(&self, idx: usize) -> Option<&str> {
        self.storage.get(idx).map(|e| e.manifest.name_str())
    }
}