protobuf_parse/
protobuf_abs_path.rs

1#![doc(hidden)]
2
3use std::fmt;
4use std::mem;
5use std::ops::Deref;
6
7use protobuf::descriptor::FileDescriptorProto;
8use protobuf::reflect::FileDescriptor;
9use protobuf::reflect::MessageDescriptor;
10
11use crate::protobuf_ident::ProtobufIdent;
12use crate::protobuf_rel_path::ProtobufRelPath;
13use crate::ProtobufIdentRef;
14use crate::ProtobufRelPathRef;
15
16/// Protobuf absolute name (e. g. `.foo.Bar`).
17#[derive(Clone, Eq, PartialEq, Debug, Hash)]
18#[doc(hidden)]
19pub struct ProtobufAbsPath {
20    pub path: String,
21}
22
23#[doc(hidden)]
24#[derive(Eq, PartialEq, Debug, Hash)]
25#[repr(C)]
26pub struct ProtobufAbsPathRef(str);
27
28impl Default for ProtobufAbsPath {
29    fn default() -> ProtobufAbsPath {
30        ProtobufAbsPath::root()
31    }
32}
33
34impl Deref for ProtobufAbsPathRef {
35    type Target = str;
36
37    fn deref(&self) -> &str {
38        &self.0
39    }
40}
41
42impl Deref for ProtobufAbsPath {
43    type Target = ProtobufAbsPathRef;
44
45    fn deref(&self) -> &ProtobufAbsPathRef {
46        ProtobufAbsPathRef::new(&self.path)
47    }
48}
49
50impl ProtobufAbsPathRef {
51    pub fn is_root(&self) -> bool {
52        self.0.is_empty()
53    }
54
55    pub fn root() -> &'static ProtobufAbsPathRef {
56        Self::new("")
57    }
58
59    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
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
78    pub fn starts_with(&self, that: &ProtobufAbsPathRef) -> bool {
79        self.remove_prefix(that).is_some()
80    }
81
82    pub fn as_str(&self) -> &str {
83        &self.0
84    }
85
86    pub fn to_owned(&self) -> ProtobufAbsPath {
87        ProtobufAbsPath {
88            path: self.0.to_owned(),
89        }
90    }
91
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
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
121impl ProtobufAbsPath {
122    pub fn root() -> ProtobufAbsPath {
123        ProtobufAbsPathRef::root().to_owned()
124    }
125
126    pub fn as_ref(&self) -> &ProtobufAbsPathRef {
127        ProtobufAbsPathRef::new(&self.path)
128    }
129
130    /// If given name is an fully quialified protobuf name.
131    pub fn is_abs(path: &str) -> bool {
132        path.is_empty() || (path.starts_with(".") && path != ".")
133    }
134
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
143    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
154    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
169    pub fn package_from_file_proto(file: &FileDescriptorProto) -> ProtobufAbsPath {
170        Self::new_from_rel(file.package())
171    }
172
173    pub fn package_from_file_descriptor(file: &FileDescriptor) -> ProtobufAbsPath {
174        Self::package_from_file_proto(file.proto())
175    }
176
177    pub fn from_message(message: &MessageDescriptor) -> ProtobufAbsPath {
178        Self::new_from_rel(&message.full_name())
179    }
180
181    pub fn concat(a: &ProtobufAbsPathRef, b: &ProtobufRelPathRef) -> ProtobufAbsPath {
182        let mut a = a.to_owned();
183        a.push_relative(b);
184        a
185    }
186
187    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
194    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
202    pub fn push_simple(&mut self, simple: &ProtobufIdentRef) {
203        self.path.push('.');
204        self.path.push_str(&simple);
205    }
206
207    pub fn push_relative(&mut self, relative: &ProtobufRelPathRef) {
208        if !relative.is_empty() {
209            self.path.push_str(&format!(".{}", relative));
210        }
211    }
212
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
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
242    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
250    pub fn ends_with(&self, that: &ProtobufRelPath) -> bool {
251        self.remove_suffix(that).is_some()
252    }
253}
254
255impl From<&'_ str> for ProtobufAbsPath {
256    fn from(s: &str) -> Self {
257        ProtobufAbsPath::new(s.to_owned())
258    }
259}
260
261impl From<String> for ProtobufAbsPath {
262    fn from(s: String) -> Self {
263        ProtobufAbsPath::new(s)
264    }
265}
266
267impl fmt::Display for ProtobufAbsPathRef {
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        write!(f, "{}", &self.0)
270    }
271}
272
273impl fmt::Display for ProtobufAbsPath {
274    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275        write!(f, "{}", ProtobufAbsPathRef::new(&self.0))
276    }
277}
278
279#[cfg(test)]
280mod test {
281    use super::*;
282
283    #[test]
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]
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]
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]
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}