winbrew_engines/
lib.rs

1//! Engine dispatch and platform-specific installers for WinBrew.
2//!
3//! `winbrew-engines` maps installer metadata to execution backends and owns
4//! the archive, portable, and Windows-specific install/remove implementations.
5//! The crate stays focused on engine selection, engine receipts, and platform
6//! adapters so the app layer can orchestrate without embedding OS details.
7
8mod payload;
9mod registry;
10
11pub mod archive;
12pub mod portable;
13pub mod windows;
14
15pub(crate) use winbrew_core as core;
16pub(crate) use winbrew_models as models;
17#[cfg(windows)]
18pub(crate) use winbrew_windows as windows_dep;
19
20use anyhow::Result;
21use std::path::Path;
22
23pub use crate::models::install::engine::{EngineInstallReceipt, EngineKind};
24pub use crate::models::shared::DeploymentKind;
25
26use crate::models::catalog::package::CatalogInstaller;
27use crate::models::install::installed::InstalledPackage;
28use crate::models::install::installer::InstallerType;
29
30pub trait PackageEngine {
31    fn install(
32        &self,
33        installer: &CatalogInstaller,
34        download_path: &Path,
35        install_dir: &Path,
36        package_name: &str,
37    ) -> Result<EngineInstallReceipt>;
38
39    fn remove(&self, package: &InstalledPackage) -> Result<()>;
40}
41
42pub fn resolve_engine_for_installer(installer: &CatalogInstaller) -> Result<EngineKind> {
43    registry::resolve_engine_kind_for_installer(installer)
44}
45
46pub fn probe_installer_from_download(
47    installer: &CatalogInstaller,
48    download_path: &Path,
49) -> Result<InstallerType> {
50    registry::probe_installer_from_download(installer, download_path)
51}
52
53pub fn resolve_deployment_kind(installer: &CatalogInstaller) -> DeploymentKind {
54    registry::resolve_deployment_kind(installer)
55}
56
57impl PackageEngine for EngineKind {
58    fn install(
59        &self,
60        installer: &CatalogInstaller,
61        download_path: &Path,
62        install_dir: &Path,
63        package_name: &str,
64    ) -> Result<EngineInstallReceipt> {
65        registry::install(*self, installer, download_path, install_dir, package_name)
66    }
67
68    fn remove(&self, package: &InstalledPackage) -> Result<()> {
69        registry::remove(*self, package)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::{DeploymentKind, EngineKind, resolve_deployment_kind};
76    use crate::models::catalog::package::CatalogInstaller;
77    use crate::models::install::installer::InstallerType;
78    use winbrew_testing::{CatalogInstallerBuilderExt as _, catalog_installer};
79
80    fn installer(kind: InstallerType, nested_kind: Option<InstallerType>) -> CatalogInstaller {
81        let installer = catalog_installer("Contoso.App".into(), "https://example.invalid/app.zip")
82            .with_kind(kind);
83
84        match nested_kind {
85            Some(nested_kind) => installer.with_nested(nested_kind),
86            None => installer,
87        }
88    }
89
90    #[test]
91    fn engine_kind_from_installer_type_maps_supported_types() {
92        assert_eq!(
93            EngineKind::from_installer_type(InstallerType::Msi),
94            EngineKind::Msi
95        );
96        assert_eq!(
97            EngineKind::from_installer_type(InstallerType::Appx),
98            EngineKind::Msix
99        );
100        assert_eq!(
101            EngineKind::from_installer_type(InstallerType::Msix),
102            EngineKind::Msix
103        );
104        assert_eq!(
105            EngineKind::from_installer_type(InstallerType::Wix),
106            EngineKind::Msi
107        );
108        assert_eq!(
109            EngineKind::from_installer_type(InstallerType::Zip),
110            EngineKind::Zip
111        );
112        assert_eq!(
113            EngineKind::from_installer_type(InstallerType::Portable),
114            EngineKind::Portable
115        );
116        assert_eq!(
117            EngineKind::from_installer_type(InstallerType::Exe),
118            EngineKind::NativeExe
119        );
120        assert_eq!(
121            EngineKind::from_installer_type(InstallerType::Font),
122            EngineKind::Font
123        );
124    }
125
126    #[test]
127    fn engine_kind_for_type_recognizes_native_exe_family() {
128        for kind in [
129            InstallerType::Inno,
130            InstallerType::Nullsoft,
131            InstallerType::Burn,
132        ] {
133            assert_eq!(EngineKind::from_installer_type(kind), EngineKind::NativeExe);
134        }
135    }
136
137    #[test]
138    fn resolve_deployment_kind_uses_nested_installer_type_for_archives() {
139        let installer = installer(InstallerType::Zip, Some(InstallerType::Msi));
140
141        assert_eq!(
142            resolve_deployment_kind(&installer),
143            DeploymentKind::Installed
144        );
145    }
146
147    #[test]
148    fn resolve_deployment_kind_defaults_native_exe_family_to_installed() {
149        for kind in [
150            InstallerType::Exe,
151            InstallerType::Inno,
152            InstallerType::Nullsoft,
153            InstallerType::Burn,
154        ] {
155            let installer = installer(kind, None);
156
157            assert_eq!(
158                resolve_deployment_kind(&installer),
159                DeploymentKind::Installed
160            );
161        }
162    }
163}