use compose_spec_macros::{DeserializeTryFromString, SerializeDisplay};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{common::key_impls, AsShort, Extensions, ShortOrLong};
use super::Limit;
pub type Ulimits = IndexMap<Resource, ShortOrLong<Limit<u64>, Ulimit>>;
#[derive(
SerializeDisplay, DeserializeTryFromString, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct Resource(Box<str>);
impl Resource {
pub fn new<T>(resource: T) -> Result<Self, InvalidResourceError>
where
T: AsRef<str> + Into<Box<str>>,
{
let resource_str = resource.as_ref();
if resource_str.is_empty() {
return Err(InvalidResourceError::Empty);
}
for char in resource_str.chars() {
if !char.is_ascii_lowercase() {
return Err(InvalidResourceError::Character(char));
}
}
Ok(Self(resource.into()))
}
}
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvalidResourceError {
#[error("ulimit resources cannot be empty")]
Empty,
#[error(
"invalid character '{0}', ulimit resources can only contain lowercase ASCII letters (a-z)"
)]
Character(char),
}
key_impls!(Resource => InvalidResourceError);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Ulimit {
pub soft: Limit<u64>,
pub hard: Limit<u64>,
#[serde(flatten)]
pub extensions: Extensions,
}
impl AsShort for Ulimit {
type Short = Limit<u64>;
fn as_short(&self) -> Option<&Self::Short> {
let Self {
soft,
hard,
extensions,
} = self;
(*soft == *hard && extensions.is_empty()).then_some(soft)
}
}
impl From<u64> for Ulimit {
fn from(value: u64) -> Self {
Limit::Value(value).into()
}
}
impl From<Limit<u64>> for Ulimit {
fn from(value: Limit<u64>) -> Self {
Self {
soft: value,
hard: value,
extensions: Extensions::default(),
}
}
}