Skip to main content

pacenotes_installer\wizard/
state.rs

1/// Which of scene 3's three buttons started the run. It decides only what is
2/// skipped: every route ends in the same progress page and summary.
3#[derive(Clone, Copy, PartialEq, Eq, Debug)]
4pub enum Route {
5    /// Download/Update: scene 4 picks from the catalogue, scene 4b fetches.
6    Download,
7    /// Configure/Install: straight to the questions, against the cache and the ledger.
8    Configure,
9    /// Restore/Uninstall: pick a state to go back to, read what that would do, do it.
10    Restore,
11}
12
13/// What the run accumulates, owned by the sheet and shared by every page.
14pub struct WizardState {
15    pub language: crate::i18n::Language,
16    /// The access key from scene 2; empty is anonymous.
17    pub secret: String,
18    /// `None` until `GET /health` answers.
19    pub online: Option<bool>,
20    /// Set once `secret` is accepted, so re-entering scene 2 does not re-check it.
21    pub secret_checked: bool,
22    pub folder: String,
23    /// In `Game::ALL` order.
24    pub detected: Vec<pacenotes_installer::gamedir::Game>,
25    /// The single detection, or the user's pick when the folder holds several games.
26    pub game: Option<pacenotes_installer::gamedir::Game>,
27    pub route: Route,
28    pub base_url: String,
29    /// The game folder's own cache, opened when scene 3 settles the folder.
30    pub cache: pacenotes_installer::cache::Cache,
31    /// `--cache <dir>`, which wins over the game folder's cache when given.
32    pub cache_override: Option<std::path::PathBuf>,
33
34    /// The catalogue behind the tree, whose sizes and digests scene 6 needs.
35    pub packages: Vec<pacenotes_shared::PackageInfo>,
36    pub tree: pacenotes_installer::tree::Tree,
37    /// `HTREEITEM` per tree node, by node index.
38    pub tree_items: Vec<isize>,
39    /// Set while the model is pushed into the control, whose change notifications would recurse.
40    pub tree_syncing: bool,
41    pub catalogue_loaded: bool,
42    /// The server publishes a catalogue from a schema this build does not know,
43    /// which blocks downloading for this game and nothing else.
44    pub schema_mismatch: bool,
45    /// Set when scene 4b starts its run, since the sheet activates the page again afterwards.
46    pub download_started: bool,
47    /// What scene 4b fetched, for the summary, since the install outcome comes later.
48    pub downloaded: usize,
49    pub downloaded_bytes: u64,
50    /// What scene 4b could not bring after its retries, which scene 7 names.
51    pub not_downloaded: Vec<crate::worker::NotDownloaded>,
52    /// The keys committed with Download, in tree order.
53    pub chosen: Vec<String>,
54    /// Rebuilt from the tree each time scene 5 activates.
55    pub config: pacenotes_installer::choices::Configuration,
56
57    /// The points offered, newest first; the list's last row is "remove everything".
58    pub points: Vec<pacenotes_installer::restore::Point>,
59    /// The chosen point and what it would do, handed to the worker unchanged,
60    /// so the preview approved and the work done cannot disagree.
61    pub restore_to: Option<pacenotes_installer::restore::Point>,
62    pub restore_plan: pacenotes_installer::restore::Plan,
63
64    /// Shown on scenes 6 and 7, and written to disk as it grows.
65    pub log: pacenotes_installer::runlog::Log,
66    /// Raised by the UI thread, read by the worker between chunks.
67    pub cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
68    /// Set when scene 6 starts its run, since the sheet activates the page again afterwards.
69    pub install_started: bool,
70    /// `None` while the run is still going.
71    pub outcome: Option<crate::worker::Outcome>,
72
73    request: usize,
74}
75
76impl WizardState {
77    pub fn new(base_url: String, cache_override: Option<std::path::PathBuf>) -> WizardState {
78        let header = crate::run_header::run_header(&base_url);
79        WizardState {
80            language: crate::i18n::os_language().unwrap_or(crate::i18n::Language::En),
81            secret: String::new(),
82            online: None,
83            secret_checked: false,
84            folder: String::new(),
85            detected: Vec::new(),
86            game: None,
87            route: Route::Download,
88            base_url,
89            // Reads as empty until a route is taken, and nothing downloads before that.
90            cache: pacenotes_installer::cache::Cache::open(
91                cache_override.clone().unwrap_or_default(),
92            ),
93            cache_override,
94            packages: Vec::new(),
95            tree: pacenotes_installer::tree::Tree::default(),
96            tree_items: Vec::new(),
97            tree_syncing: false,
98            catalogue_loaded: false,
99            schema_mismatch: false,
100            download_started: false,
101            downloaded: 0,
102            downloaded_bytes: 0,
103            not_downloaded: Vec::new(),
104            chosen: Vec::new(),
105            config: pacenotes_installer::choices::Configuration::default(),
106            points: Vec::new(),
107            restore_to: None,
108            restore_plan: pacenotes_installer::restore::Plan::default(),
109            // Attached to the game folder once scene 3 settles it.
110            log: pacenotes_installer::runlog::Log::create(header, None),
111            cancel: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
112            install_started: false,
113            outcome: None,
114            request: 0,
115        }
116    }
117
118    /// A fresh id for a worker request; answers to older ids are dropped.
119    pub fn next_request(&mut self) -> usize {
120        self.request += 1;
121        self.request
122    }
123
124    pub fn is_current_request(&self, request: usize) -> bool {
125        request == self.request
126    }
127
128    /// The access key for a request, or `None` for anonymous.
129    pub fn token(&self) -> Option<String> {
130        Some(self.secret.clone()).filter(|secret| !secret.is_empty())
131    }
132
133    /// Takes up where an instance restarted as administrator left off: its
134    /// language, folder and key, the key already past scene 2.
135    pub fn resume(&mut self, restart: crate::elevation::Restart) {
136        self.language = restart.language;
137        self.folder = restart.folder;
138        self.secret = restart.secret;
139        self.secret_checked = true;
140    }
141
142    /// Scene 3 settles the game before anything needs it; RBR only spares the lookups a `None`.
143    pub fn game_id(&self) -> &'static str {
144        self.game
145            .map(|game| game.id())
146            .unwrap_or(pacenotes_shared::GAME_RBR)
147    }
148}