1 #![doc(hidden)] 2 3 use std::fmt; 4 use std::mem; 5 use std::ops::Deref; 6 7 use protobuf::descriptor::FileDescriptorProto; 8 use protobuf::reflect::FileDescriptor; 9 use protobuf::reflect::MessageDescriptor; 10 11 use crate::protobuf_ident::ProtobufIdent; 12 use crate::protobuf_rel_path::ProtobufRelPath; 13 use crate::ProtobufIdentRef; 14 use crate::ProtobufRelPathRef; 15 16 /// Protobuf absolute name (e. g. `.foo.Bar`). 17 #[derive(Clone, Eq, PartialEq, Debug, Hash)] 18 #[doc(hidden)] 19 pub struct ProtobufAbsPath { 20 pub path: String, 21 } 22 23 #[doc(hidden)] 24 #[derive(Eq, PartialEq, Debug, Hash)] 25 #[repr(C)] 26 pub struct ProtobufAbsPathRef(str); 27 28 impl Default for ProtobufAbsPath { default() -> ProtobufAbsPath29 fn default() -> ProtobufAbsPath { 30 ProtobufAbsPath::root() 31 } 32 } 33 34 impl Deref for ProtobufAbsPathRef { 35 type Target = str; 36 deref(&self) -> &str37 fn deref(&self) -> &str { 38 &self.0 39 } 40 } 41 42 impl Deref for ProtobufAbsPath { 43 type Target = ProtobufAbsPathRef; 44 deref(&self) -> &ProtobufAbsPathRef45 fn deref(&self) -> &ProtobufAbsPathRef { 46 ProtobufAbsPathRef::new(&self.path) 47 } 48 } 49 50 impl ProtobufAbsPathRef { is_root(&self) -> bool51 pub fn is_root(&self) -> bool { 52 self.0.is_empty() 53 } 54 root() -> &'static ProtobufAbsPathRef55 pub fn root() -> &'static ProtobufAbsPathRef { 56 Self::new("") 57 } 58 new(path: &str) -> &ProtobufAbsPathRef59 pub fn new(path: &str) -> &ProtobufAbsPathRef { 60 assert!(ProtobufAbsPath::is_abs(path), "{:?} is not absolute", path); 61 // SAFETY: repr(transparent) 62 unsafe { mem::transmute(path) } 63 } 64 remove_prefix(&self, prefix: &ProtobufAbsPathRef) -> Option<&ProtobufRelPathRef>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 starts_with(&self, that: &ProtobufAbsPathRef) -> bool78 pub fn starts_with(&self, that: &ProtobufAbsPathRef) -> bool { 79 self.remove_prefix(that).is_some() 80 } 81 as_str(&self) -> &str82 pub fn as_str(&self) -> &str { 83 &self.0 84 } 85 to_owned(&self) -> ProtobufAbsPath86 pub fn to_owned(&self) -> ProtobufAbsPath { 87 ProtobufAbsPath { 88 path: self.0.to_owned(), 89 } 90 } 91 parent(&self) -> Option<&ProtobufAbsPathRef>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 self_and_parents(&self) -> Vec<&ProtobufAbsPathRef>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 121 impl ProtobufAbsPath { root() -> ProtobufAbsPath122 pub fn root() -> ProtobufAbsPath { 123 ProtobufAbsPathRef::root().to_owned() 124 } 125 as_ref(&self) -> &ProtobufAbsPathRef126 pub fn as_ref(&self) -> &ProtobufAbsPathRef { 127 ProtobufAbsPathRef::new(&self.path) 128 } 129 130 /// If given name is an fully quialified protobuf name. is_abs(path: &str) -> bool131 pub fn is_abs(path: &str) -> bool { 132 path.is_empty() || (path.starts_with(".") && path != ".") 133 } 134 try_new(path: &str) -> Option<ProtobufAbsPath>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 new<S: Into<String>>(path: S) -> ProtobufAbsPath143 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 new_from_rel(path: &str) -> ProtobufAbsPath154 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 package_from_file_proto(file: &FileDescriptorProto) -> ProtobufAbsPath169 pub fn package_from_file_proto(file: &FileDescriptorProto) -> ProtobufAbsPath { 170 Self::new_from_rel(file.package()) 171 } 172 package_from_file_descriptor(file: &FileDescriptor) -> ProtobufAbsPath173 pub fn package_from_file_descriptor(file: &FileDescriptor) -> ProtobufAbsPath { 174 Self::package_from_file_proto(file.proto()) 175 } 176 from_message(message: &MessageDescriptor) -> ProtobufAbsPath177 pub fn from_message(message: &MessageDescriptor) -> ProtobufAbsPath { 178 Self::new_from_rel(&message.full_name()) 179 } 180 concat(a: &ProtobufAbsPathRef, b: &ProtobufRelPathRef) -> ProtobufAbsPath181 pub fn concat(a: &ProtobufAbsPathRef, b: &ProtobufRelPathRef) -> ProtobufAbsPath { 182 let mut a = a.to_owned(); 183 a.push_relative(b); 184 a 185 } 186 from_path_without_dot(path: &str) -> ProtobufAbsPath187 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 from_path_maybe_dot(path: &str) -> ProtobufAbsPath194 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 push_simple(&mut self, simple: &ProtobufIdentRef)202 pub fn push_simple(&mut self, simple: &ProtobufIdentRef) { 203 self.path.push('.'); 204 self.path.push_str(&simple); 205 } 206 push_relative(&mut self, relative: &ProtobufRelPathRef)207 pub fn push_relative(&mut self, relative: &ProtobufRelPathRef) { 208 if !relative.is_empty() { 209 self.path.push_str(&format!(".{}", relative)); 210 } 211 } 212 remove_suffix(&self, suffix: &ProtobufRelPathRef) -> Option<&ProtobufAbsPathRef>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 /// Pop the last name component pop(&mut self) -> Option<ProtobufIdent>231 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 to_root_rel(&self) -> ProtobufRelPath242 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 ends_with(&self, that: &ProtobufRelPath) -> bool250 pub fn ends_with(&self, that: &ProtobufRelPath) -> bool { 251 self.remove_suffix(that).is_some() 252 } 253 } 254 255 impl From<&'_ str> for ProtobufAbsPath { from(s: &str) -> Self256 fn from(s: &str) -> Self { 257 ProtobufAbsPath::new(s.to_owned()) 258 } 259 } 260 261 impl From<String> for ProtobufAbsPath { from(s: String) -> Self262 fn from(s: String) -> Self { 263 ProtobufAbsPath::new(s) 264 } 265 } 266 267 impl fmt::Display for ProtobufAbsPathRef { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result268 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 269 write!(f, "{}", &self.0) 270 } 271 } 272 273 impl fmt::Display for ProtobufAbsPath { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result274 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 275 write!(f, "{}", ProtobufAbsPathRef::new(&self.0)) 276 } 277 } 278 279 #[cfg(test)] 280 mod test { 281 use super::*; 282 283 #[test] absolute_path_push_simple()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] absolute_path_remove_prefix()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] self_and_parents()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] ends_with()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 } 339