aboutsummaryrefslogtreecommitdiffstats
path: root/zone_zfs/src/file_system.rs
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-02-22 22:19:57 -0600
committerToby Vincent <tobyv13@gmail.com>2022-02-22 22:19:57 -0600
commitf4d54562e5c4f7c61faadfe13701286cce1e3d05 (patch)
treef0056da257964b8169a8e149cf660e3069222dd5 /zone_zfs/src/file_system.rs
parent1ea68976f452a35b7aa20119c3eeb8ae14de107e (diff)
refactor(zfs): use builder pattern for FileSystem
Refactor FileSystem to use a builder pattern to create a zfs dataset Fix #26
Diffstat (limited to 'zone_zfs/src/file_system.rs')
-rw-r--r--zone_zfs/src/file_system.rs171
1 files changed, 107 insertions, 64 deletions
diff --git a/zone_zfs/src/file_system.rs b/zone_zfs/src/file_system.rs
index c961277..5c747ce 100644
--- a/zone_zfs/src/file_system.rs
+++ b/zone_zfs/src/file_system.rs
@@ -1,5 +1,6 @@
use crate::{Error, Result, Snapshot};
use bytesize::ByteSize;
+use derive_builder::Builder;
use std::{
ffi::{OsStr, OsString},
fmt::Display,
@@ -7,97 +8,119 @@ use std::{
process::Command,
};
-macro_rules! opt [
- ($name:expr, $value:expr) => ({
+macro_rules! concat_opt [
+ ($($value:expr),+) => ({
let mut _temp = OsString::new();
- _temp.push($name);
- _temp.push("=");
- _temp.push($value);
+ $(_temp.push($value);)+
_temp
})
];
-#[derive(Debug, Default, Clone)]
+#[derive(Debug, Default, Clone, Builder)]
+#[builder(
+ build_fn(private, name = "build_file_system"),
+ derive(Debug),
+ setter(into),
+ default
+)]
pub struct FileSystem {
- pub(crate) value: OsString,
+ dataset: PathBuf,
mountpoint: PathBuf,
quota: ByteSize,
}
impl Display for FileSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.value.to_string_lossy())
+ write!(f, "{}", self.dataset.to_string_lossy())
}
}
-impl TryFrom<OsString> for FileSystem {
- type Error = Error;
+impl FileSystemBuilder {
+ pub fn build(&self) -> Result<FileSystem> {
+ self.build_file_system()?.create()
+ }
+
+ pub fn build_from(&self, snapshot: &Snapshot) -> Result<FileSystem> {
+ self.build_file_system()?.create_from(snapshot)
+ }
- fn try_from(value: OsString) -> Result<Self> {
- Ok(FileSystem {
- value,
- ..FileSystem::default()
- })
+ pub(crate) fn to_file_system(&self) -> Result<FileSystem> {
+ self.build_file_system().map_err(Error::from)
}
}
-impl TryFrom<&str> for FileSystem {
- type Error = Error;
+impl FileSystem {
+ pub fn builder() -> FileSystemBuilder {
+ FileSystemBuilder::default()
+ }
- fn try_from(value: &str) -> Result<Self> {
- value.try_into()
+ /// Get a reference to the file system's dataset.
+ pub fn dataset(&self) -> &PathBuf {
+ &self.dataset
}
-}
-impl AsRef<OsStr> for FileSystem {
- fn as_ref(&self) -> &OsStr {
- self.value.as_ref()
+ /// Get a reference to the file system's mountpoint.
+ pub fn mountpoint(&self) -> &PathBuf {
+ &self.mountpoint
}
-}
-impl From<FileSystem> for PathBuf {
- fn from(val: FileSystem) -> Self {
- PathBuf::from(val.value)
+ /// Get the file system's quota.
+ pub fn quota(&self) -> ByteSize {
+ self.quota
}
-}
-impl From<FileSystem> for String {
- fn from(val: FileSystem) -> Self {
- val.value.to_string_lossy().to_string()
+ /// Get the value of the file system's `option` from ZFS
+ pub fn get_opt<T>(&self, option: T) -> Result<OsString>
+ where
+ T: AsRef<OsStr>,
+ {
+ Command::new("zfs")
+ .arg("get")
+ .arg("-H")
+ .arg("-o")
+ .arg("value,source")
+ .arg(&option)
+ .arg(&self.dataset)
+ .output()
+ .map(|o| String::from_utf8(o.stdout))?
+ .map(OsString::from)
+ .map_err(Error::from)
}
-}
-impl FileSystem {
- pub(super) fn set_opt<T, U>(&self, name: T, value: U) -> Result<&Self>
+ pub fn set_opt<T, U>(&self, option: T, value: U) -> Result<&Self>
where
T: AsRef<OsStr>,
U: AsRef<OsStr>,
{
Command::new("zfs")
.arg("set")
- .arg(opt!(&name, value))
- .arg(&self.value)
+ .arg(concat_opt!(&option, "=", value))
+ .arg(&self.dataset)
.status()?
.success()
.then(|| self)
.ok_or_else(|| {
- Error::FileSystem(format!("Failed to set {:?}: {:?}", name.as_ref(), self))
+ Error::FileSystem(format!("Failed to set {:?}: {:?}", option.as_ref(), self))
})
}
- pub(super) fn set_quota(&mut self, quota: ByteSize) -> Result<Self> {
+ pub fn set_quota(&mut self, quota: ByteSize) -> Result<Self> {
self.set_opt("quota", quota.to_string())?;
self.quota = quota;
Ok(self.to_owned())
}
- pub(super) fn set_mountpoint(&mut self, mountpoint: PathBuf) -> Result<Self> {
+ pub fn set_mountpoint(&mut self, mountpoint: PathBuf) -> Result<Self> {
self.set_opt("mountpoint", &mountpoint)?;
self.mountpoint = mountpoint;
Ok(self.to_owned())
}
- pub(super) fn get_snapshots(&self) -> Result<Vec<Snapshot>> {
+ pub fn get_mountpoint(&self) -> PathBuf {
+ self.mountpoint.to_owned()
+ }
+
+ pub fn get_snapshots(&self) -> Result<Vec<Snapshot>> {
Command::new("zfs")
.arg("list")
.arg("-H")
@@ -105,56 +128,76 @@ impl FileSystem {
.arg("name")
.arg("-t")
.arg("snapshot")
- .arg(self)
+ .arg(&self.dataset)
.output()
.map(|o| String::from_utf8(o.stdout))?
.map_err(Error::from)?
.split_whitespace()
- .map(Snapshot::try_from)
+ .filter_map(|s| Snapshot::try_from(s).ok())
+ .map(Ok)
.collect()
}
- pub(super) fn get_latest_snapshot(&self) -> Result<Option<Snapshot>> {
- Ok(self
- .get_snapshots()?
- .into_iter()
- .max_by_key(|s| s.timestamp))
+ pub fn get_latest_snapshot(&self) -> Result<Option<Snapshot>> {
+ todo!("implement using feature")
}
- pub fn mount(&self) -> Result<()> {
+ pub(crate) fn create(&self) -> Result<Self> {
Command::new("zfs")
- .arg("mount")
- .arg(&self.value)
+ .arg("create")
+ .arg("-o")
+ .arg(concat_opt!("mountpoint=", &self.mountpoint))
+ .arg(concat_opt!("quota=", self.quota.to_string()))
.status()?
.success()
- .then(|| ())
- .ok_or_else(|| Error::FileSystem(format!("Failed to mount: {:?}", self)))
+ .then(|| self.to_owned())
+ .ok_or_else(|| Error::FileSystem(format!("Failed to create file system: {:?}", self)))
}
- pub fn unmount(&self) -> Result<()> {
+ pub(crate) fn create_from(&self, snapshot: &Snapshot) -> Result<Self> {
Command::new("zfs")
- .arg("unmount")
- .arg(&self.value)
+ .arg("clone")
+ .arg(&snapshot.name())
+ .arg(&self.dataset)
.status()?
.success()
- .then(|| ())
- .ok_or_else(|| Error::FileSystem(format!("Failed to unmount: {:?}", self)))
+ .then(|| self.to_owned())
+ .ok_or_else(|| Error::Snapshot(format!("Failed to clone snapshot: {:?}", self)))
}
pub fn destroy(&self, force: bool) -> Result<()> {
- let mut args: Vec<&OsStr> = Vec::new();
- let f_arg = &OsString::from("-f");
- if force {
- args.push(f_arg);
- }
- args.push(&self.value);
+ let args = if force {
+ vec!["destroy", "-f"]
+ } else {
+ vec!["destroy"]
+ };
Command::new("zfs")
- .arg("destroy")
.args(args)
+ .arg(&self.dataset)
.status()?
.success()
.then(|| ())
.ok_or_else(|| Error::FileSystem(format!("Failed to destroy: {:?}", self)))
}
+
+ pub fn mount(&self) -> Result<()> {
+ Command::new("zfs")
+ .arg("mount")
+ .arg(&self.dataset)
+ .status()?
+ .success()
+ .then(|| ())
+ .ok_or_else(|| Error::FileSystem(format!("Failed to mount: {:?}", self)))
+ }
+
+ pub fn unmount(&self) -> Result<()> {
+ Command::new("zfs")
+ .arg("unmount")
+ .arg(&self.dataset)
+ .status()?
+ .success()
+ .then(|| ())
+ .ok_or_else(|| Error::FileSystem(format!("Failed to unmount: {:?}", self)))
+ }
}