Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 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 { |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 8 | /// New ident from a string. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 9 | #[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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 18 | /// Get as a string. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 19 | pub fn get(&self) -> &str { |
| 20 | &self.0 |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | impl From<&'_ str> for ProtobufIdent { |
| 25 | fn from(s: &str) -> Self { |
| 26 | ProtobufIdent::new(s) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl From<String> for ProtobufIdent { |
| 31 | fn from(s: String) -> Self { |
| 32 | ProtobufIdent::new(&s) |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | impl 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 42 | /// Relative protobuf identifier path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 43 | #[derive(Debug, Eq, PartialEq, Clone)] |
| 44 | pub struct ProtobufRelativePath { |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 45 | /// The path |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 46 | pub path: String, |
| 47 | } |
| 48 | |
| 49 | #[allow(dead_code)] |
| 50 | impl ProtobufRelativePath { |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 51 | /// Empty relative path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 52 | pub fn empty() -> ProtobufRelativePath { |
| 53 | ProtobufRelativePath::new(String::new()) |
| 54 | } |
| 55 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 56 | /// New path from a string. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 57 | pub fn new(path: String) -> ProtobufRelativePath { |
| 58 | assert!(!path.starts_with(".")); |
| 59 | |
| 60 | ProtobufRelativePath { path } |
| 61 | } |
| 62 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 63 | /// From path components. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 64 | 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 69 | /// Get the string. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 70 | pub fn get(&self) -> &str { |
| 71 | &self.path |
| 72 | } |
| 73 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 74 | /// The path is empty. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 75 | pub fn is_empty(&self) -> bool { |
| 76 | self.path.is_empty() |
| 77 | } |
| 78 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 79 | /// As absolute path from root namespace. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 80 | 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 114 | /// Self path and parent paths. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 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 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 130 | /// Append path component. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 131 | 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 139 | /// Append identifier to the path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 140 | pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath { |
| 141 | self.append(&ProtobufRelativePath::from(simple.clone())) |
| 142 | } |
| 143 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 144 | /// Get first component path and remaining. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 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 { |
| 164 | fn from(s: &str) -> ProtobufRelativePath { |
| 165 | ProtobufRelativePath::from(s.to_owned()) |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | impl From<String> for ProtobufRelativePath { |
| 170 | fn from(s: String) -> ProtobufRelativePath { |
| 171 | ProtobufRelativePath::new(s) |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | impl From<ProtobufIdent> for ProtobufRelativePath { |
| 176 | fn from(s: ProtobufIdent) -> ProtobufRelativePath { |
| 177 | ProtobufRelativePath::from(s.get()) |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | impl From<Vec<ProtobufIdent>> for ProtobufRelativePath { |
| 182 | fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath { |
| 183 | ProtobufRelativePath::from_components(s.into_iter()) |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | impl 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)] |
| 194 | mod 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 232 | /// Absolute protobuf path (e. g. package). |
| 233 | /// |
| 234 | /// This is not filesystem path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 235 | #[derive(Clone, Eq, PartialEq, Debug, Hash)] |
| 236 | pub struct ProtobufAbsolutePath { |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 237 | /// The path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 238 | pub path: String, |
| 239 | } |
| 240 | |
| 241 | impl ProtobufAbsolutePath { |
| 242 | fn root() -> ProtobufAbsolutePath { |
| 243 | ProtobufAbsolutePath::new(String::new()) |
| 244 | } |
| 245 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 246 | /// From string. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 247 | pub fn new(path: String) -> ProtobufAbsolutePath { |
Joel Galenson | cb467ae | 2021-05-19 16:26:55 -0700 | [diff] [blame] | 248 | assert!(path.is_empty() || path.starts_with("."), "{}", path); |
| 249 | assert!(!path.ends_with("."), "{}", path); |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 250 | ProtobufAbsolutePath { path } |
| 251 | } |
| 252 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 253 | /// The path is empty. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 254 | pub fn is_empty(&self) -> bool { |
| 255 | self.path.is_empty() |
| 256 | } |
| 257 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 258 | /// From a path without leading dot. |
| 259 | /// |
| 260 | /// (Protobuf paths start with dot). |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 261 | 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 271 | /// Parse absolute path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 272 | #[allow(dead_code)] |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 273 | 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 Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 281 | 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 Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 289 | /// Push identifier to the path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 290 | pub fn push_simple(&mut self, simple: ProtobufIdent) { |
| 291 | self.path.push('.'); |
| 292 | self.path.push_str(simple.get()); |
| 293 | } |
| 294 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 295 | /// Push relative path. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 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 | |
Haibo Huang | ba676d3 | 2020-08-12 13:52:13 -0700 | [diff] [blame] | 303 | /// Try remove a prefix. |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 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 { |
| 319 | fn from(s: &str) -> Self { |
| 320 | ProtobufAbsolutePath::new(s.to_owned()) |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | impl From<String> for ProtobufAbsolutePath { |
| 325 | fn from(s: String) -> Self { |
| 326 | ProtobufAbsolutePath::new(s) |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | impl 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)] |
| 337 | mod 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 | } |