1use super::path_node::*;
2use crate::tf;
3
4#[derive(Debug, Clone, Eq, PartialEq, Hash)]
6pub struct Path {
7 pub(super) prim: PoolHandle,
8 pub(super) prop: PoolHandle,
9}
10
11impl Path {
12 pub const fn empty_path() -> Self {
14 Self {
15 prim: INVALID_NODE_HANDLE,
16 prop: INVALID_NODE_HANDLE,
17 }
18 }
19
20 pub const fn absolute_root_path() -> Self {
22 Self {
23 prim: ABSOLUTE_ROOT_NODE_HANDLE,
24 prop: INVALID_NODE_HANDLE,
25 }
26 }
27
28 pub const fn reflexive_relative_path() -> Self {
30 Self {
31 prim: RELATIVE_ROOT_NODE_HANDLE,
32 prop: INVALID_NODE_HANDLE,
33 }
34 }
35
36 pub fn is_empty(&self) -> bool {
38 self.prim == INVALID_NODE_HANDLE && self.prop == INVALID_NODE_HANDLE
39 }
40
41 pub fn is_absolute_root(&self) -> bool {
43 *self == Path::absolute_root_path()
44 }
45
46 pub fn append_child(&self, child_name: &tf::Token) -> Self {
50 if self.prop != INVALID_NODE_HANDLE {
51 return Self::empty_path();
52 }
53
54 Self {
55 prim: find_or_create_path_node(
56 &PATH_PRIM_PART_POOL,
57 Some(self.prim),
58 &PathNodeData::Prim {
59 name: child_name.clone(),
60 },
61 ),
62 prop: INVALID_NODE_HANDLE,
63 }
64 }
65
66 pub fn append_property(&self, prop_name: &tf::Token) -> Self {
70 if self.prop != INVALID_NODE_HANDLE {
71 return Self::empty_path();
72 }
73
74 Self {
75 prim: self.prim,
76 prop: find_or_create_path_node(
77 &PATH_PROP_PART_POOL,
78 None,
79 &PathNodeData::PrimProperty {
80 name: prop_name.clone(),
81 },
82 ),
83 }
84 }
85
86 pub fn append_variant_selection(&self, variant_set: &str, variant: &str) -> Self {
90 Self {
91 prim: find_or_create_path_node(
92 &PATH_PRIM_PART_POOL,
93 Some(self.prim),
94 &PathNodeData::PrimVariantSelection {
95 variant_set: tf::Token::new(variant_set),
96 variant_name: tf::Token::new(variant),
97 },
98 ),
99 prop: INVALID_NODE_HANDLE,
100 }
101 }
102
103 pub fn append_target(&self, target_path: &Path) -> Self {
107 Self {
108 prim: self.prim,
109 prop: find_or_create_path_node(
110 &PATH_PROP_PART_POOL,
111 Some(self.prop),
112 &PathNodeData::Target {
113 target_path: target_path.clone(),
114 },
115 ),
116 }
117 }
118
119 pub fn append_relational_attribute(&self, attr_name: &tf::Token) -> Self {
123 Self {
124 prim: self.prim,
125 prop: find_or_create_path_node(
126 &PATH_PROP_PART_POOL,
127 Some(self.prop),
128 &PathNodeData::RelationalAttribute {
129 name: attr_name.clone(),
130 },
131 ),
132 }
133 }
134
135 pub fn append_mapper(&self, target_path: &Path) -> Self {
139 Self {
140 prim: self.prim,
141 prop: find_or_create_path_node(
142 &PATH_PROP_PART_POOL,
143 Some(self.prop),
144 &PathNodeData::Mapper {
145 target_path: target_path.clone(),
146 },
147 ),
148 }
149 }
150
151 pub fn append_mapper_arg(&self, arg_name: &tf::Token) -> Self {
155 Self {
156 prim: self.prim,
157 prop: find_or_create_path_node(
158 &PATH_PROP_PART_POOL,
159 Some(self.prop),
160 &PathNodeData::MapperArg {
161 name: arg_name.clone(),
162 },
163 ),
164 }
165 }
166
167 pub fn append_expression(&self) -> Self {
171 Self {
172 prim: self.prim,
173 prop: find_or_create_path_node(
174 &PATH_PROP_PART_POOL,
175 Some(self.prop),
176 &PathNodeData::Expression,
177 ),
178 }
179 }
180
181 pub fn parent_path(&self) -> Self {
183 if self.is_empty() {
184 return Self::empty_path();
185 }
186
187 if self.prop != INVALID_NODE_HANDLE {
189 let prop_pool = PATH_PROP_PART_POOL.read().unwrap();
190 let prop_node = prop_pool.get(self.prop).unwrap();
191
192 return Self {
193 prim: self.prim,
194 prop: prop_node.parent,
195 };
196 }
197
198 let prim_pool = PATH_PRIM_PART_POOL.read().unwrap();
203 let prim_node = prim_pool.get(self.prim).unwrap();
204
205 if prim_node.is_absolute_path()
206 || (self.prim != RELATIVE_ROOT_NODE_HANDLE
207 && match &prim_node.data {
208 PathNodeData::Prim { name } => name.as_str() != "..",
209 _ => true,
210 }) {
211 return Self {
212 prim: prim_node.parent,
213 prop: INVALID_NODE_HANDLE,
214 };
215 }
216
217 self.append_child(&tf::Token::new(".."))
219 }
220
221 pub fn name(&self) -> String {
222 if self.prop != INVALID_NODE_HANDLE {
223 let prop_pool = PATH_PROP_PART_POOL.read().unwrap();
224 let prop_node = prop_pool.get(self.prop).unwrap();
225 return prop_node.name().to_string();
226 }
227
228 if self.prim != INVALID_NODE_HANDLE {
229 let prim_pool = PATH_PRIM_PART_POOL.read().unwrap();
230 let prim_node = prim_pool.get(self.prim).unwrap();
231 return prim_node.name().to_string();
232 }
233
234 "".to_string()
235 }
236}
237
238impl Default for Path {
239 fn default() -> Self {
240 Self::empty_path()
241 }
242}
243
244impl From<&str> for Path {
245 fn from(s: &str) -> Self {
246 super::path_parser::parse_path(s).unwrap_or_else(|_| Self::empty_path())
247 }
248}
249
250impl std::fmt::Display for Path {
251 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
252 write!(f, "{}", PathNode::path_string(self.prim, self.prop))
253 }
254}
255
256#[cfg(test)]
257#[rustfmt::skip]
258mod tests {
259 use super::*;
260
261 fn p(s: &str) -> Path {
262 Path::from(s)
263 }
264
265 fn t(s: &str) -> tf::Token {
266 tf::Token::new(s)
267 }
268
269 #[test]
270 fn append_child() {
271 assert_eq!(p("/foo").append_child(&t("bar")), p("/foo/bar"));
272 assert_eq!(p("foo").append_child(&t("bar")), p("foo/bar"));
273 assert_eq!(p("/foo.prop").append_child(&t("bar")), Path::empty_path());
274 }
275
276 #[test]
277 fn append_property() {
278 assert_eq!(p("/foo").append_property(&t("prop")), p("/foo.prop"));
279 assert_eq!(p("/foo").append_property(&t("prop:foo:bar")), p("/foo.prop:foo:bar"));
280 assert_eq!(p("/foo.prop").append_property(&t("prop2")), Path::empty_path());
281 assert_eq!(p("/foo.prop").append_property(&t("prop2:foo:bar")), Path::empty_path());
282 }
283
284 #[test]
285 fn parent_path() {
286 assert_eq!(p("/foo").parent_path(), Path::absolute_root_path());
287 assert_eq!(p("/foo/bar").parent_path(), p("/foo"));
288 assert_eq!(p("foo/bar").parent_path(), p("foo"));
289 assert_eq!(p("/foo.prop").parent_path(), p("/foo"));
290 assert_eq!(p("foo.prop").parent_path(), p("foo"));
291 assert_eq!(p("/foo.prop:bar").parent_path(), p("/foo"));
292 }
293
294 #[test]
295 fn print() {
296 assert_eq!(p("/foo").to_string(), "/foo");
297 assert_eq!(p("/foo/bar").to_string(), "/foo/bar");
298 assert_eq!(p("foo/bar").to_string(), "foo/bar");
299 assert_eq!(p("/foo.prop").to_string(), "/foo.prop");
300 }
301}