use clap::Args; use derive_builder::Builder; use serde::{Deserialize, Serialize}; use std::{fmt::Display, path::PathBuf}; use tabled::Tabled; use crate::FilterContainer; pub use status::ContainerStatus; mod status; #[derive(Debug, PartialEq, Default, Serialize, Deserialize, Builder, Tabled, Clone, Args)] #[builder( name = "ContainerOptions", derive(Debug, Serialize, Deserialize), field(public) )] #[serde(rename_all = "camelCase")] pub struct Container { #[tabled("ID")] pub id: u64, #[tabled("Template")] pub parent: String, #[tabled("User")] pub user: String, } impl Container { pub fn builder() -> ContainerOptions { ContainerOptions::default() } } impl Display for Container { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}-{}-{}", self.user, self.parent, self.id) } } impl From for String { fn from(c: Container) -> Self { format!("{}-{}-{}", c.user, c.parent, c.id) } } impl From for ContainerOptions { fn from(c: Container) -> Self { Self { id: Some(c.id), parent: Some(c.parent), user: Some(c.user), } } } impl From<&Container> for &ContainerOptions { fn from(c: &Container) -> Self { ContainerOptions { id: Some(c.id.to_owned()), parent: Some(c.parent.to_owned()), user: Some(c.user.to_owned()), } .into() } } impl From for &ContainerOptions { fn from(o: ContainerOptions) -> Self { o.into() } } #[derive(Debug, Serialize, Deserialize, Clone, Args)] pub struct CloneOptions { pub template: String, pub user: String, } impl FilterContainer for T where T: Iterator, T::Item: TryInto, { fn filter_container(&mut self, pred: &ContainerOptions) -> Vec { self.filter_map(|c| -> Option { c.try_into().ok() }) .filter(|c| { pred.id.map_or(false, |p| p == c.id) && pred.parent.as_ref().map_or(false, |p| p == &c.parent) && pred.user.as_ref().map_or(false, |p| p == &c.user) }) .collect() } } impl TryFrom for Container { type Error = zone_zfs::Error; fn try_from(fs: zone_zfs::FileSystem) -> zone_zfs::Result { let path_buf = PathBuf::from(fs.dataset()) .file_name() .ok_or_else(|| { zone_zfs::Error::FileSystem(format!("Invalid FileSystem path: {:?}", fs)) })? .to_string_lossy() .into_owned(); let (user, id) = path_buf.rsplit_once('-').ok_or_else(|| { zone_zfs::Error::FileSystem(format!("Invalid FileSystem name: {:?}", fs)) })?; let id = id.parse::().map_err(|err| { zone_zfs::Error::FileSystem(format!("Failed to parse container ID: {:?}", err)) })?; let template = PathBuf::from(fs.dataset()) .parent() .ok_or_else(|| { zone_zfs::Error::FileSystem(format!("Invalid path for filesystem: {:?}", &fs)) })? .to_string_lossy() .into_owned(); Ok(Container { id, parent: template, user: user.to_string(), }) } }