1use core::fmt::Display;
2
3use crate::prelude_internal::*;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Plural)]
6pub struct EurePath(pub Vec<PathSegment>);
7
8impl EurePath {
9 pub fn root() -> Self {
11 EurePath(Vec::new())
12 }
13
14 pub fn is_root(&self) -> bool {
16 self.0.is_empty()
17 }
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub enum PathSegment {
22 Ident(Identifier),
24 Extension(Identifier),
26 Value(ObjectKey),
28 TupleIndex(u8),
30 ArrayIndex(Option<usize>),
32}
33
34impl Display for EurePath {
35 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36 if self.0.is_empty() {
37 return write!(f, "(root)");
38 }
39 for (i, segment) in self.0.iter().enumerate() {
40 let is_first = i == 0;
41 match segment {
42 PathSegment::Ident(id) => {
43 if !is_first {
44 write!(f, ".")?;
45 }
46 write!(f, "{}", id)?;
47 }
48 PathSegment::Extension(id) => {
49 if !is_first {
50 write!(f, ".")?;
51 }
52 write!(f, "${}", id)?;
53 }
54 PathSegment::Value(key) => {
55 if !is_first {
56 write!(f, ".")?;
57 }
58 write!(f, "{}", key)?;
59 }
60 PathSegment::TupleIndex(index) => {
61 if !is_first {
62 write!(f, ".")?;
63 }
64 write!(f, "#{}", index)?;
65 }
66 PathSegment::ArrayIndex(Some(index)) => write!(f, "[{}]", index)?,
67 PathSegment::ArrayIndex(None) => write!(f, "[]")?,
68 }
69 }
70 Ok(())
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use alloc::format;
77
78 use super::*;
79 use crate::value::ObjectKey;
80
81 #[test]
82 fn test_display_empty_path() {
83 let path = EurePath::root();
84 assert_eq!(format!("{}", path), "(root)");
85 }
86
87 #[test]
88 fn test_display_single_ident() {
89 let path = EurePath(vec![PathSegment::Ident(Identifier::new_unchecked("name"))]);
90 assert_eq!(format!("{}", path), "name");
91 }
92
93 #[test]
94 fn test_display_nested_idents() {
95 let path = EurePath(vec![
96 PathSegment::Ident(Identifier::new_unchecked("a")),
97 PathSegment::Ident(Identifier::new_unchecked("b")),
98 PathSegment::Ident(Identifier::new_unchecked("c")),
99 ]);
100 assert_eq!(format!("{}", path), "a.b.c");
101 }
102
103 #[test]
104 fn test_display_extension() {
105 let path = EurePath(vec![PathSegment::Extension(Identifier::new_unchecked(
106 "variant",
107 ))]);
108 assert_eq!(format!("{}", path), "$variant");
109 }
110
111 #[test]
112 fn test_display_array_index() {
113 let path = EurePath(vec![
114 PathSegment::Ident(Identifier::new_unchecked("items")),
115 PathSegment::ArrayIndex(Some(0)),
116 ]);
117 assert_eq!(format!("{}", path), "items[0]");
118 }
119
120 #[test]
121 fn test_display_array_index_none() {
122 let path = EurePath(vec![
123 PathSegment::Ident(Identifier::new_unchecked("items")),
124 PathSegment::ArrayIndex(None),
125 ]);
126 assert_eq!(format!("{}", path), "items[]");
127 }
128
129 #[test]
130 fn test_display_tuple_index() {
131 let path = EurePath(vec![
132 PathSegment::Ident(Identifier::new_unchecked("point")),
133 PathSegment::TupleIndex(1),
134 ]);
135 assert_eq!(format!("{}", path), "point.#1");
136 }
137
138 #[test]
139 fn test_display_string_key() {
140 let path = EurePath(vec![PathSegment::Value(ObjectKey::String(
141 "hello".to_string(),
142 ))]);
143 assert_eq!(format!("{}", path), "\"hello\"");
144 }
145
146 #[test]
147 fn test_display_string_key_with_spaces() {
148 let path = EurePath(vec![PathSegment::Value(ObjectKey::String(
149 "hello world".to_string(),
150 ))]);
151 assert_eq!(format!("{}", path), "\"hello world\"");
152 }
153
154 #[test]
155 fn test_display_string_key_with_quotes() {
156 let path = EurePath(vec![PathSegment::Value(ObjectKey::String(
157 "say \"hi\"".to_string(),
158 ))]);
159 assert_eq!(format!("{}", path), "\"say \\\"hi\\\"\"");
160 }
161
162 #[test]
163 fn test_display_number_key() {
164 let path = EurePath(vec![PathSegment::Value(ObjectKey::Number(42.into()))]);
165 assert_eq!(format!("{}", path), "42");
166 }
167
168 #[test]
169 fn test_display_bool_key() {
170 let path = EurePath(vec![PathSegment::Value(ObjectKey::String("true".into()))]);
172 assert_eq!(format!("{}", path), "\"true\"");
173 }
174
175 #[test]
176 fn test_display_complex_path() {
177 let path = EurePath(vec![
178 PathSegment::Ident(Identifier::new_unchecked("config")),
179 PathSegment::Extension(Identifier::new_unchecked("eure")),
180 PathSegment::Ident(Identifier::new_unchecked("items")),
181 PathSegment::ArrayIndex(Some(0)),
182 PathSegment::Value(ObjectKey::String("key with space".to_string())),
183 ]);
184 assert_eq!(
185 format!("{}", path),
186 "config.$eure.items[0].\"key with space\""
187 );
188 }
189}