horto_os_ui_shared/ops/
doctor.rs1use crate::context::{is_root, HostContext};
4use crate::kits::docker;
5use serde::{Deserialize, Serialize};
6
7#[allow(clippy::struct_excessive_bools)]
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DoctorFlags {
11 pub is_root: bool,
13 pub has_sudo: bool,
15 pub docker_present: bool,
17 pub active_setup_dir: bool,
19 pub full_env: bool,
21 pub minimal_env: bool,
23 pub docker_dir: bool,
25 pub backup_dir: bool,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct DoctorReport {
32 #[serde(flatten)]
34 pub flags: DoctorFlags,
35 pub notes: Vec<String>,
37}
38
39impl std::ops::Deref for DoctorReport {
40 type Target = DoctorFlags;
41
42 fn deref(&self) -> &Self::Target {
43 &self.flags
44 }
45}
46
47#[must_use]
49pub fn doctor(ctx: &HostContext) -> DoctorReport {
50 let mut notes = Vec::new();
51 let root = is_root();
52 if !root {
53 notes.push("Not running as root; apply mode needs sudo.".into());
54 }
55 let has_sudo = which::which("sudo").is_ok();
56 let docker_present = docker::docker_available();
57 if !docker_present {
58 notes.push("docker binary not found on PATH.".into());
59 }
60 let active_setup_dir = ctx.paths.active_setup.is_dir();
61 let full_env = ctx.paths.full_env_file().is_file();
62 let minimal_env = ctx.paths.minimal_env_file().is_file();
63 let os_conf = ctx.paths.os_configuration_file().is_file();
64 let docker_dir = ctx.paths.docker.is_dir();
65 let backup_dir = ctx.paths.backup.is_dir();
66 if !full_env && !minimal_env && !os_conf {
67 notes.push(
68 "No active env yet (os-configuration.env, iot-lan_conf.env, or minimal_setup_vars.env)."
69 .into(),
70 );
71 }
72 DoctorReport {
73 flags: DoctorFlags {
74 is_root: root,
75 has_sudo,
76 docker_present,
77 active_setup_dir,
78 full_env,
79 minimal_env,
80 docker_dir,
81 backup_dir,
82 },
83 notes,
84 }
85}