compose_lens/model/
identity.rs1use super::Located;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct UserSpec {
8 raw: Located<String>,
9 user: IdentityComponent,
10 group: Option<IdentityComponent>,
11}
12
13impl UserSpec {
14 pub(crate) fn parse(raw: Located<String>) -> Self {
15 let (user, group) = split_user_group(raw.value())
16 .map_or_else(|| (raw.value().as_str(), None), |(user, group)| (user, Some(group)));
17 Self {
18 user: IdentityComponent::parse(user),
19 group: group.map(IdentityComponent::parse),
20 raw,
21 }
22 }
23
24 #[must_use]
26 pub const fn raw(&self) -> &Located<String> {
27 &self.raw
28 }
29
30 #[must_use]
32 pub const fn user(&self) -> &IdentityComponent {
33 &self.user
34 }
35
36 #[must_use]
38 pub const fn group(&self) -> Option<&IdentityComponent> {
39 self.group.as_ref()
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum IdentityComponent {
46 Numeric(String),
48 Name(String),
50 Expression(String),
52 Empty,
54}
55
56impl IdentityComponent {
57 fn parse(value: &str) -> Self {
58 if value.is_empty() {
59 Self::Empty
60 } else if value.contains("${") || value.contains("$$") {
61 Self::Expression(value.to_owned())
62 } else if value.bytes().all(|byte| byte.is_ascii_digit()) {
63 Self::Numeric(value.to_owned())
64 } else {
65 Self::Name(value.to_owned())
66 }
67 }
68
69 #[must_use]
71 pub fn raw(&self) -> &str {
72 match self {
73 Self::Numeric(value) | Self::Name(value) | Self::Expression(value) => value,
74 Self::Empty => "",
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct UserNamespaceMode {
82 raw: Located<String>,
83 kind: UserNamespaceModeKind,
84}
85
86impl UserNamespaceMode {
87 pub(crate) fn parse(raw: Located<String>) -> Self {
88 let kind = match raw.value().as_str() {
89 "host" | "" => UserNamespaceModeKind::Host,
90 value if value == "keep-id" || value.starts_with("keep-id:") => UserNamespaceModeKind::PodmanKeepId,
91 value if value == "auto" || value.starts_with("auto:") => UserNamespaceModeKind::PodmanAuto,
92 "nomap" => UserNamespaceModeKind::PodmanNoMap,
93 value if value.starts_with("container:") => UserNamespaceModeKind::Container,
94 _ => UserNamespaceModeKind::Other,
95 };
96 Self { raw, kind }
97 }
98
99 #[must_use]
101 pub const fn raw(&self) -> &Located<String> {
102 &self.raw
103 }
104
105 #[must_use]
107 pub const fn kind(&self) -> UserNamespaceModeKind {
108 self.kind
109 }
110
111 #[must_use]
113 pub const fn is_podman_specific(&self) -> bool {
114 matches!(
115 self.kind,
116 UserNamespaceModeKind::PodmanKeepId
117 | UserNamespaceModeKind::PodmanAuto
118 | UserNamespaceModeKind::PodmanNoMap
119 )
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub enum UserNamespaceModeKind {
126 Host,
128 PodmanKeepId,
130 PodmanAuto,
132 PodmanNoMap,
134 Container,
136 Other,
138}
139
140fn split_user_group(value: &str) -> Option<(&str, &str)> {
141 let bytes = value.as_bytes();
142 let mut interpolation_depth = 0_usize;
143 let mut index = 0_usize;
144 while index < bytes.len() {
145 if bytes[index] == b'$' && bytes.get(index + 1) == Some(&b'{') {
146 interpolation_depth += 1;
147 index += 2;
148 continue;
149 }
150 if bytes[index] == b'}' && interpolation_depth > 0 {
151 interpolation_depth -= 1;
152 } else if bytes[index] == b':' && interpolation_depth == 0 {
153 return Some((&value[..index], &value[index + 1..]));
154 }
155 index += 1;
156 }
157 None
158}
159
160#[cfg(test)]
161mod tests {
162 use super::{IdentityComponent, UserSpec};
163 use crate::model::Located;
164 use crate::source::{SourceId, SourceSpan};
165
166 #[test]
167 fn does_not_split_interpolation_default_operators() -> Result<(), &'static str> {
168 let value = "${UID:-1000}:${GID:-1000}";
169 let span = SourceSpan::new(SourceId::new(1), 0, value.len()).ok_or("valid test span expected")?;
170 let parsed = UserSpec::parse(Located::new(value.to_owned(), span));
171 assert_eq!(parsed.user(), &IdentityComponent::Expression("${UID:-1000}".to_owned()));
172 assert_eq!(
173 parsed.group(),
174 Some(&IdentityComponent::Expression("${GID:-1000}".to_owned()))
175 );
176 Ok(())
177 }
178}