diff --git a/local/recipes/system/cub/source/cub-lib/src/storage.rs b/local/recipes/system/cub/source/cub-lib/src/storage.rs index 30175f323..364ee2aa5 100644 --- a/local/recipes/system/cub/source/cub-lib/src/storage.rs +++ b/local/recipes/system/cub/source/cub-lib/src/storage.rs @@ -102,7 +102,7 @@ impl CubStore { self.recipes_dir().join(name).is_dir() } - fn from_root(root_dir: PathBuf) -> Self { + pub fn from_root(root_dir: PathBuf) -> Self { Self { root_dir } } } diff --git a/local/recipes/system/cub/source/cub-tui/src/app.rs b/local/recipes/system/cub/source/cub-tui/src/app.rs index a2bbf6f8c..d9eeb3e11 100644 --- a/local/recipes/system/cub/source/cub-tui/src/app.rs +++ b/local/recipes/system/cub/source/cub-tui/src/app.rs @@ -1,3 +1,4 @@ +use std::env; use std::fs; use std::io::{self, stdout}; use std::path::{Path, PathBuf}; @@ -21,7 +22,6 @@ use termion::raw::IntoRawMode; use termion::screen::IntoAlternateScreen; use crate::theme::RedBearTheme; -use crate::views; const DEFAULT_TARGET: &str = "x86_64-unknown-redox"; @@ -82,19 +82,33 @@ pub struct CubApp { } impl CubApp { - pub fn new() -> Result { - let store = CubStore::new()?; - store.init()?; + pub fn new() -> Self { + let store = CubStore::new().unwrap_or_else(|_| { + let fallback = env::var("HOME") + .ok() + .map(|h| PathBuf::from(h).join(".cub")) + .unwrap_or_else(|| PathBuf::from("/tmp/.cub")); + CubStore::from_root(fallback) + }); + let _ = store.init(); + + let aur_client = Some(AurClient::new()).filter(|_| { + env::var("AUR_OFFLINE").is_err() + }); let mut app = Self { search_query: String::new(), search_results: Vec::new(), selected_index: 0, current_view: View::Search, - status_message: "Type a query, press Enter to search AUR, Tab to change views.".into(), + status_message: if aur_client.is_some() { + "Type a query, press Enter to search AUR, Tab to change views.".into() + } else { + "AUR offline — Query view available, Tab to change views.".into() + }, running: true, store, - aur_client: Some(AurClient::new()), + aur_client, query_entries: Vec::new(), query_details: Vec::new(), install_log: vec![ @@ -110,19 +124,26 @@ impl CubApp { active_action: None, tick: 0, }; - app.refresh_query_view()?; - Ok(app) + let _ = app.refresh_query_view(); + app } pub fn run(&mut self) -> Result<(), CubError> { - let stdout = stdout(); - let stdout = stdout.into_raw_mode()?; + let stdout = stdout().into_raw_mode()?; let stdout = stdout.into_alternate_screen()?; let backend = TermionBackend::new(stdout); let mut terminal = Terminal::new(backend).map_err(terminal_error)?; terminal.clear().map_err(terminal_error)?; let mut events = termion::async_stdin().keys(); + self.run_inner(&mut terminal, &mut events) + } + + pub fn run_inner( + &mut self, + terminal: &mut Terminal>>>, + events: &mut impl Iterator>, + ) -> Result<(), CubError> { let run_result = (|| -> Result<(), CubError> { while self.running { self.tick = self.tick.wrapping_add(1); @@ -168,17 +189,17 @@ impl CubApp { ]) .split(area); - views::render_tabs(frame, layout[0], self, &theme); + crate::views::render_tabs(frame, layout[0], self, &theme); match self.current_view { - View::Search => views::search::render(frame, layout[1], self, &theme), - View::PackageInfo => views::info::render(frame, layout[1], self, &theme), - View::Install => views::install::render(frame, layout[1], self, &theme), - View::Build => views::build::render(frame, layout[1], self, &theme), - View::Query => views::query::render(frame, layout[1], self, &theme), + View::Search => crate::views::search::render(frame, layout[1], self, &theme), + View::PackageInfo => crate::views::info::render(frame, layout[1], self, &theme), + View::Install => crate::views::install::render(frame, layout[1], self, &theme), + View::Build => crate::views::build::render(frame, layout[1], self, &theme), + View::Query => crate::views::query::render(frame, layout[1], self, &theme), } - views::render_status(frame, layout[2], self, &theme); + crate::views::render_status(frame, layout[2], self, &theme); } pub fn handle_key(&mut self, key: Key) { @@ -207,11 +228,11 @@ impl CubApp { } match self.current_view { - View::Search => views::search::handle_key(self, key), - View::PackageInfo => views::info::handle_key(self, key), - View::Install => views::install::handle_key(self, key), - View::Build => views::build::handle_key(self, key), - View::Query => views::query::handle_key(self, key), + View::Search => crate::views::search::handle_key(self, key), + View::PackageInfo => crate::views::info::handle_key(self, key), + View::Install => crate::views::install::handle_key(self, key), + View::Build => crate::views::build::handle_key(self, key), + View::Query => crate::views::query::handle_key(self, key), } } @@ -258,7 +279,7 @@ impl CubApp { } let Some(client) = self.aur_client.as_ref() else { - self.status_message = "AUR client is unavailable in this build.".into(); + self.status_message = "AUR offline — cannot search.".into(); return; }; diff --git a/local/recipes/system/cub/source/cub-tui/src/lib.rs b/local/recipes/system/cub/source/cub-tui/src/lib.rs index 77557b188..9d1ebba12 100644 --- a/local/recipes/system/cub/source/cub-tui/src/lib.rs +++ b/local/recipes/system/cub/source/cub-tui/src/lib.rs @@ -1,15 +1,13 @@ -mod app; -mod theme; -mod views; -mod widgets; +pub mod app; +pub mod theme; +pub mod views; +pub mod widgets; use std::io; use app::CubApp; pub fn run() -> io::Result<()> { - match CubApp::new() { - Ok(mut app) => app.run().map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e}"))), - Err(_) => Err(io::Error::new(io::ErrorKind::Other, "failed to initialize cub")), - } + let mut app = CubApp::new(); + app.run().map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e}"))) }