Skip to main content

openusd_rs/sdf/
path.rs

1use super::path_node::*;
2use crate::tf;
3
4/// A path value used to locate objects in layers or scenegraphs.
5#[derive(Debug, Clone, Eq, PartialEq, Hash)]
6pub struct Path {
7	pub(super) prim: PoolHandle,
8	pub(super) prop: PoolHandle,
9}
10
11impl Path {
12	/// The empty path value.
13	pub const fn empty_path() -> Self {
14		Self {
15			prim: INVALID_NODE_HANDLE,
16			prop: INVALID_NODE_HANDLE,
17		}
18	}
19
20	/// The absolute path representing the top of the namespace hierarchy.
21	pub const fn absolute_root_path() -> Self {
22		Self {
23			prim: ABSOLUTE_ROOT_NODE_HANDLE,
24			prop: INVALID_NODE_HANDLE,
25		}
26	}
27
28	/// The relative path representing "self".
29	pub const fn reflexive_relative_path() -> Self {
30		Self {
31			prim: RELATIVE_ROOT_NODE_HANDLE,
32			prop: INVALID_NODE_HANDLE,
33		}
34	}
35
36	/// Returns true if this path is the [`Path::empty_path`].
37	pub fn is_empty(&self) -> bool {
38		self.prim == INVALID_NODE_HANDLE && self.prop == INVALID_NODE_HANDLE
39	}
40
41	/// Returns true if this path is the [`Path::absolute_root_path`].
42	pub fn is_absolute_root(&self) -> bool {
43		*self == Path::absolute_root_path()
44	}
45
46	/// Creates a path by appending an element for `child_name` to this path.
47	///
48	/// This path must be a prim path, the AbsoluteRootPath or the ReflexiveRelativePath.
49	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	/// Creates a path by appending an element for `prop_name` to this path.
67	///
68	/// This path must be a prim path or the ReflexiveRelativePath.
69	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	/// Creates a path by appending an element for `variant_set` and `variant` to this path.
87	///
88	/// This path must be a prim path.
89	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	/// Creates a path by appending an element for `target_path` to this path.
104	///
105	/// This path must be a prim property or relational attribute path.
106	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	/// Creates a path by appending an element for `attr_name` to this path.
120	///
121	/// This path must be a target path.
122	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	/// Creates a path by appending an element for `target_path` to this path.
136	///
137	/// This path must be a prim property or relational attribute path.
138	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	/// Creates a path by appending an element for `arg_name` to this path.
152	///
153	/// This path must be a mapper path.
154	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	/// Creates a path by appending an expression element to this path.
168	///
169	/// This path must be a prim property or relational attribute path.
170	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	/// Return the path that identifies this path's namespace parent.
182	pub fn parent_path(&self) -> Self {
183		if self.is_empty() {
184			return Self::empty_path();
185		}
186
187		// If this is a property-like path, trim that first.
188		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		// This is a prim-like path. If this is an absolute path (most common case)
199		// then it's just the parent path node. On the other hand if this path is a
200		// relative path, and is '.' or ends with '..', the logical parent path is
201		// made by appending a '..' component.
202		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		// Is relative root '.' or ends with '..'.
218		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}