use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use path_absolutize::Absolutize;
use crate::VolumeError;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VolumeName(pub(crate) String);
impl FromStr for VolumeName {
type Err = VolumeError;
fn from_str(str: &str) -> Result<Self, Self::Err> {
if str.is_empty() {
return Err(VolumeError::EmptyVolumeName);
}
Ok(Self(str.to_string()))
}
}
impl Display for VolumeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Volume {
ContainerVolume {
name: VolumeName,
container: PathBuf,
readonly: bool,
},
BindMount {
container: PathBuf,
host: PathBuf,
readonly: bool,
},
Tmpfs {
container: PathBuf,
},
}
impl Volume {
pub fn container_volume(name: VolumeName, container: impl Into<PathBuf>) -> Self {
let container = container.into();
Self::ContainerVolume {
name,
container,
readonly: false,
}
}
pub fn bind_mount(host: impl Into<PathBuf>, container: impl Into<PathBuf>) -> Self {
let host = host.into();
let container = container.into();
Self::BindMount {
host,
container,
readonly: false,
}
}
pub fn tmpfs(container: impl Into<PathBuf>) -> Self {
let container = container.into();
Self::Tmpfs { container }
}
pub fn read_only(&mut self) {
match self {
Self::ContainerVolume { readonly, .. } | Self::BindMount { readonly, .. } => {
*readonly = true;
}
Self::Tmpfs { .. } => {}
}
}
pub(crate) fn mount_arg(&self) -> Result<String, VolumeError> {
let arg = match self {
Self::BindMount {
container,
host,
readonly,
} => {
let src = host.absolutize()?;
let trg = container.absolutize()?;
let ro = if *readonly { ",readonly" } else { "" };
format!(
"type=bind,source={},target={}{ro}",
src.to_string_lossy(),
trg.to_string_lossy()
)
}
Self::ContainerVolume {
name,
container,
readonly,
} => {
let trg = container.absolutize()?;
let ro = if *readonly { ",readonly" } else { "" };
format!(
"type=volume,source={name},target={}{ro}",
trg.to_string_lossy()
)
}
Self::Tmpfs { container } => {
let trg = container.absolutize()?;
format!("type=tmpfs,target={}", trg.to_string_lossy())
}
};
Ok(arg)
}
}
impl<P, Q> From<(P, Q)> for Volume
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
fn from(value: (P, Q)) -> Self {
let host = value.0.as_ref();
let container = value.1.as_ref();
Self::bind_mount(host, container)
}
}
impl<P> From<(VolumeName, P)> for Volume
where
P: AsRef<Path>,
{
fn from(value: (VolumeName, P)) -> Self {
let container = value.1.as_ref();
Self::container_volume(value.0, container)
}
}