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(super) 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
114/// Long syntax with independent soft and hard limits.
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct UlimitRange {
117    span: SourceSpan,
118    soft: Option<Located<LimitValue>>,
119    hard: Option<Located<LimitValue>>,
120    extension_fields: Vec<FieldReference>,
121    unknown_fields: Vec<FieldReference>,
122}
123
124impl UlimitRange {
125    pub(super) const fn new(span: SourceSpan) -> Self {
126        Self {
127            span,
128            soft: None,
129            hard: None,
130            extension_fields: Vec::new(),
131            unknown_fields: Vec::new(),
132        }
133    }
134
135    pub(super) fn set_soft(&mut self, value: Located<LimitValue>) {
136        self.soft = Some(value);
137    }
138
139    pub(super) fn set_hard(&mut self, value: Located<LimitValue>) {
140        self.hard = Some(value);
141    }
142
143    pub(super) fn push_extension(&mut self, field: FieldReference) {
144        self.extension_fields.push(field);
145    }
146
147    pub(super) fn push_unknown(&mut self, field: FieldReference) {
148        self.unknown_fields.push(field);
149    }
150
151    /// Returns the complete range mapping span.
152    #[must_use]
153    pub const fn span(&self) -> SourceSpan {
154        self.span
155    }
156
157    /// Returns the explicitly authored soft limit.
158    #[must_use]
159    pub const fn soft(&self) -> Option<&Located<LimitValue>> {
160        self.soft.as_ref()
161    }
162
163    /// Returns the explicitly authored hard limit.
164    #[must_use]
165    pub const fn hard(&self) -> Option<&Located<LimitValue>> {
166        self.hard.as_ref()
167    }
168
169    /// Returns retained `x-` fields.
170    #[must_use]
171    pub fn extension_fields(&self) -> &[FieldReference] {
172        &self.extension_fields
173    }
174
175    /// Returns unrecognized range fields.
176    #[must_use]
177    pub fn unknown_fields(&self) -> &[FieldReference] {
178        &self.unknown_fields
179    }
180}