shape_ast/ast/
type_path.rs1use serde::{Deserialize, Serialize};
8use std::borrow::Borrow;
9use std::fmt;
10use std::hash::{Hash, Hasher};
11use std::ops::Deref;
12
13#[derive(Clone, Debug)]
25pub struct TypePath {
26 segments: Vec<String>,
27 qualified: String,
28}
29
30impl TypePath {
31 pub fn simple(name: impl Into<String>) -> Self {
33 let name = name.into();
34 TypePath {
35 segments: vec![name.clone()],
36 qualified: name,
37 }
38 }
39
40 pub fn from_segments(segments: Vec<String>) -> Self {
42 let qualified = segments.join("::");
43 TypePath {
44 segments,
45 qualified,
46 }
47 }
48
49 pub fn from_qualified(s: impl Into<String>) -> Self {
51 let s = s.into();
52 let segments: Vec<String> = s.split("::").map(|seg| seg.to_string()).collect();
53 TypePath {
54 segments,
55 qualified: s,
56 }
57 }
58
59 pub fn name(&self) -> &str {
61 self.segments.last().map(|s| s.as_str()).unwrap_or("")
62 }
63
64 pub fn module_segments(&self) -> &[String] {
66 if self.segments.len() > 1 {
67 &self.segments[..self.segments.len() - 1]
68 } else {
69 &[]
70 }
71 }
72
73 pub fn is_qualified(&self) -> bool {
75 self.segments.len() > 1
76 }
77
78 pub fn as_str(&self) -> &str {
80 &self.qualified
81 }
82
83 pub fn segments(&self) -> &[String] {
85 &self.segments
86 }
87}
88
89impl Deref for TypePath {
92 type Target = str;
93 fn deref(&self) -> &str {
94 &self.qualified
95 }
96}
97
98impl Borrow<str> for TypePath {
99 fn borrow(&self) -> &str {
100 &self.qualified
101 }
102}
103
104impl AsRef<str> for TypePath {
105 fn as_ref(&self) -> &str {
106 &self.qualified
107 }
108}
109
110impl PartialEq for TypePath {
113 fn eq(&self, other: &Self) -> bool {
114 self.qualified == other.qualified
115 }
116}
117
118impl Eq for TypePath {}
119
120impl Hash for TypePath {
121 fn hash<H: Hasher>(&self, state: &mut H) {
122 self.qualified.hash(state);
123 }
124}
125
126impl PartialEq<str> for TypePath {
127 fn eq(&self, other: &str) -> bool {
128 self.qualified == other
129 }
130}
131
132impl PartialEq<&str> for TypePath {
133 fn eq(&self, other: &&str) -> bool {
134 self.qualified.as_str() == *other
135 }
136}
137
138impl PartialEq<String> for TypePath {
139 fn eq(&self, other: &String) -> bool {
140 self.qualified == *other
141 }
142}
143
144impl PartialEq<TypePath> for str {
145 fn eq(&self, other: &TypePath) -> bool {
146 self == other.qualified
147 }
148}
149
150impl PartialEq<TypePath> for &str {
151 fn eq(&self, other: &TypePath) -> bool {
152 *self == other.qualified.as_str()
153 }
154}
155
156impl PartialEq<TypePath> for String {
157 fn eq(&self, other: &TypePath) -> bool {
158 *self == other.qualified
159 }
160}
161
162impl fmt::Display for TypePath {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str(&self.qualified)
167 }
168}
169
170impl From<String> for TypePath {
173 fn from(s: String) -> Self {
174 TypePath::from_qualified(s)
175 }
176}
177
178impl From<&str> for TypePath {
179 fn from(s: &str) -> Self {
180 TypePath::from_qualified(s)
181 }
182}
183
184impl Serialize for TypePath {
187 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
188 self.qualified.serialize(serializer)
189 }
190}
191
192impl<'de> Deserialize<'de> for TypePath {
193 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
194 let s = String::deserialize(deserializer)?;
195 Ok(TypePath::from_qualified(s))
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_simple_path() {
205 let p = TypePath::simple("Foo");
206 assert_eq!(p.as_str(), "Foo");
207 assert_eq!(p.name(), "Foo");
208 assert!(!p.is_qualified());
209 assert!(p.module_segments().is_empty());
210 }
211
212 #[test]
213 fn test_qualified_path() {
214 let p = TypePath::from_segments(vec!["foo".into(), "Bar".into()]);
215 assert_eq!(p.as_str(), "foo::Bar");
216 assert_eq!(p.name(), "Bar");
217 assert!(p.is_qualified());
218 assert_eq!(p.module_segments(), &["foo".to_string()]);
219 }
220
221 #[test]
222 fn test_deeply_qualified() {
223 let p = TypePath::from_segments(vec!["a".into(), "b".into(), "C".into()]);
224 assert_eq!(p.as_str(), "a::b::C");
225 assert_eq!(p.name(), "C");
226 assert_eq!(p.module_segments(), &["a".to_string(), "b".to_string()]);
227 }
228
229 #[test]
230 fn test_deref_str() {
231 let p = TypePath::simple("Foo");
232 let s: &str = &p;
233 assert_eq!(s, "Foo");
234 }
235
236 #[test]
237 fn test_eq_str() {
238 let p = TypePath::simple("Foo");
239 assert!(p == "Foo");
240 assert!("Foo" == p);
241 assert!(p == "Foo".to_string());
242 }
243
244 #[test]
245 fn test_from_string() {
246 let p: TypePath = "foo::Bar".to_string().into();
247 assert!(p.is_qualified());
248 assert_eq!(p.name(), "Bar");
249 }
250
251 #[test]
252 fn test_from_str() {
253 let p: TypePath = "Baz".into();
254 assert!(!p.is_qualified());
255 }
256
257 #[test]
258 fn test_serde_roundtrip() {
259 let p = TypePath::from_segments(vec!["mod".into(), "Type".into()]);
260 let json = serde_json::to_string(&p).unwrap();
261 assert_eq!(json, "\"mod::Type\"");
262 let p2: TypePath = serde_json::from_str(&json).unwrap();
263 assert_eq!(p, p2);
264 }
265
266 #[test]
267 fn test_hashmap_lookup() {
268 use std::collections::HashMap;
269 let mut map: HashMap<String, i32> = HashMap::new();
270 map.insert("foo::Bar".to_string(), 42);
271 let p = TypePath::from_qualified("foo::Bar");
272 assert_eq!(map.get(p.as_str()), Some(&42));
274 }
275}