| |
|
|
| use std::fmt; |
|
|
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct AppServerPath(String); |
|
|
| impl AppServerPath { |
| pub fn from_app_server(path: impl Into<String>) -> Self { |
| Self(path.into()) |
| } |
|
|
| pub fn from_absolute_str(raw: &str) -> Option<Self> { |
| (raw.starts_with('/') || is_windows_absolute_path(raw)).then(|| Self(raw.to_string())) |
| } |
|
|
| pub fn as_str(&self) -> &str { |
| &self.0 |
| } |
|
|
| pub fn components(&self) -> Vec<&str> { |
| let separators = if is_windows_absolute_path(&self.0) { |
| &['/', '\\'][..] |
| } else { |
| &['/'][..] |
| }; |
| self.0 |
| .split(separators) |
| .filter(|part| !part.is_empty()) |
| .collect() |
| } |
|
|
| pub fn join(&self, segment: impl AsRef<str>) -> Self { |
| let is_windows = is_windows_absolute_path(&self.0); |
| let (path, separator) = if is_windows { |
| (self.0.trim_end_matches(['/', '\\']), '\\') |
| } else { |
| (self.0.trim_end_matches('/'), '/') |
| }; |
| Self(format!("{path}{separator}{}", segment.as_ref())) |
| } |
| } |
|
|
| impl fmt::Display for AppServerPath { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| self.0.fmt(f) |
| } |
| } |
|
|
| fn is_windows_absolute_path(path: &str) -> bool { |
| let bytes = path.as_bytes(); |
| (bytes.len() >= 3 |
| && bytes[0].is_ascii_alphabetic() |
| && bytes[1] == b':' |
| && matches!(bytes[2], b'\\' | b'/')) |
| || path.starts_with("\\\\") |
| || path.starts_with("//") |
| } |
|
|