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#[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 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 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 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}