1 #![doc(hidden)]
2 
3 use std::fmt;
4 use std::mem;
5 use std::ops::Deref;
6 
7 use protobuf::descriptor::FileDescriptorProto;
8 use protobuf::reflect::FileDescriptor;
9 use protobuf::reflect::MessageDescriptor;
10 
11 use crate::protobuf_ident::ProtobufIdent;
12 use crate::protobuf_rel_path::ProtobufRelPath;
13 use crate::ProtobufIdentRef;
14 use crate::ProtobufRelPathRef;
15 
16 /// Protobuf absolute name (e. g. `.foo.Bar`).
17 #[derive(Clone, Eq, PartialEq, Debug, Hash)]
18 #[doc(hidden)]
19 pub struct ProtobufAbsPath {
20     pub path: String,
21 }
22 
23 #[doc(hidden)]
24 #[derive(Eq, PartialEq, Debug, Hash)]
25 #[repr(C)]
26 pub struct ProtobufAbsPathRef(str);
27 
28 impl Default for ProtobufAbsPath {
default() -> ProtobufAbsPath29     fn default() -> ProtobufAbsPath {
30         ProtobufAbsPath::root()
31     }
32 }
33 
34 impl Deref for ProtobufAbsPathRef {
35     type Target = str;
36 
deref(&self) -> &str37     fn deref(&self) -> &str {
38         &self.0
39     }
40 }
41 
42 impl Deref for ProtobufAbsPath {
43     type Target = ProtobufAbsPathRef;
44 
deref(&self) -> &ProtobufAbsPathRef45     fn deref(&self) -> &ProtobufAbsPathRef {
46         ProtobufAbsPathRef::new(&self.path)
47     }
48 }
49 
50 impl ProtobufAbsPathRef {
is_root(&self) -> bool51     pub fn is_root(&self) -> bool {
52         self.0.is_empty()
53     }
54 
root() -> &'static ProtobufAbsPathRef55     pub fn root() -> &'static ProtobufAbsPathRef {
56         Self::new("")
57     }
58 
new(path: &str) -> &ProtobufAbsPathRef59     pub fn new(path: &str) -> &ProtobufAbsPathRef {
60         assert!(ProtobufAbsPath::is_abs(path), "{:?} is not absolute", path);
61         // SAFETY: repr(transparent)
62         unsafe { mem::transmute(path) }
63     }
64 
remove_prefix(&self, prefix: &ProtobufAbsPathRef) -> Option<&ProtobufRelPathRef>65     pub fn remove_prefix(&self, prefix: &ProtobufAbsPathRef) -> Option<&ProtobufRelPathRef> {
66         if self.0.starts_with(&prefix.0) {
67             let rem = &self.0[prefix.0.len()..];
68             if rem.is_empty() {
69                 return Some(ProtobufRelPathRef::empty());
70             }
71             if rem.starts_with('.') {
72                 return Some(ProtobufRelPathRef::new(&rem[1..]));
73             }
74         }
75         None
76     }
77 
starts_with(&self, that: &ProtobufAbsPathRef) -> bool78     pub fn starts_with(&self, that: &ProtobufAbsPathRef) -> bool {
79         self.remove_prefix(that).is_some()
80     }
81 
as_str(&self) -> &str82     pub fn as_str(&self) -> &str {
83         &self.0
84     }
85 
to_owned(&self) -> ProtobufAbsPath86     pub fn to_owned(&self) -> ProtobufAbsPath {
87         ProtobufAbsPath {
88             path: self.0.to_owned(),
89         }
90     }
91 
parent(&self) -> Option<&ProtobufAbsPathRef>92     pub fn parent(&self) -> Option<&ProtobufAbsPathRef> {
93         match self.0.rfind('.') {
94             Some(pos) => Some(ProtobufAbsPathRef::new(&self.0[..pos])),
95             None => {
96                 if self.0.is_empty() {
97                     None
98                 } else {
99                     Some(ProtobufAbsPathRef::root())
100                 }
101             }
102         }
103     }
104 
self_and_parents(&self) -> Vec<&ProtobufAbsPathRef>105     pub fn self_and_parents(&self) -> Vec<&ProtobufAbsPathRef> {
106         let mut tmp = self;
107 
108         let mut r: Vec<&ProtobufAbsPathRef> = Vec::new();
109 
110         r.push(&self);
111 
112         while let Some(parent) = tmp.parent() {
113             r.push(parent);
114             tmp = parent;
115         }
116 
117         r
118     }
119 }
120 
121 impl ProtobufAbsPath {
root() -> ProtobufAbsPath122     pub fn root() -> ProtobufAbsPath {
123         ProtobufAbsPathRef::root().to_owned()
124     }
125 
as_ref(&self) -> &ProtobufAbsPathRef126     pub fn as_ref(&self) -> &ProtobufAbsPathRef {
127         ProtobufAbsPathRef::new(&self.path)
128     }
129 
130     /// If given name is an fully quialified protobuf name.
is_abs(path: &str) -> bool131     pub fn is_abs(path: &str) -> bool {
132         path.is_empty() || (path.starts_with(".") && path != ".")
133     }
134 
try_new(path: &str) -> Option<ProtobufAbsPath>135     pub fn try_new(path: &str) -> Option<ProtobufAbsPath> {
136         if ProtobufAbsPath::is_abs(path) {
137             Some(ProtobufAbsPath::new(path))
138         } else {
139             None
140         }
141     }
142 
new<S: Into<String>>(path: S) -> ProtobufAbsPath143     pub fn new<S: Into<String>>(path: S) -> ProtobufAbsPath {
144         let path = path.into();
145         assert!(
146             ProtobufAbsPath::is_abs(&path),
147             "path is not absolute: `{}`",
148             path
149         );
150         assert!(!path.ends_with("."), "{}", path);
151         ProtobufAbsPath { path }
152     }
153 
new_from_rel(path: &str) -> ProtobufAbsPath154     pub fn new_from_rel(path: &str) -> ProtobufAbsPath {
155         assert!(
156             !path.starts_with("."),
157             "rel path must not start with dot: {:?}",
158             path
159         );
160         ProtobufAbsPath {
161             path: if path.is_empty() {
162                 String::new()
163             } else {
164                 format!(".{}", path)
165             },
166         }
167     }
168 
package_from_file_proto(file: &FileDescriptorProto) -> ProtobufAbsPath169     pub fn package_from_file_proto(file: &FileDescriptorProto) -> ProtobufAbsPath {
170         Self::new_from_rel(file.package())
171     }
172 
package_from_file_descriptor(file: &FileDescriptor) -> ProtobufAbsPath173     pub fn package_from_file_descriptor(file: &FileDescriptor) -> ProtobufAbsPath {
174         Self::package_from_file_proto(file.proto())
175     }
176 
from_message(message: &MessageDescriptor) -> ProtobufAbsPath177     pub fn from_message(message: &MessageDescriptor) -> ProtobufAbsPath {
178         Self::new_from_rel(&message.full_name())
179     }
180 
concat(a: &ProtobufAbsPathRef, b: &ProtobufRelPathRef) -> ProtobufAbsPath181     pub fn concat(a: &ProtobufAbsPathRef, b: &ProtobufRelPathRef) -> ProtobufAbsPath {
182         let mut a = a.to_owned();
183         a.push_relative(b);
184         a
185     }
186 
from_path_without_dot(path: &str) -> ProtobufAbsPath187     pub fn from_path_without_dot(path: &str) -> ProtobufAbsPath {
188         assert!(!path.is_empty());
189         assert!(!path.starts_with("."));
190         assert!(!path.ends_with("."));
191         ProtobufAbsPath::new(format!(".{}", path))
192     }
193 
from_path_maybe_dot(path: &str) -> ProtobufAbsPath194     pub fn from_path_maybe_dot(path: &str) -> ProtobufAbsPath {
195         if path.starts_with(".") {
196             ProtobufAbsPath::new(path.to_owned())
197         } else {
198             ProtobufAbsPath::from_path_without_dot(path)
199         }
200     }
201 
push_simple(&mut self, simple: &ProtobufIdentRef)202     pub fn push_simple(&mut self, simple: &ProtobufIdentRef) {
203         self.path.push('.');
204         self.path.push_str(&simple);
205     }
206 
push_relative(&mut self, relative: &ProtobufRelPathRef)207     pub fn push_relative(&mut self, relative: &ProtobufRelPathRef) {
208         if !relative.is_empty() {
209             self.path.push_str(&format!(".{}", relative));
210         }
211     }
212 
remove_suffix(&self, suffix: &ProtobufRelPathRef) -> Option<&ProtobufAbsPathRef>213     pub fn remove_suffix(&self, suffix: &ProtobufRelPathRef) -> Option<&ProtobufAbsPathRef> {
214         if suffix.is_empty() {
215             return Some(ProtobufAbsPathRef::new(&self.path));
216         }
217 
218         if self.path.ends_with(suffix.as_str()) {
219             let rem = &self.path[..self.path.len() - suffix.as_str().len()];
220             if rem.is_empty() {
221                 return Some(ProtobufAbsPathRef::root());
222             }
223             if rem.ends_with('.') {
224                 return Some(ProtobufAbsPathRef::new(&rem[..rem.len() - 1]));
225             }
226         }
227         None
228     }
229 
230     /// Pop the last name component
pop(&mut self) -> Option<ProtobufIdent>231     pub fn pop(&mut self) -> Option<ProtobufIdent> {
232         match self.path.rfind('.') {
233             Some(dot) => {
234                 let ident = ProtobufIdent::new(&self.path[dot + 1..]);
235                 self.path.truncate(dot);
236                 Some(ident)
237             }
238             None => None,
239         }
240     }
241 
to_root_rel(&self) -> ProtobufRelPath242     pub fn to_root_rel(&self) -> ProtobufRelPath {
243         if self == &Self::root() {
244             ProtobufRelPath::empty()
245         } else {
246             ProtobufRelPath::new(&self.path[1..])
247         }
248     }
249 
ends_with(&self, that: &ProtobufRelPath) -> bool250     pub fn ends_with(&self, that: &ProtobufRelPath) -> bool {
251         self.remove_suffix(that).is_some()
252     }
253 }
254 
255 impl From<&'_ str> for ProtobufAbsPath {
from(s: &str) -> Self256     fn from(s: &str) -> Self {
257         ProtobufAbsPath::new(s.to_owned())
258     }
259 }
260 
261 impl From<String> for ProtobufAbsPath {
from(s: String) -> Self262     fn from(s: String) -> Self {
263         ProtobufAbsPath::new(s)
264     }
265 }
266 
267 impl fmt::Display for ProtobufAbsPathRef {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result268     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269         write!(f, "{}", &self.0)
270     }
271 }
272 
273 impl fmt::Display for ProtobufAbsPath {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result274     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275         write!(f, "{}", ProtobufAbsPathRef::new(&self.0))
276     }
277 }
278 
279 #[cfg(test)]
280 mod test {
281     use super::*;
282 
283     #[test]
absolute_path_push_simple()284     fn absolute_path_push_simple() {
285         let mut foo = ProtobufAbsPath::new(".foo".to_owned());
286         foo.push_simple(ProtobufIdentRef::new("bar"));
287         assert_eq!(ProtobufAbsPath::new(".foo.bar".to_owned()), foo);
288 
289         let mut foo = ProtobufAbsPath::root();
290         foo.push_simple(ProtobufIdentRef::new("bar"));
291         assert_eq!(ProtobufAbsPath::new(".bar".to_owned()), foo);
292     }
293 
294     #[test]
absolute_path_remove_prefix()295     fn absolute_path_remove_prefix() {
296         assert_eq!(
297             Some(ProtobufRelPathRef::empty()),
298             ProtobufAbsPath::new(".foo".to_owned())
299                 .remove_prefix(&ProtobufAbsPath::new(".foo".to_owned()))
300         );
301         assert_eq!(
302             Some(ProtobufRelPathRef::new("bar")),
303             ProtobufAbsPath::new(".foo.bar".to_owned())
304                 .remove_prefix(&ProtobufAbsPath::new(".foo".to_owned()))
305         );
306         assert_eq!(
307             Some(ProtobufRelPathRef::new("baz.qux")),
308             ProtobufAbsPath::new(".foo.bar.baz.qux".to_owned())
309                 .remove_prefix(&ProtobufAbsPath::new(".foo.bar".to_owned()))
310         );
311         assert_eq!(
312             None,
313             ProtobufAbsPath::new(".foo.barbaz".to_owned())
314                 .remove_prefix(ProtobufAbsPathRef::new(".foo.bar"))
315         );
316     }
317 
318     #[test]
self_and_parents()319     fn self_and_parents() {
320         assert_eq!(
321             vec![
322                 ProtobufAbsPathRef::new(".ab.cde.fghi"),
323                 ProtobufAbsPathRef::new(".ab.cde"),
324                 ProtobufAbsPathRef::new(".ab"),
325                 ProtobufAbsPathRef::root(),
326             ],
327             ProtobufAbsPath::new(".ab.cde.fghi".to_owned()).self_and_parents()
328         );
329     }
330 
331     #[test]
ends_with()332     fn ends_with() {
333         assert!(ProtobufAbsPath::new(".foo.bar").ends_with(&ProtobufRelPath::new("")));
334         assert!(ProtobufAbsPath::new(".foo.bar").ends_with(&ProtobufRelPath::new("bar")));
335         assert!(ProtobufAbsPath::new(".foo.bar").ends_with(&ProtobufRelPath::new("foo.bar")));
336         assert!(!ProtobufAbsPath::new(".foo.bar").ends_with(&ProtobufRelPath::new("foo.bar.baz")));
337     }
338 }
339