Skip to main content

capsula_capture_machine/
lib.rs

1mod error;
2
3use crate::error::MachineHookError;
4use capsula_core::captured::Captured;
5use capsula_core::error::CapsulaResult;
6use capsula_core::hook::{Hook, PhaseMarker, RuntimeParams};
7use capsula_core::run::PreparedRun;
8use serde::{Deserialize, Serialize};
9use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};
10use tracing::debug;
11
12#[derive(Debug, Clone, Deserialize, Serialize, Default)]
13#[serde(deny_unknown_fields)]
14pub struct MachineHookConfig {}
15
16#[derive(Debug, Default)]
17pub struct MachineHook {
18    config: MachineHookConfig,
19}
20
21#[derive(Debug, Serialize)]
22pub struct CpuInfo {
23    name: String,
24    vender_id: String,
25    brand: String,
26    frequency_mhz: u64,
27}
28
29#[derive(Debug, Serialize)]
30pub struct MachineCaptured {
31    os: String,
32    os_version: String,
33    kernel_version: String,
34    architecture: String,
35    cpus: Vec<CpuInfo>,
36    total_memory: u64,
37    hostname: String,
38}
39
40impl<P> Hook<P> for MachineHook
41where
42    P: PhaseMarker,
43{
44    const ID: &'static str = "capture-machine";
45
46    type Config = MachineHookConfig;
47    type Output = MachineCaptured;
48
49    fn from_config(
50        config: &serde_json::Value,
51        _project_root: &std::path::Path,
52    ) -> CapsulaResult<Self> {
53        let config: MachineHookConfig = serde_json::from_value(config.clone())?;
54        Ok(Self { config })
55    }
56
57    fn config(&self) -> &Self::Config {
58        &self.config
59    }
60
61    fn run(
62        &self,
63        _metadata: &PreparedRun,
64        _params: &RuntimeParams<P>,
65    ) -> CapsulaResult<Self::Output> {
66        debug!("MachineHook: Gathering system information");
67        let os = System::name().ok_or(MachineHookError::OsInfoError)?;
68        let os_version = System::os_version().ok_or(MachineHookError::OsInfoError)?;
69        let kernel_version = System::kernel_version().ok_or(MachineHookError::OsInfoError)?;
70        let architecture = std::env::consts::ARCH.to_string();
71        debug!(
72            "MachineHook: OS: {} {}, Kernel: {}, Arch: {}",
73            os, os_version, kernel_version, architecture
74        );
75
76        debug!("MachineHook: Querying CPU and memory information");
77        let system = System::new_with_specifics(
78            RefreshKind::nothing()
79                .with_cpu(CpuRefreshKind::nothing().with_frequency())
80                .with_memory(MemoryRefreshKind::nothing().with_ram()),
81        );
82        let cpus = system
83            .cpus()
84            .iter()
85            .map(|cpu| CpuInfo {
86                name: cpu.name().to_string(),
87                vender_id: cpu.vendor_id().to_string(),
88                brand: cpu.brand().to_string(),
89                frequency_mhz: cpu.frequency(),
90            })
91            .collect::<Vec<_>>();
92
93        let total_memory = system.total_memory();
94        let hostname = System::host_name().ok_or(MachineHookError::HostnameError)?;
95        debug!(
96            "MachineHook: Found {} CPUs, {} bytes total memory, hostname: {}",
97            cpus.len(),
98            total_memory,
99            hostname
100        );
101
102        Ok(MachineCaptured {
103            os,
104            os_version,
105            kernel_version,
106            architecture,
107            cpus,
108            total_memory,
109            hostname,
110        })
111    }
112}
113
114impl Captured for MachineCaptured {
115    fn serialize_json(&self) -> Result<serde_json::Value, serde_json::Error> {
116        serde_json::to_value(self)
117    }
118}