use crate::real_path::RealPath;
use crate::virtual_path::VirtualPath;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display};
use std::path::{Path, PathBuf};
#[cfg(any(unix, target_os = "wasi"))]
pub(crate) fn prepare_to_path(path: impl AsRef<Path>) -> PathBuf {
path.as_ref().to_path_buf()
}
#[cfg(any(unix, target_os = "wasi"))]
pub(crate) fn prepare_from_path(path: impl AsRef<Path>) -> PathBuf {
path.as_ref().to_path_buf()
}
#[cfg(windows)]
pub(crate) fn prepare_to_path(path: impl AsRef<Path>) -> PathBuf {
PathBuf::from(path.as_ref().to_string_lossy().replace('\\', "/"))
}
#[cfg(windows)]
pub(crate) fn prepare_from_path(path: impl AsRef<Path>) -> PathBuf {
PathBuf::from(path.as_ref().to_string_lossy().replace('/', "\\"))
}
pub fn sort_paths_list(paths_list: &mut [(PathBuf, PathBuf)]) {
paths_list.sort_by(|a, d| d.0.cmp(&a.0).then(d.1.cmp(&a.1)));
}
pub fn convert_to_virtual_path(
path: impl AsRef<Path> + Debug,
paths_list: &[(PathBuf, PathBuf)],
) -> Option<VirtualPath> {
let path = path.as_ref();
for (host_path, guest_path) in paths_list {
let virtual_path = if path.starts_with(guest_path) {
path.to_owned()
} else if let Ok(rel_path) = path.strip_prefix(host_path) {
guest_path.join(rel_path)
} else {
continue;
};
return Some(VirtualPath::new(prepare_to_path(virtual_path)));
}
None
}
pub fn convert_to_real_path(
path: impl AsRef<Path> + Debug,
paths_list: &[(PathBuf, PathBuf)],
) -> Option<RealPath> {
let path = path.as_ref();
for (host_path, guest_path) in paths_list {
let real_path = if path.starts_with(host_path) {
path.to_owned()
} else if let Ok(rel_path) = path.strip_prefix(guest_path) {
host_path.join(rel_path)
} else {
continue;
};
return Some(RealPath::new(prepare_from_path(real_path)));
}
None
}
pub fn convert_to_real_native_path(
path: impl AsRef<Path> + Debug,
paths_list: &[(PathBuf, PathBuf)],
) -> PathBuf {
let path = path.as_ref();
for (host_path, guest_path) in paths_list {
let real_path = if path.starts_with(host_path) {
path.to_owned()
} else if let Ok(rel_path) = path.strip_prefix(guest_path) {
host_path.join(rel_path)
} else {
continue;
};
return prepare_from_path(real_path);
}
path.to_path_buf()
}
pub struct PathParseError(pub String);
impl Display for PathParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
pub(crate) enum VirtualPathShape {
Virtual {
path: PathBuf,
#[serde(alias = "v")]
virtual_prefix: PathBuf,
#[serde(alias = "r")]
real_prefix: PathBuf,
},
Real(PathBuf),
}
#[doc(hidden)]
#[macro_export]
macro_rules! extend_path_method {
($method:ident) => {
$crate::extend_path_method!($method, std::ffi::OsStr);
};
($method:ident, $ty:path) => {
#[doc = concat!(
"Like [`Path::", stringify!($method), "`] but returns `Self` instead of a path reference."
)]
pub fn $method<S>(&self, value: S) -> Self
where
S: AsRef<$ty>,
{
Self(self.0.$method(value))
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! create_path_type {
($name:ident, $doc:expr) => {
#[doc = $doc]
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(try_from = "VirtualPathShape")]
pub struct $name(PathBuf);
impl $name {
pub fn new(path: impl AsRef<std::ffi::OsStr>) -> Self {
Self(PathBuf::from(path.as_ref()))
}
pub fn is_empty(&self) -> bool {
self.0.as_os_str().is_empty()
}
pub fn into_inner(self) -> PathBuf {
self.0
}
pub fn parent(&self) -> Option<Self> {
if self
.0
.to_str()
.is_some_and(|comp| comp.is_empty() || comp == "/")
{
None
} else {
self.0.parent().map(|p| Self(p.to_path_buf()))
}
}
$crate::extend_path_method!(join, Path);
$crate::extend_path_method!(with_added_extension);
$crate::extend_path_method!(with_extension);
$crate::extend_path_method!(with_file_name);
}
impl Default for $name {
fn default() -> Self {
Self(PathBuf::new())
}
}
impl AsRef<$name> for $name {
fn as_ref(&self) -> &$name {
self
}
}
impl AsRef<PathBuf> for $name {
fn as_ref(&self) -> &PathBuf {
&self.0
}
}
impl AsRef<Path> for $name {
fn as_ref(&self) -> &Path {
&self.0
}
}
impl AsRef<std::ffi::OsStr> for $name {
fn as_ref(&self) -> &std::ffi::OsStr {
self.0.as_os_str()
}
}
impl std::ops::Deref for $name {
type Target = PathBuf;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.display())
}
}
#[cfg(feature = "schematic")]
impl schematic::Schematic for $name {
fn schema_name() -> Option<String> {
Some(stringify!($name).into())
}
fn build_schema(mut schema: schematic::SchemaBuilder) -> schematic::Schema {
schema.set_description($doc);
schema.string(schematic::schema::StringType {
format: Some("path".into()),
..Default::default()
})
}
}
};
}