Skip to main content

rusty_stack/installers/common/
distro.rs

1//! Thin facade over `platform::detection` for installer use.
2//!
3//! This module delegates all distro detection to `platform::detection::detect_distribution()`
4//! and does NOT duplicate `/etc/os-release` parsing.
5//!
6//! # Validation Assertions
7//!
8//! - **VAL-INFRA-002**: Distro detection delegates to platform::detection
9//! - **VAL-INFRA-003**: Distro family classification correct
10
11use crate::platform::detection::{detect_distribution, DistroFamily, DistroInfo, PackageManager};
12
13/// Thin facade that wraps `platform::detection` for installer use.
14///
15/// Provides convenience methods for the installer layer without duplicating
16/// any detection logic.
17#[derive(Debug, Clone)]
18pub struct DistroFacade {
19    info: DistroInfo,
20}
21
22impl DistroFacade {
23    /// Detect the current distribution by delegating to `platform::detection`.
24    ///
25    /// This is the primary constructor and performs no duplicate parsing.
26    pub fn detect() -> Self {
27        Self {
28            info: detect_distribution(),
29        }
30    }
31
32    /// Create a facade from a pre-built `DistroInfo` (useful for testing).
33    pub fn from_info(info: DistroInfo) -> Self {
34        Self { info }
35    }
36
37    /// Get the distribution ID (e.g., "ubuntu", "arch").
38    pub fn id(&self) -> &str {
39        &self.info.id
40    }
41
42    /// Get the full distribution name (e.g., "Ubuntu 24.04 LTS").
43    pub fn name(&self) -> &str {
44        &self.info.name
45    }
46
47    /// Get the distribution version (e.g., "24.04", "rolling").
48    pub fn version(&self) -> &str {
49        &self.info.version
50    }
51
52    /// Get the distribution codename (e.g., "noble").
53    pub fn codename(&self) -> &str {
54        &self.info.codename
55    }
56
57    /// Get the distribution family classification.
58    pub fn family(&self) -> DistroFamily {
59        self.info.family
60    }
61
62    /// Get the detected package manager.
63    pub fn package_manager(&self) -> PackageManager {
64        self.info.pkg_manager
65    }
66
67    /// Get the ID_LIKE field from os-release.
68    pub fn id_like(&self) -> &str {
69        &self.info.id_like
70    }
71
72    /// Get the underlying `DistroInfo`.
73    pub fn info(&self) -> &DistroInfo {
74        &self.info
75    }
76
77    /// Check if this is a Debian-family distro.
78    pub fn is_debian_family(&self) -> bool {
79        self.info.family == DistroFamily::Debian
80    }
81
82    /// Check if this is an Arch-family distro.
83    pub fn is_arch_family(&self) -> bool {
84        self.info.family == DistroFamily::Arch
85    }
86
87    /// Check if this is a RHEL-family distro.
88    pub fn is_rhel_family(&self) -> bool {
89        self.info.family == DistroFamily::Rhel
90    }
91
92    /// Check if this is a SUSE-family distro.
93    pub fn is_suse_family(&self) -> bool {
94        self.info.family == DistroFamily::Suse
95    }
96
97    /// Check if the distro uses apt.
98    pub fn uses_apt(&self) -> bool {
99        self.info.pkg_manager == PackageManager::Apt
100    }
101
102    /// Check if the distro uses pacman.
103    pub fn uses_pacman(&self) -> bool {
104        self.info.pkg_manager == PackageManager::Pacman
105    }
106
107    /// Check if the distro uses dnf.
108    pub fn uses_dnf(&self) -> bool {
109        self.info.pkg_manager == PackageManager::Dnf
110    }
111
112    /// Check if the distro uses yum.
113    pub fn uses_yum(&self) -> bool {
114        self.info.pkg_manager == PackageManager::Yum
115    }
116
117    /// Check if the distro uses zypper.
118    pub fn uses_zypper(&self) -> bool {
119        self.info.pkg_manager == PackageManager::Zypper
120    }
121}
122
123impl Default for DistroFacade {
124    fn default() -> Self {
125        Self::detect()
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    /// VAL-INFRA-002: Distro detection delegates to platform::detection
134    #[test]
135    fn test_distro_facade_delegates_to_platform() {
136        let facade = DistroFacade::detect();
137        // Should return non-empty id on a real system
138        assert!(
139            !facade.id().is_empty(),
140            "DistroFacade should delegate to platform::detection"
141        );
142    }
143
144    /// VAL-INFRA-002: No duplicate /etc/os-release parsing
145    #[test]
146    fn test_distro_facade_from_info() {
147        let info = DistroInfo {
148            id: "ubuntu".to_string(),
149            name: "Ubuntu 24.04".to_string(),
150            version: "24.04".to_string(),
151            codename: "noble".to_string(),
152            family: DistroFamily::Debian,
153            pkg_manager: PackageManager::Apt,
154            id_like: "debian".to_string(),
155        };
156        let facade = DistroFacade::from_info(info.clone());
157        assert_eq!(facade.id(), "ubuntu");
158        assert_eq!(facade.family(), DistroFamily::Debian);
159        assert_eq!(facade.package_manager(), PackageManager::Apt);
160        assert_eq!(facade.info(), &info);
161    }
162
163    /// VAL-INFRA-003: Distro family classification correct
164    #[test]
165    fn test_family_classification_debian() {
166        let info = DistroInfo {
167            id: "ubuntu".to_string(),
168            family: DistroFamily::Debian,
169            pkg_manager: PackageManager::Apt,
170            ..Default::default()
171        };
172        let facade = DistroFacade::from_info(info);
173        assert!(facade.is_debian_family());
174        assert!(!facade.is_arch_family());
175        assert!(facade.uses_apt());
176    }
177
178    #[test]
179    fn test_family_classification_arch() {
180        let info = DistroInfo {
181            id: "arch".to_string(),
182            family: DistroFamily::Arch,
183            pkg_manager: PackageManager::Pacman,
184            ..Default::default()
185        };
186        let facade = DistroFacade::from_info(info);
187        assert!(facade.is_arch_family());
188        assert!(!facade.is_debian_family());
189        assert!(facade.uses_pacman());
190    }
191
192    #[test]
193    fn test_family_classification_rhel() {
194        let info = DistroInfo {
195            id: "fedora".to_string(),
196            family: DistroFamily::Rhel,
197            pkg_manager: PackageManager::Dnf,
198            ..Default::default()
199        };
200        let facade = DistroFacade::from_info(info);
201        assert!(facade.is_rhel_family());
202        assert!(facade.uses_dnf());
203    }
204
205    #[test]
206    fn test_family_classification_suse() {
207        let info = DistroInfo {
208            id: "opensuse-leap".to_string(),
209            family: DistroFamily::Suse,
210            pkg_manager: PackageManager::Zypper,
211            ..Default::default()
212        };
213        let facade = DistroFacade::from_info(info);
214        assert!(facade.is_suse_family());
215        assert!(facade.uses_zypper());
216    }
217
218    #[test]
219    fn test_family_classification_yum() {
220        let info = DistroInfo {
221            id: "centos".to_string(),
222            family: DistroFamily::Rhel,
223            pkg_manager: PackageManager::Yum,
224            ..Default::default()
225        };
226        let facade = DistroFacade::from_info(info);
227        assert!(facade.is_rhel_family());
228        assert!(facade.uses_yum());
229    }
230
231    #[test]
232    fn test_default_impl() {
233        let facade = DistroFacade::default();
234        assert!(!facade.id().is_empty());
235    }
236
237    #[test]
238    fn test_accessor_methods() {
239        let info = DistroInfo {
240            id: "ubuntu".to_string(),
241            name: "Ubuntu 24.04 LTS".to_string(),
242            version: "24.04".to_string(),
243            codename: "noble".to_string(),
244            family: DistroFamily::Debian,
245            pkg_manager: PackageManager::Apt,
246            id_like: "debian".to_string(),
247        };
248        let facade = DistroFacade::from_info(info);
249        assert_eq!(facade.name(), "Ubuntu 24.04 LTS");
250        assert_eq!(facade.version(), "24.04");
251        assert_eq!(facade.codename(), "noble");
252        assert_eq!(facade.id_like(), "debian");
253    }
254}