use std::path::{Path, PathBuf};
use std::sync::Arc;
use super::error::GoProjectError;
use super::snapshot_error::GoSnapshotError;
use crate::resolution::confinement::{ConfinementFault, canonical_inside};
use crate::resolution::path_normalization::{RelativePathError, relative_text as lexical_text};
use crate::resolution::paths::RootError;
pub(super) use crate::resolution::paths::path_text;
const MANIFEST: &str = "go.mod";
pub(super) struct ConfinedManifest {
canonical: PathBuf,
regular: bool,
}
impl ConfinedManifest {
pub(super) fn path(&self) -> &Path {
&self.canonical
}
pub(super) fn is_regular(&self) -> bool {
self.regular
}
}
pub(super) enum PathFault {
OutOfRoot {
path: Box<str>,
root: Box<str>,
request: 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,
request,
} => Self::OutOfRoot {
path,
root,
request,
},
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> {
canonical_inside(root, path).map_err(|fault| match fault {
ConfinementFault::Unreadable(source) => PathFault::Unreadable {
path: path_text(path),
source,
},
ConfinementFault::OutOfRoot { canonical } => PathFault::OutOfRoot {
path: path_text(&canonical),
root: path_text(root),
request: path_text(path),
},
})
}
pub(super) fn confined_manifest(
root: &Path,
directory: &Path,
) -> Result<Option<ConfinedManifest>, PathFault> {
let request = directory.join(MANIFEST);
let Some(manifest) = canonical_in_root(root, &request)? else {
return Ok(None);
};
let metadata = manifest
.metadata()
.map_err(|source| PathFault::Unreadable {
path: path_text(&request),
source,
})?;
Ok(Some(ConfinedManifest {
canonical: manifest,
regular: metadata.is_file(),
}))
}
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| unrelative(error, root, path))
}
pub(super) fn relative_shared(root: &Path, path: &Path) -> Result<Arc<str>, PathFault> {
crate::resolution::path_normalization::relative_shared(root, path)
.map_err(|error| unrelative(error, root, path))
}
pub(super) fn unrelative(error: RelativePathError, root: &Path, path: &Path) -> PathFault {
match error {
RelativePathError::OutsideRoot => PathFault::OutOfRoot {
path: path_text(path),
root: path_text(root),
request: path_text(path),
},
RelativePathError::NonUtf8 => PathFault::NonUtf8 {
path: path_text(path),
},
}
}