use std::io;
use std::path::{Path, PathBuf};
use serde::ser::Error as _;
use sqry_core::project::{absolutize_without_resolution, canonicalize_path};
use super::methods::MethodError;
pub fn resolve_index_root(raw: &Path) -> Result<PathBuf, MethodError> {
let absolutised = absolutize_without_resolution(raw).map_err(|e| {
MethodError::InvalidParams(serde_json::Error::custom(format!(
"index_root absolutise: {e}"
)))
})?;
match std::fs::metadata(&absolutised) {
Ok(meta) if meta.is_dir() => canonicalize_path(&absolutised).map_err(|e| {
MethodError::InvalidParams(serde_json::Error::custom(format!(
"index_root canonicalize: {e}"
)))
}),
Ok(_) => Err(MethodError::InvalidParams(serde_json::Error::custom(
"index_root exists but is not a directory",
))),
Err(e) if e.kind() == io::ErrorKind::NotFound => {
Err(MethodError::InvalidParams(serde_json::Error::custom(
"index_root does not exist; daemon/load requires an \
existing directory so a canonical WorkspaceKey can be \
computed",
)))
}
Err(e) => Err(MethodError::InvalidParams(serde_json::Error::custom(
format!("index_root stat: {e}"),
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn existing_directory_canonicalises() {
let tmp = tempdir().unwrap();
let root = tmp.path();
let got = resolve_index_root(root).expect("existing dir must resolve");
assert_eq!(got, canonicalize_path(root).unwrap());
}
#[test]
fn nonexistent_rejected() {
let tmp = tempdir().unwrap();
let ghost = tmp.path().join("does-not-exist");
let err = resolve_index_root(&ghost).unwrap_err();
match err {
MethodError::InvalidParams(e) => {
assert!(e.to_string().contains("does not exist"), "{e}");
}
other => panic!("expected InvalidParams, got {other:?}"),
}
}
#[test]
fn file_not_directory_rejected() {
let tmp = tempdir().unwrap();
let file = tmp.path().join("a.txt");
std::fs::write(&file, b"content").unwrap();
let err = resolve_index_root(&file).unwrap_err();
match err {
MethodError::InvalidParams(e) => {
assert!(e.to_string().contains("not a directory"), "{e}");
}
other => panic!("expected InvalidParams, got {other:?}"),
}
}
#[cfg(unix)]
#[test]
fn symlink_to_directory_dedups_to_target() {
let tmp = tempdir().unwrap();
let real = tmp.path().join("real");
std::fs::create_dir(&real).unwrap();
let link = tmp.path().join("link");
std::os::unix::fs::symlink(&real, &link).unwrap();
let a = resolve_index_root(&link).unwrap();
let b = resolve_index_root(&real).unwrap();
assert_eq!(a, b, "symlink and target must dedup");
}
#[test]
fn relative_path_absolutises_against_cwd() {
let tmp = tempdir().unwrap();
let sub = tmp.path().join("sub");
std::fs::create_dir(&sub).unwrap();
let prev = std::env::current_dir().unwrap();
std::env::set_current_dir(tmp.path()).unwrap();
let got = resolve_index_root(Path::new("sub"));
std::env::set_current_dir(&prev).unwrap();
let got = got.expect("relative existing dir must resolve");
assert!(got.is_absolute(), "result must be absolute: {got:?}");
}
}