blob: c40a69cd30a3c8052904588c573bc08007b74d56 [file] [log] [blame]
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()))
);
}
}