Skip to main content

lilo_rm_core/
isolation.rs

1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
8#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
9pub enum IsolationPolicy {
10    #[default]
11    Host,
12    Docker(IsolationProfile),
13}
14
15impl IsolationPolicy {
16    pub fn is_host(&self) -> bool {
17        matches!(self, Self::Host)
18    }
19}
20
21impl Display for IsolationPolicy {
22    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
23        match self {
24            Self::Host => formatter.write_str("host"),
25            Self::Docker(profile) => {
26                formatter.write_str("docker")?;
27                if let Some(name) = &profile.name {
28                    write!(formatter, ":{name}")?;
29                }
30                Ok(())
31            }
32        }
33    }
34}
35
36impl FromStr for IsolationPolicy {
37    type Err = IsolationPolicyParseError;
38
39    fn from_str(value: &str) -> Result<Self, Self::Err> {
40        match value {
41            "host" => Ok(Self::Host),
42            "docker" => Ok(Self::Docker(IsolationProfile::default())),
43            _ => parse_docker_profile(value),
44        }
45    }
46}
47
48fn parse_docker_profile(value: &str) -> Result<IsolationPolicy, IsolationPolicyParseError> {
49    let Some(profile) = value.strip_prefix("docker:") else {
50        return Err(IsolationPolicyParseError(value.to_owned()));
51    };
52    if profile.is_empty() {
53        return Err(IsolationPolicyParseError(value.to_owned()));
54    }
55    Ok(IsolationPolicy::Docker(IsolationProfile {
56        name: Some(profile.to_owned()),
57    }))
58}
59
60#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
61pub struct IsolationProfile {
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub name: Option<String>,
64}
65
66#[derive(Debug, Error)]
67#[error("invalid isolation policy {0}; expected host, docker, or docker:PROFILE")]
68pub struct IsolationPolicyParseError(pub String);
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn isolation_policy_parses_host_and_docker_profiles() {
76        assert_eq!(
77            "host".parse::<IsolationPolicy>().unwrap(),
78            IsolationPolicy::Host
79        );
80        assert_eq!(
81            "docker".parse::<IsolationPolicy>().unwrap(),
82            IsolationPolicy::Docker(IsolationProfile { name: None })
83        );
84        assert_eq!(
85            "docker:locked".parse::<IsolationPolicy>().unwrap(),
86            IsolationPolicy::Docker(IsolationProfile {
87                name: Some("locked".to_owned())
88            })
89        );
90    }
91
92    #[test]
93    fn isolation_policy_rejects_unknown_or_empty_profiles() {
94        for value in ["", "sandbox", "docker:"] {
95            assert!(
96                value.parse::<IsolationPolicy>().is_err(),
97                "accepted invalid isolation policy {value}"
98            );
99        }
100    }
101}