init: Introduce UnitStore

This will be necessary to store persistent information about units like
if they are currently started and which process is associated with them.
This commit is contained in:
bjorn3
2026-02-25 22:06:25 +01:00
parent 73cc0b0a07
commit 6fcb07e7dd
7 changed files with 179 additions and 77 deletions
+97 -14
View File
@@ -1,13 +1,71 @@
use std::path::Path;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::{fs, io};
use serde::Deserialize;
use crate::SwitchRoot;
use crate::script::Script;
use crate::service::Service;
pub struct UnitStore {
pub config_dirs: Vec<PathBuf>,
units: BTreeMap<UnitId, Unit>,
}
impl UnitStore {
pub fn new() -> Self {
UnitStore {
config_dirs: vec![],
units: BTreeMap::new(),
}
}
pub fn load_units(&mut self) -> (Vec<UnitId>, Vec<String>) {
let mut loaded_units = vec![];
let mut errors = vec![];
let entries = match config::config_for_dirs(&self.config_dirs) {
Ok(entries) => entries,
Err(err) => {
errors.push(format!(
"failed to read configs from {}: {err}",
self.config_dirs
.iter()
.map(|dir| dir.display().to_string())
.collect::<Vec<_>>()
.join(", ")
));
return (loaded_units, errors);
}
};
for entry in entries {
let (unit, new_errors) = match Unit::from_file(&entry) {
Ok(unit) => unit,
Err(err) => {
errors.push(format!("{}: {err}", entry.display()));
continue;
}
};
errors.extend(new_errors);
loaded_units.push(unit.id.clone());
self.units.insert(unit.id.clone(), unit);
}
(loaded_units, errors)
}
pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
self.units.get_mut(unit).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnitId(pub String);
pub struct Unit {
pub name: String,
pub id: UnitId,
pub info: UnitInfo,
pub kind: UnitKind,
@@ -15,17 +73,17 @@ pub struct Unit {
#[derive(Deserialize)]
pub struct UnitInfo {
pub description: Option<String>,
#[serde(default)]
description: String,
pub requires: Vec<String>,
#[serde(default)]
requires: Vec<String>,
#[serde(default)]
requires_weak: Vec<String>,
pub requires_weak: Vec<String>,
}
pub enum UnitKind {
LegacyScript { script: Script },
Service { service: Service },
SwitchRoot { switchroot: SwitchRoot },
}
#[derive(Deserialize)]
@@ -34,14 +92,22 @@ struct SerializedService {
service: Service,
}
#[derive(Deserialize)]
struct SerializedSwitchRoot {
unit: UnitInfo,
switchroot: SwitchRoot,
}
impl Unit {
pub fn from_file(config_path: &Path) -> io::Result<(Self, Vec<String>)> {
let name = config_path
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_owned();
let name = UnitId(
config_path
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_owned(),
);
let config = fs::read_to_string(config_path)?;
@@ -50,7 +116,7 @@ impl Unit {
let (script, warnings) = Script::from_str(&config)?;
(
UnitInfo {
description: "".to_owned(),
description: None,
requires: vec![],
requires_weak: vec![],
},
@@ -68,9 +134,26 @@ impl Unit {
vec![],
)
}
Some("switchroot") => {
let switchroot: SerializedSwitchRoot = serde_json::from_str(&config)?;
(
switchroot.unit,
UnitKind::SwitchRoot {
switchroot: switchroot.switchroot,
},
vec![],
)
}
Some(_) => return Err(io::Error::other("invalid file extension")),
};
Ok((Unit { name, info, kind }, errors))
Ok((
Unit {
id: name,
info,
kind,
},
errors,
))
}
}