1 use std::fmt; 2 3 /// Identifier in `.proto` file 4 #[derive(Eq, PartialEq, Debug, Clone)] 5 pub struct ProtobufIdent(String); 6 7 impl ProtobufIdent { 8 /// New ident from a string. 9 #[allow(dead_code)] new(s: &str) -> ProtobufIdent10 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 18 /// Get as a string. get(&self) -> &str19 pub fn get(&self) -> &str { 20 &self.0 21 } 22 } 23 24 impl From<&'_ str> for ProtobufIdent { from(s: &str) -> Self25 fn from(s: &str) -> Self { 26 ProtobufIdent::new(s) 27 } 28 } 29 30 impl From<String> for ProtobufIdent { from(s: String) -> Self31 fn from(s: String) -> Self { 32 ProtobufIdent::new(&s) 33 } 34 } 35 36 impl fmt::Display for ProtobufIdent { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 fmt::Display::fmt(&self.get(), f) 39 } 40 } 41 42 /// Relative protobuf identifier path. 43 #[derive(Debug, Eq, PartialEq, Clone)] 44 pub struct ProtobufRelativePath { 45 /// The path 46 pub path: String, 47 } 48 49 #[allow(dead_code)] 50 impl ProtobufRelativePath { 51 /// Empty relative path. empty() -> ProtobufRelativePath52 pub fn empty() -> ProtobufRelativePath { 53 ProtobufRelativePath::new(String::new()) 54 } 55 56 /// New path from a string. new(path: String) -> ProtobufRelativePath57 pub fn new(path: String) -> ProtobufRelativePath { 58 assert!(!path.starts_with(".")); 59 60 ProtobufRelativePath { path } 61 } 62 63 /// From path components. from_components<I: IntoIterator<Item = ProtobufIdent>>(i: I) -> ProtobufRelativePath64 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 69 /// Get the string. get(&self) -> &str70 pub fn get(&self) -> &str { 71 &self.path 72 } 73 74 /// The path is empty. is_empty(&self) -> bool75 pub fn is_empty(&self) -> bool { 76 self.path.is_empty() 77 } 78 79 /// As absolute path from root namespace. into_absolute(self) -> ProtobufAbsolutePath80 pub fn into_absolute(self) -> ProtobufAbsolutePath { 81 if self.is_empty() { 82 ProtobufAbsolutePath::root() 83 } else { 84 ProtobufAbsolutePath::from(format!(".{}", self)) 85 } 86 } 87 _last_part(&self) -> Option<&str>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 parent(&self) -> Option<ProtobufRelativePath>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 114 /// Self path and parent paths. self_and_parents(&self) -> Vec<ProtobufRelativePath>115 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 130 /// Append path component. append(&self, simple: &ProtobufRelativePath) -> ProtobufRelativePath131 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 139 /// Append identifier to the path. append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath140 pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath { 141 self.append(&ProtobufRelativePath::from(simple.clone())) 142 } 143 144 /// Get first component path and remaining. split_first_rem(&self) -> Option<(ProtobufIdent, ProtobufRelativePath)>145 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 163 impl From<&'_ str> for ProtobufRelativePath { from(s: &str) -> ProtobufRelativePath164 fn from(s: &str) -> ProtobufRelativePath { 165 ProtobufRelativePath::from(s.to_owned()) 166 } 167 } 168 169 impl From<String> for ProtobufRelativePath { from(s: String) -> ProtobufRelativePath170 fn from(s: String) -> ProtobufRelativePath { 171 ProtobufRelativePath::new(s) 172 } 173 } 174 175 impl From<ProtobufIdent> for ProtobufRelativePath { from(s: ProtobufIdent) -> ProtobufRelativePath176 fn from(s: ProtobufIdent) -> ProtobufRelativePath { 177 ProtobufRelativePath::from(s.get()) 178 } 179 } 180 181 impl From<Vec<ProtobufIdent>> for ProtobufRelativePath { from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath182 fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath { 183 ProtobufRelativePath::from_components(s.into_iter()) 184 } 185 } 186 187 impl fmt::Display for ProtobufRelativePath { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 189 fmt::Display::fmt(&self.path, f) 190 } 191 } 192 193 #[cfg(test)] 194 mod relative_path_test { 195 use super::*; 196 197 #[test] parent()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] last_part()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 232 /// Absolute protobuf path (e. g. package). 233 /// 234 /// This is not filesystem path. 235 #[derive(Clone, Eq, PartialEq, Debug, Hash)] 236 pub struct ProtobufAbsolutePath { 237 /// The path. 238 pub path: String, 239 } 240 241 impl ProtobufAbsolutePath { root() -> ProtobufAbsolutePath242 fn root() -> ProtobufAbsolutePath { 243 ProtobufAbsolutePath::new(String::new()) 244 } 245 246 /// From string. new(path: String) -> ProtobufAbsolutePath247 pub fn new(path: String) -> ProtobufAbsolutePath { 248 assert!(path.is_empty() || path.starts_with("."), "{}", path); 249 assert!(!path.ends_with("."), "{}", path); 250 ProtobufAbsolutePath { path } 251 } 252 253 /// The path is empty. is_empty(&self) -> bool254 pub fn is_empty(&self) -> bool { 255 self.path.is_empty() 256 } 257 258 /// From a path without leading dot. 259 /// 260 /// (Protobuf paths start with dot). from_path_without_dot(path: &str) -> ProtobufAbsolutePath261 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 271 /// Parse absolute path. 272 #[allow(dead_code)] from_package_path(path: Option<&str>) -> ProtobufAbsolutePath273 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. from_path_maybe_dot(path: &str) -> ProtobufAbsolutePath281 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 289 /// Push identifier to the path. push_simple(&mut self, simple: ProtobufIdent)290 pub fn push_simple(&mut self, simple: ProtobufIdent) { 291 self.path.push('.'); 292 self.path.push_str(simple.get()); 293 } 294 295 /// Push relative path. push_relative(&mut self, relative: &ProtobufRelativePath)296 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 303 /// Try remove a prefix. remove_prefix(&self, prefix: &ProtobufAbsolutePath) -> Option<ProtobufRelativePath>304 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 318 impl From<&'_ str> for ProtobufAbsolutePath { from(s: &str) -> Self319 fn from(s: &str) -> Self { 320 ProtobufAbsolutePath::new(s.to_owned()) 321 } 322 } 323 324 impl From<String> for ProtobufAbsolutePath { from(s: String) -> Self325 fn from(s: String) -> Self { 326 ProtobufAbsolutePath::new(s) 327 } 328 } 329 330 impl fmt::Display for ProtobufAbsolutePath { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result331 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 332 fmt::Display::fmt(&self.path, f) 333 } 334 } 335 336 #[cfg(test)] 337 mod absolute_path_test { 338 use super::*; 339 340 #[test] absolute_path_push_simple()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] absolute_path_remove_prefix()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 } 375