use std::path::{Path, PathBuf};
use std::sync::Arc;
use super::error::GoProjectError;
use super::snapshot_error::GoSnapshotError;
use crate::resolution::path_normalization::{RelativePathError, relative_text as lexical_text};
use crate::resolution::paths::RootError;
pub(super) use crate::resolution::paths::path_text;
pub(super) enum PathFault {
OutOfRoot {
path: Box<str>,
root: Box<str>,
},
NonUtf8 {
path: Box<str>,
},
Unreadable {
path: Box<str>,
source: std::io::Error,
},
}
impl From<PathFault> for GoProjectError {
fn from(fault: PathFault) -> Self {
match fault {
PathFault::OutOfRoot { path, root } => Self::OutOfRoot { path, root },
PathFault::NonUtf8 { path } => Self::NonUtf8Path { path },
PathFault::Unreadable { path, source } => Self::PathRead { path, source },
}
}
}
impl From<PathFault> for GoSnapshotError {
fn from(fault: PathFault) -> Self {
match fault {
PathFault::OutOfRoot { path, root } => Self::OutOfRoot { path, root },
PathFault::NonUtf8 { path } => Self::NonUtf8Path { path },
PathFault::Unreadable { path, source } => Self::PathRead { path, source },
}
}
}
pub(super) fn canonical_root(root: &Path) -> Result<PathBuf, GoProjectError> {
crate::resolution::paths::canonical_root(root).map_err(|error| GoProjectError::InvalidRoot {
path: path_text(root),
reason: RootError::reason(error),
})
}
pub(super) fn canonical_in_root(root: &Path, path: &Path) -> Result<Option<PathBuf>, PathFault> {
let canonical = match std::fs::canonicalize(path) {
Ok(canonical) => canonical,
Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(source) => {
return Err(PathFault::Unreadable {
path: path_text(path),
source,
});
}
};
match canonical.starts_with(root) {
true => Ok(Some(canonical)),
false => Err(PathFault::OutOfRoot {
path: path_text(&canonical),
root: path_text(root),
}),
}
}
pub(super) fn file_name(relative: &str) -> &str {
relative.rsplit('/').next().unwrap_or(relative)
}
pub(super) fn relative_text(root: &Path, path: &Path) -> Result<Box<str>, PathFault> {
lexical_text(root, path).map_err(|error| match error {
RelativePathError::OutsideRoot => PathFault::OutOfRoot {
path: path_text(path),
root: path_text(root),
},
RelativePathError::NonUtf8 => PathFault::NonUtf8 {
path: path_text(path),
},
})
}
pub(super) fn relative_shared(root: &Path, path: &Path) -> Result<Arc<str>, PathFault> {
relative_text(root, path).map(Arc::from)
}