app-server-client/src/path.rs
58 lines
//! Paths resolved using the app-server host's platform rules.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("//")}