#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[non_exhaustive]
pub struct ResourceLimits {
pub max_memory: Option<u64>,
pub max_processes: Option<u32>,
pub cpu_quota: Option<f64>,
}
impl ResourceLimits {
pub(crate) fn any(&self) -> bool {
self.max_memory.is_some() || self.max_processes.is_some() || self.cpu_quota.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LimitKind {
Memory,
Processes,
Cpu,
}
impl LimitKind {
pub fn name(&self) -> &'static str {
match self {
LimitKind::Memory => "memory",
LimitKind::Processes => "processes",
LimitKind::Cpu => "cpu",
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name {
"memory" => Some(LimitKind::Memory),
"processes" => Some(LimitKind::Processes),
"cpu" => Some(LimitKind::Cpu),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LimitReason {
Invalid,
Unsupported,
Unenforceable,
}
impl LimitReason {
pub fn name(&self) -> &'static str {
match self {
LimitReason::Invalid => "invalid",
LimitReason::Unsupported => "unsupported",
LimitReason::Unenforceable => "unenforceable",
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name {
"invalid" => Some(LimitReason::Invalid),
"unsupported" => Some(LimitReason::Unsupported),
"unenforceable" => Some(LimitReason::Unenforceable),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::{LimitKind, LimitReason, ResourceLimits};
const ALL_KINDS: &[LimitKind] = &[LimitKind::Memory, LimitKind::Processes, LimitKind::Cpu];
const ALL_REASONS: &[LimitReason] = &[
LimitReason::Invalid,
LimitReason::Unsupported,
LimitReason::Unenforceable,
];
#[test]
fn limit_kind_name_pins_each_variant() {
assert_eq!(LimitKind::Memory.name(), "memory");
assert_eq!(LimitKind::Processes.name(), "processes");
assert_eq!(LimitKind::Cpu.name(), "cpu");
}
#[test]
fn limit_reason_name_pins_each_variant() {
assert_eq!(LimitReason::Invalid.name(), "invalid");
assert_eq!(LimitReason::Unsupported.name(), "unsupported");
assert_eq!(LimitReason::Unenforceable.name(), "unenforceable");
}
#[test]
fn name_from_name_round_trips_every_variant() {
for &k in ALL_KINDS {
assert_eq!(LimitKind::from_name(k.name()), Some(k));
}
for &r in ALL_REASONS {
assert_eq!(LimitReason::from_name(r.name()), Some(r));
}
}
#[test]
fn from_name_rejects_unknown_without_defaulting() {
assert_eq!(LimitKind::from_name("Memory"), None);
assert_eq!(LimitKind::from_name("ram"), None);
assert_eq!(LimitReason::from_name(""), None);
assert_eq!(LimitReason::from_name("unenforced"), None);
}
#[test]
fn any_detects_each_limit_axis_independently() {
assert!(!ResourceLimits::default().any());
assert!(
ResourceLimits {
max_memory: Some(1),
..ResourceLimits::default()
}
.any()
);
assert!(
ResourceLimits {
max_processes: Some(1),
..ResourceLimits::default()
}
.any()
);
assert!(
ResourceLimits {
cpu_quota: Some(0.5),
..ResourceLimits::default()
}
.any()
);
}
}