use std::collections::BTreeMap as Map; pub const TAKE_ALL: i16 = -1; #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct Config { pub anti_phishing_code: String, #[serde(skip_serializing_if = "Option::is_none")] pub keymap: Option, #[serde(skip_serializing_if = "Option::is_none")] pub modules: Option, #[serde(skip_serializing_if = "Option::is_none")] pub resolv_conf: Option, #[serde(default)] pub vpns: Map, pub networks: Vec, pub auths: Vec, #[serde(default)] pub ssh: SSHServer, #[serde(default)] pub pre_lvm_crypt: Vec, #[serde(default)] pub lvm: Vec, #[serde(default)] pub crypt: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub signer_public_key: Option, pub bootstrap: Bootstrap, } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct Auth { pub name: String, #[serde(alias = "sshKey")] #[serde(skip_serializing_if = "Option::is_none")] pub ssh_key: Option, #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct Network { pub name: String, pub interfaces: Vec, pub script: String, } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct NetworkInterface { pub var: String, pub n: i16, #[serde(skip_serializing_if = "Vec::is_empty")] pub regexps: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub filter: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct SSHServer { pub listen: String, pub user_ca: Option, } impl Default for SSHServer { fn default() -> Self { Self { listen: "[::]:22".to_string(), user_ca: None, } } } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct LvmVG { #[serde(alias = "vg")] pub name: String, pub pvs: LvmPV, #[serde(default)] pub defaults: LvmLVDefaults, pub lvs: Vec, } #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] pub struct LvmLVDefaults { #[serde(default)] pub fs: Filesystem, #[serde(default)] pub raid: Raid, } #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum Filesystem { Ext4, Xfs, Btrfs, Other(String), } impl Filesystem { pub fn fstype(&self) -> &str { use Filesystem as F; match self { F::Ext4 => "ext4", F::Xfs => "xfs", F::Btrfs => "btrfs", F::Other(t) => t, } } } impl Default for Filesystem { fn default() -> Self { Filesystem::Ext4 } } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct LvmLV { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub fs: Option, #[serde(skip_serializing_if = "Option::is_none")] pub raid: Option, #[serde(flatten)] pub size: LvSize, } #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum LvSize { Size(String), Extents(String), } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct LvmPV { pub n: i16, #[serde(skip_serializing_if = "Vec::is_empty")] pub regexps: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub udev: Option, } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct CryptDev { pub name: String, // hit the limit of enum representation here (flatten + enum variant case) #[serde(skip_serializing_if = "Option::is_none")] pub dev: Option, #[serde(skip_serializing_if = "Option::is_none")] pub prefix: Option, #[serde(skip_serializing_if = "Option::is_none")] pub udev: Option, #[serde(skip_serializing_if = "Option::is_none")] pub optional: Option, } impl CryptDev { pub fn filter(&self) -> DevFilter<'_> { if let Some(dev) = self.dev.as_deref() { DevFilter::Dev(dev) } else if let Some(prefix) = self.prefix.as_deref() { DevFilter::Prefix(prefix) } else if let Some(udev) = self.udev.as_ref() { DevFilter::Udev(udev) } else { DevFilter::None } } pub fn optional(&self) -> bool { self.optional.unwrap_or_else(|| match self.filter() { DevFilter::None => true, DevFilter::Dev(_) => false, DevFilter::Prefix(_) => true, DevFilter::Udev(_) => true, }) } } #[test] fn test_parse_crypt_dev() { for s in [ "name: sys0\ndev: /dev/sda\n", "name: crypt-\nprefix: /dev/sd\n", "name: crypt-${name}\nudev: !glob [ DEVNAME, /dev/sd* ]\n", ] { let dev: CryptDev = serde_yaml::from_str(s).unwrap(); dev.filter(); dev.optional(); } } pub enum DevFilter<'t> { None, Dev(&'t str), Prefix(&'t str), Udev(&'t UdevFilter), } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(rename_all = "snake_case")] pub enum UdevFilter { Eq(String, String), Glob(String, String), And(Vec), Or(Vec), Not(Box), } #[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] pub struct Raid { pub mirrors: Option, pub stripes: Option, } #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct Bootstrap { pub dev: String, pub seed: Option, }