1use std::{fmt::Display, thread};
4
5use wowlab_node::{check_for_update, install_update};
6
7use crate::{AppWindow, VERSION};
8
9#[derive(Debug, Eq, PartialEq)]
10pub(crate) enum CommandSuccess {
11 Available(String),
12 Current,
13 Installed,
14}
15
16#[derive(Debug, thiserror::Error)]
17pub(crate) enum CommandError {
18 #[error("Failed to check for updates: {0}")]
19 Check(#[source] wowlab_node::UpdateError),
20 #[error("Update failed: {0}")]
21 Install(#[source] wowlab_node::UpdateError),
22}
23
24pub(crate) fn run_command(check: bool) -> Result<CommandSuccess, CommandError> {
25 if check {
26 match check_for_update(VERSION) {
27 Ok(Some(version)) => Ok(CommandSuccess::Available(version)),
28 Ok(None) => Ok(CommandSuccess::Current),
29 Err(error) => Err(CommandError::Check(error)),
30 }
31 } else {
32 match install_update("node-gui", VERSION) {
33 Ok(_) => Ok(CommandSuccess::Installed),
34 Err(error) => Err(CommandError::Install(error)),
35 }
36 }
37}
38
39pub(crate) const fn should_check_on_startup(no_update: bool) -> bool {
40 !no_update
41}
42
43pub(crate) fn check_in_background(window: &slint::Weak<AppWindow>) {
44 if let Some(window) = window.upgrade() {
45 window.set_update_status("Checking...".into());
46 }
47
48 let worker_window = window.clone();
49 let worker = thread::Builder::new()
50 .name("node-update-check".to_string())
51 .spawn(move || {
52 let status = check_status(check_for_update(VERSION));
53 let _ = slint::invoke_from_event_loop(move || {
54 if let Some(window) = worker_window.upgrade() {
55 window.set_update_status(status.into());
56 }
57 });
58 });
59
60 if let Err(error) = worker {
61 set_status(window, format!("Check failed: {error}"));
62 }
63}
64
65fn set_status(window: &slint::Weak<AppWindow>, status: String) {
66 if let Some(window) = window.upgrade() {
67 window.set_update_status(status.into());
68 }
69}
70
71fn check_status<E>(result: Result<Option<String>, E>) -> String
72where
73 E: Display,
74{
75 match result {
76 Ok(Some(version)) => format!("Update available: v{version}"),
77 Ok(None) => "Already on latest version".to_string(),
78 Err(error) => format!("Check failed: {error}"),
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use googletest::prelude::*;
85
86 use super::*;
87
88 #[gtest]
89 fn startup_policy_respects_no_update_flag() -> Result<()> {
90 verify_eq!(
91 [
92 should_check_on_startup(false),
93 should_check_on_startup(true)
94 ],
95 [true, false]
96 )
97 }
98
99 #[gtest]
100 fn background_status_preserves_user_facing_messages() -> Result<()> {
101 verify_eq!(
102 [
103 check_status::<&str>(Ok(Some("1.2.3".to_string()))),
104 check_status::<&str>(Ok(None)),
105 check_status::<&str>(Err("offline")),
106 ],
107 [
108 "Update available: v1.2.3",
109 "Already on latest version",
110 "Check failed: offline",
111 ]
112 )
113 }
114}