| use std::fmt; |
| |
| /// Identifier in `.proto` file |
| #[derive(Eq, PartialEq, Debug, Clone)] |
| pub struct ProtobufIdent(String); |
| |
| impl ProtobufIdent { |
| /// New ident from a string. |
| #[allow(dead_code)] |
| pub fn new(s: &str) -> ProtobufIdent { |
| assert!(!s.is_empty()); |
| assert!(!s.contains("/")); |
| assert!(!s.contains(".")); |
| assert!(!s.contains(":")); |
| ProtobufIdent(s.to_owned()) |
| } |
| |
| /// Get as a string. |
| pub fn get(&self) -> &str { |
| &self.0 |
| } |
| } |
| |
| impl From<&'_ str> for ProtobufIdent { |
| fn from(s: &str) -> Self { |
| ProtobufIdent::new(s) |
| } |
| } |
| |
| impl From<String> for ProtobufIdent { |
| fn from(s: String) -> Self { |
| ProtobufIdent::new(&s) |
| } |
| } |
| |
| impl fmt::Display for ProtobufIdent { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Display::fmt(&self.get(), f) |
| } |
| } |
| |
| /// Relative protobuf identifier path. |
| #[derive(Debug, Eq, PartialEq, Clone)] |
| pub struct ProtobufRelativePath { |
| /// The path |
| pub path: String, |
| } |
| |
| #[allow(dead_code)] |
| impl ProtobufRelativePath { |
| /// Empty relative path. |
| pub fn empty() -> ProtobufRelativePath { |
| ProtobufRelativePath::new(String::new()) |
| } |
| |
| /// New path from a string. |
| pub fn new(path: String) -> ProtobufRelativePath { |
| assert!(!path.starts_with(".")); |
| |
| ProtobufRelativePath { path } |
| } |
| |
| /// From path components. |
| pub fn from_components<I: IntoIterator<Item = ProtobufIdent>>(i: I) -> ProtobufRelativePath { |
| let v: Vec<String> = i.into_iter().map(|c| c.get().to_owned()).collect(); |
| ProtobufRelativePath::from(v.join(".")) |
| } |
| |
| /// Get the string. |
| pub fn get(&self) -> &str { |
| &self.path |
| } |
| |
| /// The path is empty. |
| pub fn is_empty(&self) -> bool { |
| self.path.is_empty() |
| } |
| |
| /// As absolute path from root namespace. |
| pub fn into_absolute(self) -> ProtobufAbsolutePath { |
| if self.is_empty() { |
| ProtobufAbsolutePath::root() |
| } else { |
| ProtobufAbsolutePath::from(format!(".{}", self)) |
| } |
| } |
| |
| fn _last_part(&self) -> Option<&str> { |
| match self.path.rfind('.') { |
| Some(pos) => Some(&self.path[pos + 1..]), |
| None => { |
| if self.path.is_empty() { |
| None |
| } else { |
| Some(&self.path) |
| } |
| } |
| } |
| } |
| |
| fn parent(&self) -> Option<ProtobufRelativePath> { |
| match self.path.rfind('.') { |
| Some(pos) => Some(ProtobufRelativePath::new(self.path[..pos].to_owned())), |
| None => { |
| if self.path.is_empty() { |
| None |
| } else { |
| Some(ProtobufRelativePath::empty()) |
| } |
| } |
| } |
| } |
| |
| /// Self path and parent paths. |
| pub fn self_and_parents(&self) -> Vec<ProtobufRelativePath> { |
| let mut tmp = self.clone(); |
| |
| let mut r = Vec::new(); |
| |
| r.push(self.clone()); |
| |
| while let Some(parent) = tmp.parent() { |
| r.push(parent.clone()); |
| tmp = parent; |
| } |
| |
| r |
| } |
| |
| /// Append path component. |
| pub fn append(&self, simple: &ProtobufRelativePath) -> ProtobufRelativePath { |
| if self.path.is_empty() { |
| ProtobufRelativePath::from(simple.get()) |
| } else { |
| ProtobufRelativePath::new(format!("{}.{}", self.path, simple)) |
| } |
| } |
| |
| /// Append identifier to the path. |
| pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath { |
| self.append(&ProtobufRelativePath::from(simple.clone())) |
| } |
| |
| /// Get first component path and remaining. |
| pub fn split_first_rem(&self) -> Option<(ProtobufIdent, ProtobufRelativePath)> { |
| if self.is_empty() { |
| None |
| } else { |
| Some(match self.path.find('.') { |
| Some(dot) => ( |
| ProtobufIdent::from(&self.path[..dot]), |
| ProtobufRelativePath::new(self.path[dot + 1..].to_owned()), |
| ), |
| None => ( |
| ProtobufIdent::from(self.path.clone()), |
| ProtobufRelativePath::empty(), |
| ), |
| }) |
| } |
| } |
| } |
| |
| impl From<&'_ str> for ProtobufRelativePath { |
| fn from(s: &str) -> ProtobufRelativePath { |
| ProtobufRelativePath::from(s.to_owned()) |
| } |
| } |
| |
| impl From<String> for ProtobufRelativePath { |
| fn from(s: String) -> ProtobufRelativePath { |
| ProtobufRelativePath::new(s) |
| } |
| } |
| |
| impl From<ProtobufIdent> for ProtobufRelativePath { |
| fn from(s: ProtobufIdent) -> ProtobufRelativePath { |
| ProtobufRelativePath::from(s.get()) |
| } |
| } |
| |
| impl From<Vec<ProtobufIdent>> for ProtobufRelativePath { |
| fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath { |
| ProtobufRelativePath::from_components(s.into_iter()) |
| } |
| } |
| |
| impl fmt::Display for ProtobufRelativePath { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Display::fmt(&self.path, f) |
| } |
| } |
| |
| #[cfg(test)] |
| mod relative_path_test { |
| use super::*; |
| |
| #[test] |
| fn parent() { |
| assert_eq!(None, ProtobufRelativePath::empty().parent()); |
| assert_eq!( |
| Some(ProtobufRelativePath::empty()), |
| ProtobufRelativePath::new("aaa".to_owned()).parent() |
| ); |
| assert_eq!( |
| Some(ProtobufRelativePath::new("abc".to_owned())), |
| ProtobufRelativePath::new("abc.def".to_owned()).parent() |
| ); |
| assert_eq!( |
| Some(ProtobufRelativePath::new("abc.def".to_owned())), |
| ProtobufRelativePath::new("abc.def.gh".to_owned()).parent() |
| ); |
| } |
| |
| #[test] |
| fn last_part() { |
| assert_eq!(None, ProtobufRelativePath::empty()._last_part()); |
| assert_eq!( |
| Some("aaa"), |
| ProtobufRelativePath::new("aaa".to_owned())._last_part() |
| ); |
| assert_eq!( |
| Some("def"), |
| ProtobufRelativePath::new("abc.def".to_owned())._last_part() |
| ); |
| assert_eq!( |
| Some("gh"), |
| ProtobufRelativePath::new("abc.def.gh".to_owned())._last_part() |
| ); |
| } |
| } |
| |
| /// Absolute protobuf path (e. g. package). |
| /// |
| /// This is not filesystem path. |
| #[derive(Clone, Eq, PartialEq, Debug, Hash)] |
| pub struct ProtobufAbsolutePath { |
| /// The path. |
| pub path: String, |
| } |
| |
| impl ProtobufAbsolutePath { |
| fn root() -> ProtobufAbsolutePath { |
| ProtobufAbsolutePath::new(String::new()) |
| } |
| |
| /// From string. |
| pub fn new(path: String) -> ProtobufAbsolutePath { |
| assert!(path.is_empty() || path.starts_with("."), path); |
| assert!(!path.ends_with("."), path); |
| ProtobufAbsolutePath { path } |
| } |
| |
| /// The path is empty. |
| pub fn is_empty(&self) -> bool { |
| self.path.is_empty() |
| } |
| |
| /// From a path without leading dot. |
| /// |
| /// (Protobuf paths start with dot). |
| pub fn from_path_without_dot(path: &str) -> ProtobufAbsolutePath { |
| if path.is_empty() { |
| ProtobufAbsolutePath::root() |
| } else { |
| assert!(!path.starts_with(".")); |
| assert!(!path.ends_with(".")); |
| ProtobufAbsolutePath::new(format!(".{}", path)) |
| } |
| } |
| |
| /// Parse absolute path. |
| #[allow(dead_code)] |
| pub fn from_package_path(path: Option<&str>) -> ProtobufAbsolutePath { |
| match path { |
| None => ProtobufAbsolutePath::root(), |
| Some(path) => ProtobufAbsolutePath::from_path_without_dot(path), |
| } |
| } |
| |
| /// Construct abs path from a string which may start with a dot. |
| pub fn from_path_maybe_dot(path: &str) -> ProtobufAbsolutePath { |
| if path.starts_with(".") { |
| ProtobufAbsolutePath::new(path.to_owned()) |
| } else { |
| ProtobufAbsolutePath::from_path_without_dot(path) |
| } |
| } |
| |
| /// Push identifier to the path. |
| pub fn push_simple(&mut self, simple: ProtobufIdent) { |
| self.path.push('.'); |
| self.path.push_str(simple.get()); |
| } |
| |
| /// Push relative path. |
| pub fn push_relative(&mut self, relative: &ProtobufRelativePath) { |
| if !relative.is_empty() { |
| self.path.push('.'); |
| self.path.push_str(&relative.path); |
| } |
| } |
| |
| /// Try remove a prefix. |
| pub fn remove_prefix(&self, prefix: &ProtobufAbsolutePath) -> Option<ProtobufRelativePath> { |
| if self.path.starts_with(&prefix.path) { |
| let rem = &self.path[prefix.path.len()..]; |
| if rem.is_empty() { |
| return Some(ProtobufRelativePath::empty()); |
| } |
| if rem.starts_with('.') { |
| return Some(ProtobufRelativePath::new(rem[1..].to_owned())); |
| } |
| } |
| None |
| } |
| } |
| |
| impl From<&'_ str> for ProtobufAbsolutePath { |
| fn from(s: &str) -> Self { |
| ProtobufAbsolutePath::new(s.to_owned()) |
| } |
| } |
| |
| impl From<String> for ProtobufAbsolutePath { |
| fn from(s: String) -> Self { |
| ProtobufAbsolutePath::new(s) |
| } |
| } |
| |
| impl fmt::Display for ProtobufAbsolutePath { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Display::fmt(&self.path, f) |
| } |
| } |
| |
| #[cfg(test)] |
| mod absolute_path_test { |
| use super::*; |
| |
| #[test] |
| fn absolute_path_push_simple() { |
| let mut foo = ProtobufAbsolutePath::new(".foo".to_owned()); |
| foo.push_simple(ProtobufIdent::from("bar")); |
| assert_eq!(ProtobufAbsolutePath::new(".foo.bar".to_owned()), foo); |
| |
| let mut foo = ProtobufAbsolutePath::root(); |
| foo.push_simple(ProtobufIdent::from("bar")); |
| assert_eq!(ProtobufAbsolutePath::new(".bar".to_owned()), foo); |
| } |
| |
| #[test] |
| fn absolute_path_remove_prefix() { |
| assert_eq!( |
| Some(ProtobufRelativePath::empty()), |
| ProtobufAbsolutePath::new(".foo".to_owned()) |
| .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned())) |
| ); |
| assert_eq!( |
| Some(ProtobufRelativePath::new("bar".to_owned())), |
| ProtobufAbsolutePath::new(".foo.bar".to_owned()) |
| .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned())) |
| ); |
| assert_eq!( |
| Some(ProtobufRelativePath::new("baz.qux".to_owned())), |
| ProtobufAbsolutePath::new(".foo.bar.baz.qux".to_owned()) |
| .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned())) |
| ); |
| assert_eq!( |
| None, |
| ProtobufAbsolutePath::new(".foo.barbaz".to_owned()) |
| .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned())) |
| ); |
| } |
| } |