use std::{
convert::Infallible,
fmt::{self, Display, Formatter},
num::{NonZeroU16, TryFromIntError},
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::{AbsolutePath, ByteValue};
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct BlkioConfig {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_read_bps: Vec<BpsLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_read_iops: Vec<IopsLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_write_bps: Vec<BpsLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_write_iops: Vec<IopsLimit>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weight: Option<Weight>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub weight_device: Vec<WeightDevice>,
}
impl BlkioConfig {
#[must_use]
pub fn is_empty(&self) -> bool {
let Self {
device_read_bps,
device_read_iops,
device_write_bps,
device_write_iops,
weight,
weight_device,
} = self;
device_read_bps.is_empty()
&& device_read_iops.is_empty()
&& device_write_bps.is_empty()
&& device_write_iops.is_empty()
&& weight.is_none()
&& weight_device.is_empty()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BpsLimit {
pub path: AbsolutePath,
pub rate: ByteValue,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct IopsLimit {
pub path: AbsolutePath,
pub rate: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct WeightDevice {
pub path: AbsolutePath,
pub weight: Weight,
}
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(into = "u16", try_from = "u16")]
pub struct Weight(NonZeroU16);
impl Weight {
pub const DEFAULT: Self = Self(
unsafe { NonZeroU16::new_unchecked(500) },
);
pub fn new<T>(weight: T) -> Result<Self, WeightRangeError>
where
T: TryInto<NonZeroU16>,
WeightRangeError: From<T::Error>,
{
let weight = weight.try_into()?;
match weight.get() {
10..=1000 => Ok(Self(weight)),
_ => Err(WeightRangeError { source: None }),
}
}
#[must_use]
pub const fn into_inner(self) -> NonZeroU16 {
self.0
}
}
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
#[error("weights must be between 10 and 1000")]
pub struct WeightRangeError {
#[from]
source: Option<TryFromIntError>,
}
impl From<Infallible> for WeightRangeError {
fn from(value: Infallible) -> Self {
match value {}
}
}
impl Default for Weight {
fn default() -> Self {
Self::DEFAULT
}
}
impl Display for Weight {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl TryFrom<NonZeroU16> for Weight {
type Error = WeightRangeError;
fn try_from(value: NonZeroU16) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl TryFrom<u16> for Weight {
type Error = WeightRangeError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<Weight> for NonZeroU16 {
fn from(value: Weight) -> Self {
value.into_inner()
}
}
impl From<Weight> for u16 {
fn from(value: Weight) -> Self {
value.into_inner().into()
}
}
impl PartialEq<NonZeroU16> for Weight {
fn eq(&self, other: &NonZeroU16) -> bool {
self.0.eq(other)
}
}
impl PartialEq<u16> for Weight {
fn eq(&self, other: &u16) -> bool {
self.0.get().eq(other)
}
}