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}