use crate::{OrthoError, OrthoResult};
use std::path::{Path, PathBuf};
use super::error::{file_error, invalid_input, not_found};
pub fn canonicalise(p: &Path) -> OrthoResult<PathBuf> {
#[cfg(windows)]
{
dunce::canonicalize(p).map_err(|e| file_error(p, e))
}
#[cfg(not(windows))]
{
std::fs::canonicalize(p).map_err(|e| file_error(p, e))
}
}
pub(super) fn normalize_cycle_key(path: &Path) -> PathBuf {
#[cfg(windows)]
{
use std::ffi::OsString;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
let lowered: Vec<u16> = path
.as_os_str()
.encode_wide()
.map(|unit| {
if (u16::from(b'A')..=u16::from(b'Z')).contains(&unit) {
unit + 32
} else {
unit
}
})
.collect();
PathBuf::from(OsString::from_wide(&lowered))
}
#[cfg(target_os = "macos")]
{
use std::ffi::OsString;
let lowered = match path.as_os_str().to_str() {
Some(text) => text.to_lowercase(),
None => return path.to_path_buf(),
};
PathBuf::from(OsString::from(lowered))
}
#[cfg(not(any(windows, target_os = "macos")))]
{
path.to_path_buf()
}
}
pub(super) fn resolve_base_path(current_path: &Path, base: PathBuf) -> OrthoResult<PathBuf> {
let parent = current_path.parent().ok_or_else(|| {
invalid_input(
current_path,
"Cannot determine parent directory for config file when resolving 'extends'",
)
})?;
let resolved_base = if base.is_absolute() {
base
} else {
canonicalise(parent)?.join(base)
};
match canonicalise(&resolved_base) {
Ok(path) => Ok(path),
Err(err) => {
let OrthoError::File { source, .. } = err.as_ref() else {
return Err(err);
};
let Some(io_err) = source.downcast_ref::<std::io::Error>() else {
return Err(err);
};
if io_err.kind() != std::io::ErrorKind::NotFound {
return Err(err);
}
Err(not_found(
&resolved_base,
format!(
"extended configuration file '{}' does not exist (referenced from '{}')",
resolved_base.display(),
current_path.display()
),
))
}
}
}