Skip to main content

compose_lens/model/
ulimit.rs

1//! Typed service resource limits.
2
3use super::{FieldReference, Located};
4use crate::source::SourceSpan;
5
6/// A service `ulimits` mapping.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Ulimits {
9    span: SourceSpan,
10    entries: Vec<Ulimit>,
11}
12
13impl Ulimits {
14    pub(super) const fn new(span: SourceSpan, entries: Vec<Ulimit>) -> Self {
15        Self { span, entries }
16    }
17
18    /// Returns the complete mapping span.
19    #[must_use]
20    pub const fn span(&self) -> SourceSpan {
21        self.span
22    }
23
24    /// Returns limits in authored order.
25    #[must_use]
26    pub fn entries(&self) -> &[Ulimit] {
27        &self.entries
28    }
29}
30
31/// One named service limit.
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Ulimit {
34    name: Located<String>,
35    span: SourceSpan,
36    value: UlimitValue,
37}
38
39impl Ulimit {
40    pub(super) const fn new(name: Located<String>, span: SourceSpan, value: UlimitValue) -> Self {
41        Self { name, span, value }
42    }
43
44    /// Returns the limit name.
45    #[must_use]
46    pub const fn name(&self) -> &Located<String> {
47        &self.name
48    }
49
50    /// Returns the complete entry span.
51    #[must_use]
52    pub const fn span(&self) -> SourceSpan {
53        self.span
54    }
55
56    /// Returns the short or long authored value.
57    #[must_use]
58    pub const fn value(&self) -> &UlimitValue {
59        &self.value
60    }
61}
62
63/// The authored form of one service limit.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum UlimitValue {
66    /// One scalar applies to both limits.
67    Single(Located<LimitValue>),
68    /// Separate soft and hard limits.
69    Range(UlimitRange),
70}
71
72/// A scalar resource-limit value.
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum LimitValue {
75    /// Unlimited, authored as `-1`.
76    Unlimited,
77    /// A non-negative integer with its spelling retained.
78    Number(String),
79    /// A deferred interpolation expression.
80    Expression(String),
81    /// An invalid or provider-specific scalar retained for diagnostics.
82    Other(String),
83}
84
85impl LimitValue {
86    pub(crate) fn parse(value: String) -> Self {
87        if value == "-1" {
88            Self::Unlimited
89        } else if value.bytes().all(|byte| byte.is_ascii_digit()) && !value.is_empty() {
90            Self::Number(value)
91        } else if value.contains('$') {
92            Self::Expression(value)
93        } else {
94            Self::Other(value)
95        }
96    }
97
98    /// Reports whether the value follows a specification form or remains deferred.
99    #[must_use]
100    pub const fn is_valid(&self) -> bool {
101        !matches!(self, Self::Other(_))
102    }
103
104    /// Returns a source-independent semantic spelling.
105    #[must_use]
106    pub fn raw(&self) -> &str {
107        match self {
108            Self::Unlimited => "-1",
109            Self::Number(value) | Self::Expression(value) | Self::Other(value) => value,
110        }
111    }
112}
113
114pub(crate) fn valid_ulimit_name(name: &str) -> bool {
115    !name.is_empty() && name.bytes().all(|byte| byte.is_ascii_lowercase())
116}
117
118/// Long syntax with independent soft and hard limits.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct UlimitRange {
121    span: SourceSpan,
122    soft: Option<Located<LimitValue>>,
123    hard: Option<Located<LimitValue>>,
124    extension_fields: Vec<FieldReference>,
125    unknown_fields: Vec<FieldReference>,
126}
127
128impl UlimitRange {
129    pub(super) const fn new(span: SourceSpan) -> Self {
130        Self {
131            span,
132            soft: None,
133            hard: None,
134            extension_fields: Vec::new(),
135            unknown_fields: Vec::new(),
136        }
137    }
138
139    pub(super) fn set_soft(&mut self, value: Located<LimitValue>) {
140        self.soft = Some(value);
141    }
142
143    pub(super) fn set_hard(&mut self, value: Located<LimitValue>) {
144        self.hard = Some(value);
145    }
146
147    pub(super) fn push_extension(&mut self, field: FieldReference) {
148        self.extension_fields.push(field);
149    }
150
151    pub(super) fn push_unknown(&mut self, field: FieldReference) {
152        self.unknown_fields.push(field);
153    }
154
155    /// Returns the complete range mapping span.
156    #[must_use]
157    pub const fn span(&self) -> SourceSpan {
158        self.span
159    }
160
161    /// Returns the explicitly authored soft limit.
162    #[must_use]
163    pub const fn soft(&self) -> Option<&Located<LimitValue>> {
164        self.soft.as_ref()
165    }
166
167    /// Returns the explicitly authored hard limit.
168    #[must_use]
169    pub const fn hard(&self) -> Option<&Located<LimitValue>> {
170        self.hard.as_ref()
171    }
172
173    /// Returns retained `x-` fields.
174    #[must_use]
175    pub fn extension_fields(&self) -> &[FieldReference] {
176        &self.extension_fields
177    }
178
179    /// Returns unrecognized range fields.
180    #[must_use]
181    pub fn unknown_fields(&self) -> &[FieldReference] {
182        &self.unknown_fields
183    }
184}