pacenotes_installer\wizard\scene/
fetching.rs1use windows_sys::Win32::Foundation::{HWND, LPARAM, WPARAM};
2use windows_sys::Win32::UI::Controls as ctl;
3use windows_sys::Win32::UI::WindowsAndMessaging as wm;
4
5pub unsafe extern "system" fn fetching_page(
10 dialog: HWND,
11 message: u32,
12 wparam: WPARAM,
13 lparam: LPARAM,
14) -> isize {
15 match message {
16 wm::WM_INITDIALOG => {
17 crate::wizard::page::adopt_state(dialog, lparam);
18 super::progress_page::init_progress(dialog);
19 1
20 }
21 crate::worker::WM_WORKER_PROGRESS => {
22 let Some(progress) = crate::worker::take_progress(lparam) else {
23 return 0;
24 };
25 if let Some(state) = crate::wizard::page::state(dialog) {
26 if state.is_current_request(wparam) {
27 super::progress_page::show_progress(dialog, state, &progress);
28 }
29 }
30 1
31 }
32 crate::worker::WM_WORKER_DONE => {
33 let Some(message) = crate::worker::take(lparam) else {
34 return 0;
35 };
36 if let (Some(state), crate::worker::Message::Downloaded(fetched)) =
37 (crate::wizard::page::state(dialog), message)
38 {
39 if state.is_current_request(wparam) {
40 finish(dialog, state, fetched);
41 }
42 }
43 1
44 }
45 wm::WM_NOTIFY => match crate::wizard::page::notify_code(lparam) {
46 Some(ctl::PSN_SETACTIVE) => {
47 crate::wizard::page::set_sheet_caption(dialog);
48 crate::wizard::page::set_header(dialog, crate::i18n::IDS_S4B_TITLE);
49 crate::wizard::page::set_buttons(dialog, 0);
50 if let Some(state) = crate::wizard::page::state(dialog) {
51 super::progress_page::show_log(dialog, state);
52 }
53 start(dialog);
54 crate::wizard::page::reply(dialog, 0)
55 }
56 Some(ctl::PSN_QUERYCANCEL) => super::progress_page::on_cancel(dialog),
57 _ => 0,
58 },
59 _ => 0,
60 }
61}
62
63unsafe fn start(dialog: HWND) {
66 let Some(state) = crate::wizard::page::state(dialog) else {
67 return;
68 };
69 if state.download_started {
70 return;
71 }
72 state.download_started = true;
73
74 let missing: Vec<pacenotes_shared::PackageInfo> = state
75 .tree
76 .checked_needing_download()
77 .iter()
78 .filter_map(|key| {
79 state
80 .packages
81 .iter()
82 .find(|package| &package.download_path() == key)
83 .cloned()
84 })
85 .filter(|package| {
88 let cached = state.cache.get(&package.download_path());
89 !cached.is_some_and(|entry| entry.sha256 == package.sha256)
90 || !state.cache.has_payload(&package.sha256)
91 })
92 .collect();
93 if missing.is_empty() {
94 let nothing = crate::i18n::load_string(crate::i18n::IDS_S6_NOTHING);
95 super::progress_page::log_line(dialog, state, ¬hing);
96 let done = crate::worker::Fetched::Done {
97 downloaded: 0,
98 bytes: 0,
99 failed: Vec::new(),
100 };
101 finish(dialog, state, done);
102 return;
103 }
104
105 crate::ui::set_text(
106 dialog,
107 crate::wizard::control_ids::IDC_S6_PHASE,
108 crate::i18n::IDS_S6_DOWNLOADING,
109 );
110 crate::ui::enable(
111 crate::wizard::page::sheet(dialog),
112 crate::ui::IDCANCEL,
113 true,
114 );
115 let request = state.next_request();
116 let job = crate::worker::DownloadJob {
117 base_url: state.base_url.clone(),
118 token: state.token(),
119 cache_root: state.cache.root().to_path_buf(),
120 downloads: missing,
121 cancel: std::sync::Arc::clone(&state.cancel),
122 };
123 crate::worker::spawn_reporting(dialog, request, move |reporter| {
124 crate::worker::run_download(job, reporter)
125 });
126}
127
128unsafe fn finish(
133 dialog: HWND,
134 state: &mut crate::wizard::state::WizardState,
135 fetched: crate::worker::Fetched,
136) {
137 match fetched {
138 crate::worker::Fetched::Done {
139 downloaded,
140 bytes,
141 failed,
142 } => {
143 state.downloaded += downloaded;
145 state.downloaded_bytes += bytes;
146 if downloaded > 0 {
147 let line = format!(
148 "downloaded {downloaded} package(s), {}",
149 pacenotes_installer::size::human(bytes)
150 );
151 super::progress_page::log_line(dialog, state, &line);
152 }
153 state.cache = pacenotes_installer::cache::Cache::open(state.cache.root().to_path_buf());
155 leave_out(dialog, state, failed);
156 if downloaded == 0 && !state.not_downloaded.is_empty() {
157 let nothing = crate::i18n::load_string(crate::i18n::IDS_S7_NONE_DOWNLOADED);
158 end_run_early(dialog, state, crate::worker::Outcome::Failed(nothing));
159 return;
160 }
161 if !super::configuration::has_something_to_pick(dialog, state) {
162 back_to_selection(dialog, state);
163 return;
164 }
165 super::progress_page::finish_page(dialog, crate::i18n::IDS_S4B_DONE, None);
166 }
167 crate::worker::Fetched::Cancelled => {
168 super::progress_page::log_line(dialog, state, "cancelled by the user");
169 end_run_early(dialog, state, crate::worker::Outcome::Cancelled);
170 }
171 }
172}
173
174unsafe fn leave_out(
178 dialog: HWND,
179 state: &mut crate::wizard::state::WizardState,
180 failed: Vec<crate::worker::NotDownloaded>,
181) {
182 for failure in &failed {
183 let line = format!("not downloaded: {} — {}", failure.key, failure.reason);
184 super::progress_page::log_line(dialog, state, &line);
185 if state.cache.get(&failure.key).is_none() {
186 state.tree.uncheck_key(&failure.key);
187 }
188 }
189 state.not_downloaded = failed;
190}
191
192unsafe fn back_to_selection(dialog: HWND, state: &mut crate::wizard::state::WizardState) {
195 super::progress_page::log_line(
196 dialog,
197 state,
198 "nothing to configure yet: back to the selection",
199 );
200 state.download_started = false;
201 super::progress_page::set_progress(dialog, 0);
202 let sheet = crate::wizard::page::sheet(dialog);
203 let selection = crate::wizard::control_ids::IDD_S4_DOWNLOAD;
204 wm::PostMessageW(sheet, ctl::PSM_SETCURSELID, 0, selection as LPARAM);
205}
206
207unsafe fn end_run_early(
208 dialog: HWND,
209 state: &mut crate::wizard::state::WizardState,
210 outcome: crate::worker::Outcome,
211) {
212 state.outcome = Some(outcome);
213 let summary = crate::wizard::control_ids::IDD_S7_SUMMARY;
214 super::progress_page::finish_page(dialog, crate::i18n::IDS_S6_FINISHED, Some(summary));
215}