Unit loading
continuous-integration/drone/push Build is passing Details

master
eater 4 years ago
parent c93ad87d61
commit fa1e398f0a
Signed by: eater
GPG Key ID: AD2560A0F84F0759

@ -1,6 +1,9 @@
extern crate sysf;
use sysf::unit::Unit;
fn main() {
fn main() {
let x = Unit::new("hello");
println!("{:?}", x);
}

@ -2,5 +2,4 @@
name = "sysf"
version = "0.1.0"
authors = ["eater <=@eater.me>"]
edition = "2018"
crate-type = "dylib"
edition = "2018"

@ -1,6 +1,7 @@
use std::collections::HashMap;
use crate::config::parser::{ParserError, parse_config};
use crate::time::{parse_finite_time_span, FiniteTimeSpan, TimeSpan, parse_time_span};
use crate::unit::Unit;
pub mod parser;
@ -21,8 +22,16 @@ impl Config {
id
}
pub fn load_file(&mut self, file_name: String, file_contents: String) -> Result<(), ParserError> {
parse_config(self, file_name, file_contents)
pub fn section(&self, name: &str) -> Option<&Section> {
self.sections.get(name)
}
pub fn load_file(&mut self, file_name: &str, file_contents: &str) -> Result<(), ParserError> {
parse_config(self, file_name.to_string(), file_contents.to_string())
}
pub fn get_unit(&self) -> Option<Unit> {
Unit::load_from_config(&self.name, self)
}
pub fn new(name: &str) -> Config {
@ -73,6 +82,26 @@ impl Section {
pub fn get_time_span(&self, name: &str) -> Option<TimeSpan> {
self.entries.get(name).and_then(|x| x.get_time_span())
}
pub fn get_space_separated_list(&self, name: &str) -> Vec<&str> {
self
.get_list(name)
.unwrap_or(vec![])
.iter()
.flat_map(|x| x.split(" "))
.filter(|x| x.len() > 0)
.collect()
}
pub fn get_space_separated_string(&self, name: &str) -> Vec<&str> {
self
.get_string(name)
.unwrap_or("")
.trim()
.split(" ")
.filter(|x| x.len() > 0)
.collect()
}
}
#[derive(Clone, Debug, Default)]

@ -3,7 +3,7 @@ use crate::time::TimeSpan::{Infinite, Finite};
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum TimeSpan {
Infinite,
Finite(FiniteTimeSpan)
Finite(FiniteTimeSpan),
}
#[derive(Default, Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]

@ -1,9 +1,15 @@
use crate::unit::JobMode::Replace;
use crate::time::{TimeSpan, FiniteTimeSpan};
use crate::time::TimeSpan::Infinite;
use crate::unit::UnitState::Stopped;
use crate::config::Config;
use crate::unit::JobMode::{Fail, ReplaceIrreversibly, Isolate, Flush, IgnoreDependencies, IgnoreRequirements};
use crate::unit::CollectMode::{Inactive, InactiveOrFailed};
#[allow(dead_code)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
pub struct Unit {
name: String,
state: UnitState,
description: String,
// Space only
@ -43,8 +49,8 @@ pub struct Unit {
collect_mode: CollectMode,
failure_action: UnitAction,
success_action: UnitAction,
failure_action_exit_status: u8,
success_action_exit_status: u8,
failure_action_exit_status: Option<u8>,
success_action_exit_status: Option<u8>,
job_timeout_sec: TimeSpan,
job_running_timeout_sec: TimeSpan,
job_timeout_action: UnitAction,
@ -56,9 +62,192 @@ pub struct Unit {
source_path: Option<String>,
}
impl Unit {
pub fn new(name: &str) -> Unit {
let mut unit = Unit::default();
unit.name = name.to_string();
unit
}
// RsBorrowCheck says unit is moved, even though it isn't
//noinspection RsBorrowChecker
pub(crate) fn load_from_config(name: &str, config: &Config) -> Option<Unit> {
let section = config.section("Unit")?;
let mut unit = Unit::new(name);
unit.description = section
.get_string("Description")
.map(str::to_string)
.unwrap_or(unit.description);
unit.documentation = section
.get_space_separated_string("Documentation")
.to_string();
unit.wants = section
.get_space_separated_list("Wants")
.to_string();
unit.requires = section
.get_space_separated_list("Requirers")
.to_string();
unit.requisite = section
.get_space_separated_list("Requisite")
.to_string();
unit.binds_to = section
.get_space_separated_list("BindsTo")
.to_string();
unit.part_of = section
.get_space_separated_list("PartOf")
.to_string();
unit.conflicts = section
.get_space_separated_string("Conflicts")
.to_string();
unit.before = section
.get_space_separated_list("Before")
.to_string();
unit.after = section
.get_space_separated_list("After")
.to_string();
unit.on_failure = section
.get_space_separated_string("OnFailure")
.to_string();
unit.propagates_reload_to = section
.get_space_separated_list("PropagatesReloadTo")
.to_string();
unit.propagates_reload_from = section
.get_space_separated_list("PropagatesReloadFrom")
.to_string();
unit.joins_namespace_of = section
.get_space_separated_list("JoinsNamespaceOf")
.to_string();
unit.requires_mounts_for = section
.get_space_separated_list("RequiresMountsFor")
.to_string();
unit.on_failure_job_mode = section
.get_string("OnFailureJobMode")
.and_then(JobMode::from_str)
.unwrap_or(unit.on_failure_job_mode);
unit.ignore_on_isolate = section
.get_boolean("IgnoreOnIsolate")
.unwrap_or(unit.ignore_on_isolate);
unit.stop_when_unneeded = section
.get_boolean("StopWhenUnneeded")
.unwrap_or(unit.stop_when_unneeded);
unit.refuse_manual_start = section
.get_boolean("RefuseManualStart")
.unwrap_or(unit.refuse_manual_start);
unit.refuse_manual_stop = section
.get_boolean("RefuseManualStop")
.unwrap_or(unit.refuse_manual_stop);
unit.allow_isolate = section
.get_boolean("AllowIsolate")
.unwrap_or(unit.allow_isolate);
unit.default_dependencies = section
.get_boolean("DefaultDependencies")
.unwrap_or(unit.default_dependencies);
unit.collect_mode = section
.get_string("CollectMode")
.and_then(CollectMode::from_str)
.unwrap_or(unit.collect_mode);
unit.failure_action = section
.get_string("FailureAction")
.and_then(UnitAction::from_str)
.unwrap_or(unit.failure_action);
unit.success_action = section
.get_string("SuccessAction")
.and_then(UnitAction::from_str)
.unwrap_or(unit.success_action);
unit.failure_action_exit_status = section
.get_string("FailureActionExitStatus")
.and_then(|x| x.parse::<u8>().ok())
.or(unit.failure_action_exit_status);
unit.success_action_exit_status = section
.get_string("SuccessActionExitStatus")
.and_then(|x| x.parse::<u8>().ok())
.or(unit.success_action_exit_status);
unit.job_timeout_sec = section
.get_time_span("JobTimeoutSec")
.unwrap_or(unit.job_timeout_sec);
unit.job_running_timeout_sec = section
.get_time_span("JobRunningTimeoutSec")
.unwrap_or(unit.job_running_timeout_sec);
unit.job_timeout_action = section
.get_string("JobTimeoutAction")
.and_then(UnitAction::from_str)
.unwrap_or(unit.job_timeout_action);
unit.job_timeout_reboot_argument = section
.get_string("JobTimeoutRebootArgument")
.map(str::to_string);
unit.start_limit_interval_sec = section
.get_finite_time_span("StartLimitIntervalSec")
.unwrap_or(unit.start_limit_interval_sec);
unit.start_limit_burst = section
.get_string("StartLimitBurst")
.and_then(|x| x.parse::<usize>().ok())
.unwrap_or(unit.start_limit_burst);
unit.start_limit_action = section
.get_string("StartLimitAction")
.and_then(UnitAction::from_str)
.unwrap_or(unit.start_limit_action);
unit.reboot_argument = section
.get_string("RebootArgument")
.map(str::to_string);
unit.source_path = section
.get_string("SourcePath")
.map(str::to_string);
Some(unit)
}
}
trait StringFromStr {
fn to_string(&self) -> Vec<String>;
}
impl StringFromStr for Vec<&str> {
fn to_string(&self) -> Vec<String> {
self.iter().map(|x| x.to_string()).collect()
}
}
impl Default for Unit {
fn default() -> Self {
Unit {
name: "".to_string(),
state: Stopped,
description: "".to_string(),
documentation: vec![],
wants: vec![],
@ -84,8 +273,8 @@ impl Default for Unit {
collect_mode: CollectMode::Inactive,
failure_action: UnitAction::None,
success_action: UnitAction::None,
failure_action_exit_status: 0,
success_action_exit_status: 0,
failure_action_exit_status: None,
success_action_exit_status: None,
job_timeout_sec: Infinite,
job_running_timeout_sec: Infinite,
job_timeout_action: UnitAction::None,
@ -99,7 +288,15 @@ impl Default for Unit {
}
}
enum JobMode {
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
pub enum UnitState {
Stopped,
Failed,
Running,
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
pub enum JobMode {
Fail,
Replace,
ReplaceIrreversibly,
@ -109,11 +306,38 @@ enum JobMode {
IgnoreRequirements,
}
enum CollectMode {
impl JobMode {
fn from_str(value: &str) -> Option<JobMode> {
Some(match value.trim().to_ascii_lowercase().as_str() {
"fail" => Fail,
"replace" => Replace,
"replace-irreversibly" => ReplaceIrreversibly,
"isolate" => Isolate,
"flush" => Flush,
"ignore-dependencies" => IgnoreDependencies,
"ignore-requirements" => IgnoreRequirements,
_ => return None
})
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
pub enum CollectMode {
Inactive,
InactiveOrFailed,
}
impl CollectMode {
pub fn from_str(value: &str) -> Option<CollectMode> {
Some(match value.trim().to_ascii_lowercase().as_str() {
"inactive" => Inactive,
"inactive-or-failed" => InactiveOrFailed,
_ => return None
})
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
enum UnitAction {
None,
Reboot(UnitActionSeverity),
@ -121,8 +345,67 @@ enum UnitAction {
Exit(UnitActionSeverity),
}
enum UnitActionSeverity {
impl UnitAction {
pub fn from_str(value: &str) -> Option<UnitAction> {
Some(match value.trim().to_ascii_lowercase().as_str() {
"none" => UnitAction::None,
"reboot" => UnitAction::Reboot(UnitActionSeverity::None),
"reboot-force" => UnitAction::Reboot(UnitActionSeverity::Force),
"reboot-immediate" => UnitAction::Reboot(UnitActionSeverity::Immediate),
"poweroff" => UnitAction::PowerOff(UnitActionSeverity::None),
"poweroff-force" => UnitAction::PowerOff(UnitActionSeverity::Force),
"poweroff-immediate" => UnitAction::PowerOff(UnitActionSeverity::Immediate),
"exit" => UnitAction::Exit(UnitActionSeverity::None),
"exit-force" => UnitAction::Exit(UnitActionSeverity::Force),
_ => return None
})
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
pub enum UnitActionSeverity {
None,
Force,
Immediate,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_unit_parsing() {
let mut config = Config::new("test.unit");
let res = config.load_file("/test.unit", r"
[Unit]
Description=It's just a test.
Documentation=none :)
Documentation=man:test https://example.com
Before=sysinit.target multi-user.target
Before=ye.target
Before=
Before=ohno.target
After=time.target
After=myself.target you.target
FailureAction=reboot-immediate
CollectMode=inactive-or-failed
FailureActionExitStatus=1
SuccessActionExitStatus=256
");
assert!(res.is_ok());
let unit = config.get_unit();
assert!(unit.is_some());
let unit = unit.unwrap();
assert_eq!(unit.description, "It's just a test.");
assert_eq!(unit.documentation, vec!["man:test", "https://example.com"]);
assert_eq!(unit.before, vec!["ohno.target"]);
assert_eq!(unit.after, vec!["time.target", "myself.target", "you.target"]);
assert_eq!(unit.failure_action, UnitAction::Reboot(UnitActionSeverity::Immediate));
assert_eq!(unit.collect_mode, CollectMode::InactiveOrFailed);
assert_eq!(unit.success_action_exit_status, None);
assert_eq!(unit.failure_action_exit_status, Some(1));
}
}
Loading…
Cancel
Save