Skip to main content

pacenotes_installer\restore/
execute.rs

1/// Everything read and checked before the first file moves, as `engine::preflight`
2/// does for an install.
3#[derive(Debug, Default)]
4pub struct Prepared {
5    /// Per package to put back, the files its payload holds.
6    pub files: std::collections::BTreeMap<String, Vec<crate::zip::FileEntry>>,
7    pub install_bytes: u64,
8}
9
10/// Finds a missing or damaged payload, an unwritable folder, too little room
11/// or a running game while the folder is still untouched.
12pub fn prepare(
13    plan: &super::Plan,
14    cache: &crate::cache::Cache,
15    facts: crate::engine::HostFacts,
16    game_dir: &std::path::Path,
17    watcher: &mut dyn crate::engine::Watcher,
18) -> Result<Prepared, crate::engine::PreflightError> {
19    if facts.game_running {
20        return Err(crate::engine::PreflightError::GameRunning);
21    }
22    crate::engine::probe_writable(game_dir)?;
23
24    let mut prepared = Prepared::default();
25    let count = plan.put_back.len() as u64;
26    for (index, wanted) in plan.put_back.iter().enumerate() {
27        let phase = crate::engine::Phase::Checking;
28        watcher.phase(phase, &wanted.key, index as u64, count);
29        let listed = crate::engine::verified_files(cache, &wanted.key, &wanted.sha256)?;
30        prepared.install_bytes += listed.iter().map(|file| file.size).sum::<u64>();
31        prepared.files.insert(wanted.key.clone(), listed);
32    }
33    crate::engine::check_space(prepared.install_bytes, facts)?;
34    Ok(prepared)
35}
36
37/// Carries out a restore, opening a restore point of its own first, so a
38/// restore to the wrong point can be undone like any other run.
39///
40/// The ledger is rewritten even when a put-back fails, so a restore that
41/// stopped halfway still describes the folder truthfully.
42pub fn execute(
43    target: &super::Snapshot,
44    plan: &super::Plan,
45    prepared: &Prepared,
46    game_dir: &std::path::Path,
47    cache: &crate::cache::Cache,
48    ledger: &mut crate::ledger::Ledger,
49    watcher: &mut dyn crate::engine::Watcher,
50) -> Result<crate::engine::Outcome, crate::engine::ExecuteError> {
51    let mut outcome = crate::engine::Outcome::default();
52    if plan.is_empty() {
53        return Ok(outcome);
54    }
55    let live = ledger.packages().clone();
56    let point = crate::engine::open_restore_point(game_dir, ledger, watcher)?;
57    outcome.restore_point = Some(point.clone());
58    let run = &mut crate::engine::RunContext {
59        game_dir,
60        cache,
61        ledger,
62        watcher,
63    };
64
65    let count = plan.remove.len() as u64;
66    for (index, key) in plan.remove.iter().enumerate() {
67        let phase = crate::engine::Phase::Uninstalling;
68        run.watcher.phase(phase, key, index as u64, count);
69        crate::engine::uninstall_package(run, key, &mut outcome)?;
70    }
71
72    let put_back = put_back_all(target, plan, prepared, run, &point, &mut outcome);
73    let failed: Vec<String> = plan
74        .put_back
75        .iter()
76        .map(|wanted| wanted.key.clone())
77        .filter(|key| !outcome.installed.contains(key))
78        .collect();
79    run.ledger
80        .replace_all(super::ledger_after(target, &live, plan, &failed))
81        .map_err(crate::engine::ExecuteError::ledger)?;
82    put_back?;
83
84    for wanted in &plan.unavailable {
85        run.watcher.log(&format!(
86            "{} could not be put back: the download it needs is no longer in the cache, \
87             so what is installed was left alone",
88            wanted.key
89        ));
90    }
91    Ok(outcome)
92}
93
94/// Installs each payload the snapshot names, stopping at the first failure.
95fn put_back_all(
96    target: &super::Snapshot,
97    plan: &super::Plan,
98    prepared: &Prepared,
99    run: &mut crate::engine::RunContext,
100    point: &str,
101    outcome: &mut crate::engine::Outcome,
102) -> Result<(), crate::engine::ExecuteError> {
103    let mut progress = crate::engine::Progress {
104        written: 0,
105        total: prepared.install_bytes,
106    };
107    for wanted in &plan.put_back {
108        let files = prepared
109            .files
110            .get(&wanted.key)
111            .map_or(&[][..], Vec::as_slice);
112        let group = target
113            .get(&wanted.key)
114            .and_then(|entry| entry.exclusive_group.as_deref());
115        let what = crate::engine::Installing {
116            key: &wanted.key,
117            sha256: &wanted.sha256,
118            version: &wanted.version,
119            exclusive_group: group,
120        };
121        crate::engine::install_package(run, &what, files, point, &mut progress)?;
122        outcome.installed.push(wanted.key.clone());
123    }
124    Ok(())
125}