Skip to main content

leenfetch_core/
system_info.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{config::settings::Config, core::Data, modules::enums::SongInfo};
4
5pub const PROTOCOL_VERSION: u32 = 1;
6
7/// Stable JSON-friendly representation of all collected system information.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SystemInfo {
10    pub protocol_version: u32,
11    pub username: Option<String>,
12    pub hostname: Option<String>,
13    pub host: Option<String>,
14    pub os: Option<String>,
15    pub distro: Option<String>,
16    pub model: Option<String>,
17    pub kernel: Option<String>,
18    pub os_age: Option<String>,
19    pub uptime: Option<String>,
20    pub packages: Option<String>,
21    pub shell: Option<String>,
22    pub wm: Option<String>,
23    pub de: Option<String>,
24    pub cpu: Option<String>,
25    pub gpu: Option<Vec<String>>,
26    pub memory: Option<String>,
27    pub disk: Option<Vec<(String, String)>>,
28    pub resolution: Option<String>,
29    pub theme: Option<String>,
30    pub battery: Option<Vec<String>>,
31    pub song: Option<SongInfo>,
32    pub colors: Option<String>,
33}
34
35/// Remote SSH payload containing the effective remote config and collected system data.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct RemoteSshPayload {
38    pub config: Config,
39    pub system_info: SystemInfo,
40}
41
42impl RemoteSshPayload {
43    pub fn new(config: Config, system_info: SystemInfo) -> Self {
44        Self {
45            config,
46            system_info,
47        }
48    }
49}
50
51impl From<Data> for SystemInfo {
52    fn from(value: Data) -> Self {
53        let host = match (&value.username, &value.hostname) {
54            (Some(user), Some(host)) => Some(format!("{user}@{host}")),
55            (_, Some(host)) => Some(host.clone()),
56            _ => None,
57        };
58
59        Self {
60            protocol_version: PROTOCOL_VERSION,
61            username: value.username,
62            hostname: value.hostname,
63            host,
64            os: value.os,
65            distro: value.distro,
66            model: value.model,
67            kernel: value.kernel,
68            os_age: value.os_age,
69            uptime: value.uptime,
70            packages: value.packages,
71            shell: value.shell,
72            wm: value.wm,
73            de: value.de,
74            cpu: value.cpu,
75            gpu: value.gpu,
76            memory: value.memory,
77            disk: value.disk,
78            resolution: value.resolution,
79            theme: value.theme,
80            battery: value.battery,
81            song: value.song,
82            colors: value.colors,
83        }
84    }
85}
86
87impl From<&SystemInfo> for Data {
88    fn from(value: &SystemInfo) -> Self {
89        Data {
90            username: value.username.clone(),
91            hostname: value.hostname.clone(),
92            os: value.os.clone(),
93            distro: value.distro.clone(),
94            model: value.model.clone(),
95            kernel: value.kernel.clone(),
96            os_age: value.os_age.clone(),
97            uptime: value.uptime.clone(),
98            packages: value.packages.clone(),
99            shell: value.shell.clone(),
100            wm: value.wm.clone(),
101            de: value.de.clone(),
102            cpu: value.cpu.clone(),
103            gpu: value.gpu.clone(),
104            memory: value.memory.clone(),
105            disk: value.disk.clone(),
106            resolution: value.resolution.clone(),
107            theme: value.theme.clone(),
108            battery: value.battery.clone(),
109            song: value.song.clone(),
110            colors: value.colors.clone(),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::config::default_config;
119
120    fn sample_system_info() -> SystemInfo {
121        SystemInfo {
122            protocol_version: PROTOCOL_VERSION,
123            username: Some("anakin".to_string()),
124            hostname: Some("tattooine".to_string()),
125            host: Some("anakin@tattooine".to_string()),
126            os: Some("Linux".to_string()),
127            distro: Some("Arch Linux".to_string()),
128            model: Some("X1".to_string()),
129            kernel: Some("6.0.0".to_string()),
130            os_age: None,
131            uptime: None,
132            packages: None,
133            shell: None,
134            wm: None,
135            de: None,
136            cpu: None,
137            gpu: None,
138            memory: None,
139            disk: None,
140            resolution: None,
141            theme: None,
142            battery: None,
143            song: None,
144            colors: None,
145        }
146    }
147
148    #[test]
149    fn remote_ssh_payload_round_trips() {
150        let payload = RemoteSshPayload::new(default_config(), sample_system_info());
151        let encoded = serde_json::to_string(&payload).unwrap();
152        let decoded: RemoteSshPayload = serde_json::from_str(&encoded).unwrap();
153
154        assert_eq!(decoded.system_info.username.as_deref(), Some("anakin"));
155        assert_eq!(decoded.system_info.hostname.as_deref(), Some("tattooine"));
156        assert!(!decoded.config.layout.is_empty());
157    }
158}