winbrew_app\operations\repair/
restore.rs1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5
6use crate::core::{fs::cleanup_path, network::installer_filename, temp_workspace};
7use crate::engines;
8use crate::operations::install;
9
10use super::ResolvedFileRestoreTarget;
11
12pub fn restore_file_restore_target(
14 target: &ResolvedFileRestoreTarget,
15 target_paths: &[PathBuf],
16) -> Result<usize> {
17 let temp_root =
18 temp_workspace::build_temp_root(&target.package.name, &target.package.version.to_string());
19 cleanup_path(&temp_root)?;
20 fs::create_dir_all(&temp_root)?;
21
22 let result = (|| -> Result<usize> {
23 let stage_dir = temp_root.join("stage");
24 let client = install::download::build_client()?;
25 let download_path = temp_root.join(installer_filename(&target.installer.url));
26
27 install::download::download_installer(
28 &client,
29 &target.installer,
30 &download_path,
31 false,
32 |_| {},
33 |_| {},
34 )?;
35
36 let resolved_kind =
37 engines::probe_installer_from_download(&target.installer, &download_path)?;
38 let mut resolved_installer = target.installer.clone();
39 resolved_installer.kind = resolved_kind;
40 let engine = engines::resolve_engine_for_installer(&resolved_installer)?;
41
42 let _ = install::flow::execute_engine_install(
43 engine,
44 &resolved_installer,
45 &download_path,
46 &stage_dir,
47 &target.package.name,
48 )?;
49
50 restore_target_files(
51 &stage_dir,
52 Path::new(&target.installed_package.install_dir),
53 target_paths,
54 )
55 })();
56
57 let _ = cleanup_path(&temp_root);
58
59 result
60}
61
62pub(crate) fn restore_target_files(
63 stage_dir: &Path,
64 install_dir: &Path,
65 target_paths: &[PathBuf],
66) -> Result<usize> {
67 let mut restored = 0usize;
68
69 for target_path in target_paths {
70 let relative_path = target_path.strip_prefix(install_dir).with_context(|| {
71 format!(
72 "failed to derive restored file path for {} from {}",
73 target_path.display(),
74 install_dir.display()
75 )
76 })?;
77 let source_path = stage_dir.join(relative_path);
78
79 if let Some(parent) = target_path.parent() {
80 fs::create_dir_all(parent).with_context(|| {
81 format!(
82 "failed to prepare parent directory for {}",
83 target_path.display()
84 )
85 })?;
86 }
87
88 fs::copy(&source_path, target_path).with_context(|| {
89 format!(
90 "failed to restore file {} from staged package",
91 target_path.display()
92 )
93 })?;
94
95 restored += 1;
96 }
97
98 Ok(restored)
99}
100
101#[cfg(test)]
102mod tests {
103 use super::restore_target_files;
104 use anyhow::Result;
105 use std::fs;
106 use tempfile::tempdir;
107
108 #[test]
109 fn restore_target_files_copies_staged_content() -> Result<()> {
110 let root = tempdir().expect("temp dir");
111 let stage_dir = root.path().join("stage");
112 let install_dir = root.path().join("packages").join("Contoso.App");
113 let target_path = install_dir.join("bin").join("tool.exe");
114 let staged_path = stage_dir.join("bin").join("tool.exe");
115
116 fs::create_dir_all(staged_path.parent().expect("stage parent")).expect("stage dir");
117 fs::create_dir_all(target_path.parent().expect("target parent")).expect("target dir");
118 fs::write(&staged_path, b"restored-binary").expect("write staged file");
119
120 let restored =
121 restore_target_files(&stage_dir, &install_dir, std::slice::from_ref(&target_path))?;
122
123 assert_eq!(restored, 1);
124 assert_eq!(
125 fs::read(&target_path).expect("read target"),
126 b"restored-binary"
127 );
128
129 Ok(())
130 }
131}