use crate::dir_context::aipack_paths::AipackPaths;
use crate::dir_context::resolve_pack_ref_base_path;
use crate::runtime::Session;
use crate::support::files::{current_dir, home_dir};
use crate::types::{PackRef, looks_like_pack_ref};
use crate::{Error, Result};
use simple_fs::SPath;
use std::str::FromStr;
#[allow(clippy::enum_variant_names)] pub enum PathResolver {
CurrentDir,
WksDir,
AipackDir,
}
#[derive(Debug, Clone)]
pub struct DirContext {
home_dir: SPath,
current_dir: SPath,
aipack_paths: AipackPaths,
}
impl DirContext {
pub fn new(aipack_paths: AipackPaths) -> Result<Self> {
let current_dir = current_dir()?;
Self::from_aipack_dir_and_current_dir(aipack_paths, current_dir)
}
fn from_aipack_dir_and_current_dir(aipack_paths: AipackPaths, current_dir: SPath) -> Result<Self> {
let current_dir = current_dir.canonicalize()?;
Ok(Self {
home_dir: home_dir(),
current_dir,
aipack_paths,
})
}
#[cfg(test)]
pub fn from_current_and_aipack_paths(current_dir: SPath, aipack_paths: AipackPaths) -> Result<Self> {
Ok(Self {
home_dir: home_dir(),
current_dir,
aipack_paths,
})
}
}
impl DirContext {
pub fn home_dir(&self) -> &SPath {
&self.home_dir
}
pub fn current_dir(&self) -> &SPath {
&self.current_dir
}
pub fn aipack_paths(&self) -> &AipackPaths {
&self.aipack_paths
}
pub fn wks_dir(&self) -> Option<&SPath> {
self.aipack_paths().wks_dir()
}
pub fn try_wks_dir_with_err_ctx(&self, ctx_msg: &str) -> Result<&SPath> {
self.aipack_paths().wks_dir().ok_or_else(|| {
format!(
"{ctx_msg}.\nCause: No Workspace available.\nDo a 'aip init' in your project root folder to set the '.aipack/' workspace marker folder"
)
.into()
})
}
}
impl DirContext {
pub fn get_display_path(&self, file_path: &str) -> Result<SPath> {
let file_path = SPath::new(file_path);
if file_path.as_str().contains(".aipack-base") {
Ok(file_path)
} else {
let spath = match self.wks_dir() {
Some(wks_dir) => file_path.try_diff(wks_dir)?,
None => file_path,
};
Ok(spath)
}
}
}
impl DirContext {
pub fn resolve_path(
&self,
session: &Session,
path: SPath,
mode: PathResolver,
base_dir: Option<&SPath>,
) -> Result<SPath> {
let path = if path.starts_with("~/") {
path.into_replace_prefix("~", self.home_dir())
} else {
path
};
let final_path = if path.is_absolute() {
path
}
else if self.is_tmp_path(&path) {
self.resolve_tmp_path(session, &path)?
}
else if looks_like_pack_ref(&path) {
let pack_ref = PackRef::from_str(path.as_str())?;
let base_path = resolve_pack_ref_base_path(self, &pack_ref)?;
pack_ref.sub_path.map(|p| base_path.join(p)).unwrap_or(base_path)
}
else {
let base_path = if let Some(base_dir) = base_dir {
Some(base_dir)
} else {
match mode {
PathResolver::CurrentDir => Some(self.current_dir()),
PathResolver::WksDir => {
let wks_dir = self.try_wks_dir_with_err_ctx(&format!(
"Cannot resolve '{path}' for workspace, because no workspace are available"
))?;
Some(wks_dir)
}
PathResolver::AipackDir => {
match self.aipack_paths().aipack_wks_dir() {
Some(dir) => Some(dir.as_ref()), None => {
return Err(Error::custom(format!(
"Cannot resolve path relative to '.aipack' directory because it was not found in workspace '{}'",
self.wks_dir()
.map(|p| p.to_string())
.unwrap_or_else(|| "no workspace found".to_string())
)));
}
}
}
}
};
match base_path {
Some(base) => base.join(path),
None => path, }
};
let path = final_path.into_collapsed();
Ok(path)
}
pub fn is_tmp_path(&self, path: &SPath) -> bool {
path.starts_with("$tmp")
}
pub fn resolve_tmp_path(&self, session: &Session, orig_path: &SPath) -> Result<SPath> {
let path = orig_path
.strip_prefix("$tmp")
.map_err(|_| Error::cc("Path not not a temp path", orig_path.to_string()))?;
let Some(base_dir) = self.aipack_paths().tmp_dir(session) else {
return Err(Error::custom(format!(
"cannot resolve tmp path '{orig_path}'.\nCause: No workspace found"
)));
};
Ok(base_dir.join(path))
}
pub fn maybe_home_path_into_tilde(&self, path: SPath) -> SPath {
if path.is_absolute() && path.starts_with(self.home_dir()) {
path.into_replace_prefix(self.home_dir(), "~")
} else {
path
}
}
pub fn maybe_tilde_path_into_home(&self, path: SPath) -> SPath {
if path.starts_with("~/") {
path.into_replace_prefix("~", self.home_dir())
} else {
path
}
}
}