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