1pub mod app;
14pub mod keyboard_help;
15pub mod openapi_sync;
16pub mod path_picker;
17pub mod runtime;
18pub mod screens;
19pub mod theme;
20pub mod utils;
21
22use anyhow::Result;
23use std::time::Duration;
24
25use crate::client::RommClient;
26use crate::config::{openapi_cache_path, should_check_updates, Config};
27use crate::feature_compat::save_sync_compatibility;
28
29use self::app::App;
30use self::openapi_sync::sync_openapi_registry;
31use self::screens::connected_splash::StartupSplash;
32use self::screens::setup_wizard::SetupWizard;
33
34fn install_panic_hook() {
35 let original_hook = std::panic::take_hook();
36 std::panic::set_hook(Box::new(move |panic| {
37 let _ = crossterm::terminal::disable_raw_mode();
38 let _ = crossterm::execute!(
39 std::io::stdout(),
40 crossterm::terminal::LeaveAlternateScreen,
41 crossterm::event::DisableMouseCapture
42 );
43 original_hook(panic);
44 }));
45}
46
47fn startup_splash_for(
48 from_setup_wizard: bool,
49 config: &Config,
50 server_version: &Option<String>,
51) -> Option<StartupSplash> {
52 if from_setup_wizard {
53 return Some(StartupSplash::new(
54 config.base_url.clone(),
55 server_version.clone(),
56 ));
57 }
58 if server_version.is_some() {
59 return Some(StartupSplash::new(
60 config.base_url.clone(),
61 server_version.clone(),
62 ));
63 }
64 None
65}
66
67fn startup_splash_for_launch(
69 from_setup_wizard: bool,
70 config: &Config,
71 server_version: &Option<String>,
72 update_pending: bool,
73) -> Option<StartupSplash> {
74 if update_pending {
75 return None;
76 }
77 startup_splash_for(from_setup_wizard, config, server_version)
78}
79
80async fn run_started(
81 client: RommClient,
82 config: Config,
83 from_setup_wizard: bool,
84 mock_update: bool,
85) -> Result<()> {
86 install_panic_hook();
87 let cache_path = openapi_cache_path()?;
88 let (registry, server_version) = sync_openapi_registry(&client, &cache_path).await?;
89
90 let startup_update = if mock_update {
91 Some(crate::update::UpdateStatus {
92 current_version: format!("{} (dev)", env!("CARGO_PKG_VERSION")),
93 latest_version: "9.9.9-mock".into(),
94 release_tag: "v9.9.9-mock".into(),
95 should_update: true,
96 release_url: "https://github.com/patricksmill/romm-cli".into(),
97 changelog_url: crate::update::changelog_url().to_string(),
98 })
99 } else if should_check_updates() {
100 match tokio::time::timeout(Duration::from_secs(2), crate::update::check_for_update()).await
101 {
102 Ok(Ok(status)) if status.should_update => Some(status),
103 _ => None,
104 }
105 } else {
106 None
107 };
108
109 let splash = startup_splash_for_launch(
110 from_setup_wizard,
111 &config,
112 &server_version,
113 startup_update.is_some(),
114 );
115 let save_sync_compat = save_sync_compatibility(®istry);
116 let mut app = App::new(
117 client,
118 config,
119 save_sync_compat,
120 server_version,
121 splash,
122 startup_update,
123 );
124 app.run().await
125}
126
127pub async fn run(client: RommClient, config: Config, mock_update: bool) -> Result<()> {
129 run_started(client, config, false, mock_update).await
130}
131
132pub async fn run_interactive(verbose: bool, mock_update: bool) -> Result<()> {
134 let (from_wizard, config) = match crate::config::load_config() {
135 Ok(c) => (false, c),
136 Err(_) => (true, SetupWizard::new().run(verbose).await?),
137 };
138 let client = RommClient::new(&config, verbose)?;
139 run_started(client, config, from_wizard, mock_update).await
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::config::{default_theme_id, Config, ExtrasDefaults};
146
147 fn test_config() -> Config {
148 Config {
149 base_url: "http://127.0.0.1:9".into(),
150 download_dir: "/tmp".into(),
151 use_https: false,
152 auth: None,
153 extras_defaults: ExtrasDefaults::default(),
154 save_sync: Default::default(),
155 roms_layout: Default::default(),
156 theme: default_theme_id(),
157 tui_layout: Default::default(),
158 }
159 }
160
161 #[test]
162 fn startup_splash_for_launch_skips_splash_when_update_pending() {
163 let config = test_config();
164 let version = Some("4.0.0".into());
165 assert!(startup_splash_for_launch(false, &config, &version, true).is_none());
166 }
167
168 #[test]
169 fn startup_splash_for_launch_shows_splash_when_no_update() {
170 let config = test_config();
171 let version = Some("4.0.0".into());
172 assert!(startup_splash_for_launch(false, &config, &version, false).is_some());
173 }
174}