blob: c40a69cd30a3c8052904588c573bc08007b74d56 [file] [log] [blame]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07001use std::fmt;
2
3/// Identifier in `.proto` file
4#[derive(Eq, PartialEq, Debug, Clone)]
5pub struct ProtobufIdent(String);
6
7impl ProtobufIdent {
Haibo Huangba676d32020-08-12 13:52:13 -07008 /// New ident from a string.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07009 #[allow(dead_code)]
10 pub fn new(s: &str) -> ProtobufIdent {
11 assert!(!s.is_empty());
12 assert!(!s.contains("/"));
13 assert!(!s.contains("."));
14 assert!(!s.contains(":"));
15 ProtobufIdent(s.to_owned())
16 }
17
Haibo Huangba676d32020-08-12 13:52:13 -070018 /// Get as a string.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070019 pub fn get(&self) -> &str {
20 &self.0
21 }
22}
23
24impl From<&'_ str> for ProtobufIdent {
25 fn from(s: &str) -> Self {
26 ProtobufIdent::new(s)
27 }
28}
29
30impl From<String> for ProtobufIdent {
31 fn from(s: String) -> Self {
32 ProtobufIdent::new(&s)
33 }
34}
35
36impl fmt::Display for ProtobufIdent {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 fmt::Display::fmt(&self.get(), f)
39 }
40}
41
Haibo Huangba676d32020-08-12 13:52:13 -070042/// Relative protobuf identifier path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070043#[derive(Debug, Eq, PartialEq, Clone)]
44pub struct ProtobufRelativePath {
Haibo Huangba676d32020-08-12 13:52:13 -070045 /// The path
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070046 pub path: String,
47}
48
49#[allow(dead_code)]
50impl ProtobufRelativePath {
Haibo Huangba676d32020-08-12 13:52:13 -070051 /// Empty relative path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070052 pub fn empty() -> ProtobufRelativePath {
53 ProtobufRelativePath::new(String::new())
54 }
55
Haibo Huangba676d32020-08-12 13:52:13 -070056 /// New path from a string.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070057 pub fn new(path: String) -> ProtobufRelativePath {
58 assert!(!path.starts_with("."));
59
60 ProtobufRelativePath { path }
61 }
62
Haibo Huangba676d32020-08-12 13:52:13 -070063 /// From path components.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070064 pub fn from_components<I: IntoIterator<Item = ProtobufIdent>>(i: I) -> ProtobufRelativePath {
65 let v: Vec<String> = i.into_iter().map(|c| c.get().to_owned()).collect();
66 ProtobufRelativePath::from(v.join("."))
67 }
68
Haibo Huangba676d32020-08-12 13:52:13 -070069 /// Get the string.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070070 pub fn get(&self) -> &str {
71 &self.path
72 }
73
Haibo Huangba676d32020-08-12 13:52:13 -070074 /// The path is empty.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070075 pub fn is_empty(&self) -> bool {
76 self.path.is_empty()
77 }
78
Haibo Huangba676d32020-08-12 13:52:13 -070079 /// As absolute path from root namespace.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -070080 pub fn into_absolute(self) -> ProtobufAbsolutePath {
81 if self.is_empty() {
82 ProtobufAbsolutePath::root()
83 } else {
84 ProtobufAbsolutePath::from(format!(".{}", self))
85 }
86 }
87
88 fn _last_part(&self) -> Option<&str> {
89 match self.path.rfind('.') {
90 Some(pos) => Some(&self.path[pos + 1..]),
91 None => {
92 if self.path.is_empty() {
93 None
94 } else {
95 Some(&self.path)
96 }
97 }
98 }
99 }
100
101 fn parent(&self) -> Option<ProtobufRelativePath> {
102 match self.path.rfind('.') {
103 Some(pos) => Some(ProtobufRelativePath::new(self.path[..pos].to_owned())),
104 None => {
105 if self.path.is_empty() {
106 None
107 } else {
108 Some(ProtobufRelativePath::empty())
109 }
110 }
111 }
112 }
113
Haibo Huangba676d32020-08-12 13:52:13 -0700114 /// Self path and parent paths.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700115 pub fn self_and_parents(&self) -> Vec<ProtobufRelativePath> {
116 let mut tmp = self.clone();
117
118 let mut r = Vec::new();
119
120 r.push(self.clone());
121
122 while let Some(parent) = tmp.parent() {
123 r.push(parent.clone());
124 tmp = parent;
125 }
126
127 r
128 }
129
Haibo Huangba676d32020-08-12 13:52:13 -0700130 /// Append path component.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700131 pub fn append(&self, simple: &ProtobufRelativePath) -> ProtobufRelativePath {
132 if self.path.is_empty() {
133 ProtobufRelativePath::from(simple.get())
134 } else {
135 ProtobufRelativePath::new(format!("{}.{}", self.path, simple))
136 }
137 }
138
Haibo Huangba676d32020-08-12 13:52:13 -0700139 /// Append identifier to the path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700140 pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath {
141 self.append(&ProtobufRelativePath::from(simple.clone()))
142 }
143
Haibo Huangba676d32020-08-12 13:52:13 -0700144 /// Get first component path and remaining.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700145 pub fn split_first_rem(&self) -> Option<(ProtobufIdent, ProtobufRelativePath)> {
146 if self.is_empty() {
147 None
148 } else {
149 Some(match self.path.find('.') {
150 Some(dot) => (
151 ProtobufIdent::from(&self.path[..dot]),
152 ProtobufRelativePath::new(self.path[dot + 1..].to_owned()),
153 ),
154 None => (
155 ProtobufIdent::from(self.path.clone()),
156 ProtobufRelativePath::empty(),
157 ),
158 })
159 }
160 }
161}
162
163impl From<&'_ str> for ProtobufRelativePath {
164 fn from(s: &str) -> ProtobufRelativePath {
165 ProtobufRelativePath::from(s.to_owned())
166 }
167}
168
169impl From<String> for ProtobufRelativePath {
170 fn from(s: String) -> ProtobufRelativePath {
171 ProtobufRelativePath::new(s)
172 }
173}
174
175impl From<ProtobufIdent> for ProtobufRelativePath {
176 fn from(s: ProtobufIdent) -> ProtobufRelativePath {
177 ProtobufRelativePath::from(s.get())
178 }
179}
180
181impl From<Vec<ProtobufIdent>> for ProtobufRelativePath {
182 fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath {
183 ProtobufRelativePath::from_components(s.into_iter())
184 }
185}
186
187impl fmt::Display for ProtobufRelativePath {
188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 fmt::Display::fmt(&self.path, f)
190 }
191}
192
193#[cfg(test)]
194mod relative_path_test {
195 use super::*;
196
197 #[test]
198 fn parent() {
199 assert_eq!(None, ProtobufRelativePath::empty().parent());
200 assert_eq!(
201 Some(ProtobufRelativePath::empty()),
202 ProtobufRelativePath::new("aaa".to_owned()).parent()
203 );
204 assert_eq!(
205 Some(ProtobufRelativePath::new("abc".to_owned())),
206 ProtobufRelativePath::new("abc.def".to_owned()).parent()
207 );
208 assert_eq!(
209 Some(ProtobufRelativePath::new("abc.def".to_owned())),
210 ProtobufRelativePath::new("abc.def.gh".to_owned()).parent()
211 );
212 }
213
214 #[test]
215 fn last_part() {
216 assert_eq!(None, ProtobufRelativePath::empty()._last_part());
217 assert_eq!(
218 Some("aaa"),
219 ProtobufRelativePath::new("aaa".to_owned())._last_part()
220 );
221 assert_eq!(
222 Some("def"),
223 ProtobufRelativePath::new("abc.def".to_owned())._last_part()
224 );
225 assert_eq!(
226 Some("gh"),
227 ProtobufRelativePath::new("abc.def.gh".to_owned())._last_part()
228 );
229 }
230}
231
Haibo Huangba676d32020-08-12 13:52:13 -0700232/// Absolute protobuf path (e. g. package).
233///
234/// This is not filesystem path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700235#[derive(Clone, Eq, PartialEq, Debug, Hash)]
236pub struct ProtobufAbsolutePath {
Haibo Huangba676d32020-08-12 13:52:13 -0700237 /// The path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700238 pub path: String,
239}
240
241impl ProtobufAbsolutePath {
242 fn root() -> ProtobufAbsolutePath {
243 ProtobufAbsolutePath::new(String::new())
244 }
245
Haibo Huangba676d32020-08-12 13:52:13 -0700246 /// From string.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700247 pub fn new(path: String) -> ProtobufAbsolutePath {
Joel Galensoncb467ae2021-05-19 16:26:55 -0700248 assert!(path.is_empty() || path.starts_with("."), "{}", path);
249 assert!(!path.ends_with("."), "{}", path);
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700250 ProtobufAbsolutePath { path }
251 }
252
Haibo Huangba676d32020-08-12 13:52:13 -0700253 /// The path is empty.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700254 pub fn is_empty(&self) -> bool {
255 self.path.is_empty()
256 }
257
Haibo Huangba676d32020-08-12 13:52:13 -0700258 /// From a path without leading dot.
259 ///
260 /// (Protobuf paths start with dot).
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700261 pub fn from_path_without_dot(path: &str) -> ProtobufAbsolutePath {
262 if path.is_empty() {
263 ProtobufAbsolutePath::root()
264 } else {
265 assert!(!path.starts_with("."));
266 assert!(!path.ends_with("."));
267 ProtobufAbsolutePath::new(format!(".{}", path))
268 }
269 }
270
Haibo Huangba676d32020-08-12 13:52:13 -0700271 /// Parse absolute path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700272 #[allow(dead_code)]
Haibo Huangba676d32020-08-12 13:52:13 -0700273 pub fn from_package_path(path: Option<&str>) -> ProtobufAbsolutePath {
274 match path {
275 None => ProtobufAbsolutePath::root(),
276 Some(path) => ProtobufAbsolutePath::from_path_without_dot(path),
277 }
278 }
279
280 /// Construct abs path from a string which may start with a dot.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700281 pub fn from_path_maybe_dot(path: &str) -> ProtobufAbsolutePath {
282 if path.starts_with(".") {
283 ProtobufAbsolutePath::new(path.to_owned())
284 } else {
285 ProtobufAbsolutePath::from_path_without_dot(path)
286 }
287 }
288
Haibo Huangba676d32020-08-12 13:52:13 -0700289 /// Push identifier to the path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700290 pub fn push_simple(&mut self, simple: ProtobufIdent) {
291 self.path.push('.');
292 self.path.push_str(simple.get());
293 }
294
Haibo Huangba676d32020-08-12 13:52:13 -0700295 /// Push relative path.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700296 pub fn push_relative(&mut self, relative: &ProtobufRelativePath) {
297 if !relative.is_empty() {
298 self.path.push('.');
299 self.path.push_str(&relative.path);
300 }
301 }
302
Haibo Huangba676d32020-08-12 13:52:13 -0700303 /// Try remove a prefix.
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700304 pub fn remove_prefix(&self, prefix: &ProtobufAbsolutePath) -> Option<ProtobufRelativePath> {
305 if self.path.starts_with(&prefix.path) {
306 let rem = &self.path[prefix.path.len()..];
307 if rem.is_empty() {
308 return Some(ProtobufRelativePath::empty());
309 }
310 if rem.starts_with('.') {
311 return Some(ProtobufRelativePath::new(rem[1..].to_owned()));
312 }
313 }
314 None
315 }
316}
317
318impl From<&'_ str> for ProtobufAbsolutePath {
319 fn from(s: &str) -> Self {
320 ProtobufAbsolutePath::new(s.to_owned())
321 }
322}
323
324impl From<String> for ProtobufAbsolutePath {
325 fn from(s: String) -> Self {
326 ProtobufAbsolutePath::new(s)
327 }
328}
329
330impl fmt::Display for ProtobufAbsolutePath {
331 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332 fmt::Display::fmt(&self.path, f)
333 }
334}
335
336#[cfg(test)]
337mod absolute_path_test {
338 use super::*;
339
340 #[test]
341 fn absolute_path_push_simple() {
342 let mut foo = ProtobufAbsolutePath::new(".foo".to_owned());
343 foo.push_simple(ProtobufIdent::from("bar"));
344 assert_eq!(ProtobufAbsolutePath::new(".foo.bar".to_owned()), foo);
345
346 let mut foo = ProtobufAbsolutePath::root();
347 foo.push_simple(ProtobufIdent::from("bar"));
348 assert_eq!(ProtobufAbsolutePath::new(".bar".to_owned()), foo);
349 }
350
351 #[test]
352 fn absolute_path_remove_prefix() {
353 assert_eq!(
354 Some(ProtobufRelativePath::empty()),
355 ProtobufAbsolutePath::new(".foo".to_owned())
356 .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
357 );
358 assert_eq!(
359 Some(ProtobufRelativePath::new("bar".to_owned())),
360 ProtobufAbsolutePath::new(".foo.bar".to_owned())
361 .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
362 );
363 assert_eq!(
364 Some(ProtobufRelativePath::new("baz.qux".to_owned())),
365 ProtobufAbsolutePath::new(".foo.bar.baz.qux".to_owned())
366 .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
367 );
368 assert_eq!(
369 None,
370 ProtobufAbsolutePath::new(".foo.barbaz".to_owned())
371 .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
372 );
373 }
374}