use crate::item_hash::ItemHash;
use crate::memory_size::gigabyte_to_mebibyte;
use crate::toolkit::serde::default_true;
use memsizes::MiB;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(thiserror::Error, Debug)]
pub enum VolumeError {
#[error("value {size} is out of range ({min}..={max})")]
OutOfRange { size: u64, min: u64, max: u64 },
}
pub trait IsReadOnly {
fn is_read_only() -> bool;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BaseVolume {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mount: Option<PathBuf>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImmutableVolume {
#[serde(flatten)]
pub base: BaseVolume,
#[serde(rename = "ref")]
pub reference: ItemHash,
#[serde(default = "default_true")]
pub use_latest: bool,
}
impl IsReadOnly for ImmutableVolume {
fn is_read_only() -> bool {
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "u64", into = "u64")]
pub struct EphemeralVolumeSize(MiB);
impl EphemeralVolumeSize {
const MIN: u64 = 1;
const MAX: u64 = 1000;
}
impl TryFrom<u64> for EphemeralVolumeSize {
type Error = VolumeError;
fn try_from(size: u64) -> Result<Self, Self::Error> {
if (Self::MIN..=Self::MAX).contains(&size) {
Ok(Self(MiB::from(size)))
} else {
Err(VolumeError::OutOfRange {
size,
min: Self::MIN,
max: Self::MAX,
})
}
}
}
impl From<EphemeralVolumeSize> for u64 {
fn from(size: EphemeralVolumeSize) -> Self {
size.0.count()
}
}
impl From<MiB> for EphemeralVolumeSize {
fn from(size: MiB) -> Self {
Self(size)
}
}
impl From<EphemeralVolumeSize> for MiB {
fn from(value: EphemeralVolumeSize) -> Self {
value.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EphemeralVolume {
#[serde(flatten)]
pub base: BaseVolume,
ephemeral: bool,
pub size_mib: EphemeralVolumeSize,
}
impl EphemeralVolume {
pub fn new(size_mib: u64, mount: impl Into<PathBuf>) -> Result<Self, VolumeError> {
Ok(Self {
base: BaseVolume {
comment: None,
mount: Some(mount.into()),
},
ephemeral: true,
size_mib: EphemeralVolumeSize::try_from(size_mib)?,
})
}
}
impl IsReadOnly for EphemeralVolume {
fn is_read_only() -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ParentVolume {
#[serde(rename = "ref")]
pub reference: ItemHash,
#[serde(default = "default_true")]
pub use_latest: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VolumePersistence {
Host,
Store,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "u64", into = "u64")]
pub struct PersistentVolumeSize(MiB);
impl PersistentVolumeSize {
const MIN: u64 = 1;
const MAX: u64 = gigabyte_to_mebibyte(2048);
}
impl TryFrom<u64> for PersistentVolumeSize {
type Error = VolumeError;
fn try_from(size: u64) -> Result<Self, Self::Error> {
if (Self::MIN..=Self::MAX).contains(&size) {
Ok(Self(MiB::from(size)))
} else {
Err(VolumeError::OutOfRange {
size,
min: Self::MIN,
max: Self::MAX,
})
}
}
}
impl From<PersistentVolumeSize> for u64 {
fn from(size: PersistentVolumeSize) -> Self {
size.0.count()
}
}
impl From<MiB> for PersistentVolumeSize {
fn from(size: MiB) -> Self {
Self(size)
}
}
impl From<PersistentVolumeSize> for MiB {
fn from(value: PersistentVolumeSize) -> Self {
value.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PersistentVolume {
#[serde(flatten)]
pub base: BaseVolume,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent: Option<ParentVolume>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub persistence: Option<VolumePersistence>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub size_mib: PersistentVolumeSize,
}
impl IsReadOnly for PersistentVolume {
fn is_read_only() -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum MachineVolume {
Immutable(ImmutableVolume),
Ephemeral(EphemeralVolume),
Persistent(PersistentVolume),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RootfsVolume {
pub parent: ParentVolume,
pub persistence: VolumePersistence,
pub size_mib: PersistentVolumeSize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub forgotten_by: Option<Vec<ItemHash>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_read_only() {
assert!(ImmutableVolume::is_read_only());
assert!(!EphemeralVolume::is_read_only());
assert!(!PersistentVolume::is_read_only());
}
}