use super::path_consts::PACK_INSTALLED;
use super::path_consts::{CONFIG_FILE_NAME, PACK_CUSTOM};
use crate::dir_context::path_consts::PACK_DOWNLOAD;
use crate::dir_context::{AipackBaseDir, AipackWksDir};
use crate::support::files::current_dir;
use crate::{Error, Result};
use simple_fs::SPath;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct AipackPaths {
wks_dir: Option<SPath>,
aipack_wks_dir: Option<AipackWksDir>,
aipack_base_dir: AipackBaseDir,
}
impl AipackPaths {
pub fn aipack_wks_dir(&self) -> Option<&AipackWksDir> {
self.aipack_wks_dir.as_ref()
}
}
impl AipackPaths {
pub fn new() -> Result<Self> {
let wks_dir = find_wks_dir(current_dir()?)?;
match wks_dir {
Some(wks_dir) => Self::from_wks_dir(wks_dir),
None => {
let aipack_base_dir = AipackBaseDir::new()?;
Ok(Self {
wks_dir: None,
aipack_wks_dir: None,
aipack_base_dir,
})
}
}
}
pub fn from_wks_dir(wks_path: impl AsRef<Path>) -> Result<Self> {
let wks_path = wks_path.as_ref();
if !wks_path.exists() {
return Err(Error::custom(format!(
"Cannot initialize AipackPaths, potential workspace path does not exist {}",
wks_path.to_string_lossy()
)));
}
let wks_path = wks_path.canonicalize().map_err(|err| {
Error::custom(format!(
"Cannot canonicalize potential wks path for {}: {}",
wks_path.to_string_lossy(),
err
))
})?;
let wks_dir = SPath::from_std_path(wks_path)?;
let aipack_wks_dir = Some(AipackWksDir::new_from_wks_dir(&wks_dir)?);
let aipack_base_dir = AipackBaseDir::new()?;
Ok(Self {
wks_dir: Some(wks_dir),
aipack_wks_dir,
aipack_base_dir,
})
}
}
#[cfg(test)]
impl AipackPaths {
pub fn from_aipack_base_and_wks_dirs(
base_aipack_dir: AipackBaseDir,
wks_dir: SPath, ) -> Result<Self> {
let aipack_wks_dir = AipackWksDir::new_from_wks_dir(&wks_dir)?;
Ok(AipackPaths {
wks_dir: Some(wks_dir),
aipack_wks_dir: Some(aipack_wks_dir),
aipack_base_dir: base_aipack_dir,
})
}
pub fn mock_aipack_wks_dir(path: SPath) -> Result<AipackWksDir> {
AipackWksDir::new_for_test(path)
}
}
impl AipackPaths {
pub fn wks_dir(&self) -> Option<&SPath> {
self.wks_dir.as_ref()
}
#[allow(unused)]
pub fn aipack_base_dir(&self) -> &AipackBaseDir {
&self.aipack_base_dir
}
}
#[derive(Debug, Clone, Copy)]
pub enum RepoKind {
WksCustom,
BaseCustom,
BaseInstalled,
}
impl RepoKind {
pub fn to_pretty_lower(self) -> String {
match self {
Self::WksCustom => "workspace custom - .aipack/pack/custom",
Self::BaseCustom => "base custom - ~/.aipack-base/pack/custom",
Self::BaseInstalled => "base installed - ~/.aipack-base/pack/installed",
}
.to_string()
}
}
#[derive(Debug)]
pub struct PackRepo {
pub kind: RepoKind,
pub path: SPath,
}
impl PackRepo {
pub fn new(kind: RepoKind, path: SPath) -> Self {
Self { kind, path }
}
#[allow(unused)]
pub fn to_str(&self) -> &str {
self.path.as_str()
}
pub fn path(&self) -> &SPath {
&self.path
}
}
impl AipackPaths {
pub fn get_wks_config_toml_paths(&self) -> Result<Vec<SPath>> {
let mut paths = Vec::with_capacity(2);
let base_config_path = self.aipack_base_dir.join(CONFIG_FILE_NAME);
paths.push(base_config_path);
if let Some(aipack_wks_dir) = self.aipack_wks_dir() {
let wks_config_path = aipack_wks_dir.get_config_toml_path()?;
if wks_config_path.exists() {
paths.push(wks_config_path);
}
}
Ok(paths)
}
pub fn get_base_pack_custom_dir(&self) -> Result<SPath> {
let dir = self.aipack_base_dir.join(PACK_CUSTOM);
Ok(dir)
}
pub fn get_base_pack_installed_dir(&self) -> Result<SPath> {
let dir = self.aipack_base_dir.join(PACK_INSTALLED);
Ok(dir)
}
pub fn get_base_pack_download_dir(&self) -> Result<SPath> {
let dir = self.aipack_base_dir.join(PACK_DOWNLOAD);
Ok(dir)
}
pub fn get_pack_repo_dirs(&self) -> Result<Vec<PackRepo>> {
let mut dirs = Vec::new();
if let Some(aipack_wks_dir) = self.aipack_wks_dir() {
let wks_custom = aipack_wks_dir.get_pack_custom_dir()?;
if wks_custom.exists() {
dirs.push(PackRepo::new(RepoKind::WksCustom, wks_custom));
}
}
let base_custom = self.get_base_pack_custom_dir()?;
if base_custom.exists() {
dirs.push(PackRepo::new(RepoKind::BaseCustom, base_custom));
}
let base_installed = self.get_base_pack_installed_dir()?;
if base_installed.exists() {
dirs.push(PackRepo::new(RepoKind::BaseInstalled, base_installed));
}
Ok(dirs)
}
}
pub fn find_wks_dir(from_dir: SPath) -> Result<Option<SPath>> {
let mut current_dir: Option<SPath> = Some(from_dir);
while let Some(parent_dir) = current_dir {
if let Ok(aipack_paths) = AipackPaths::from_wks_dir(&parent_dir) {
if aipack_paths.aipack_wks_dir().is_some() {
return Ok(Some(parent_dir));
}
}
current_dir = parent_dir.parent();
}
Ok(None)
}
#[cfg(test)]
mod tests {
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
use super::*;
use crate::_test_support::assert_ends_with;
use crate::runtime::Runtime;
#[tokio::test]
async fn test_aipack_paths_from_wks_dir_exists() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let aipack_paths = runtime.dir_context().aipack_paths();
assert!(
aipack_paths
.wks_dir()
.ok_or("Should have wks_dir")?
.as_str()
.contains("sandbox-01")
);
assert!(aipack_paths.aipack_wks_dir().is_some());
assert_ends_with(aipack_paths.aipack_wks_dir().unwrap().as_str(), "sandbox-01/.aipack");
let config_paths = aipack_paths.get_wks_config_toml_paths()?;
assert_eq!(config_paths.len(), 2); assert_ends_with(config_paths[0].as_str(), ".aipack-base/config.toml");
assert_ends_with(config_paths[1].as_str(), "sandbox-01/.aipack/config.toml");
let pack_dirs = aipack_paths.get_pack_repo_dirs()?;
assert_eq!(pack_dirs.len(), 3);
assert_ends_with(pack_dirs[0].path().as_str(), ".aipack/pack/custom");
assert_ends_with(pack_dirs[1].path().as_str(), ".aipack-base/pack/custom");
assert_ends_with(pack_dirs[2].path().as_str(), ".aipack-base/pack/installed");
Ok(())
}
#[tokio::test]
async fn test_get_pack_dirs_runtime() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let aipack_paths = runtime.dir_context().aipack_paths();
assert!(aipack_paths.aipack_wks_dir().is_some());
let dirs = aipack_paths.get_pack_repo_dirs()?;
assert_eq!(dirs.len(), 3); assert_eq!(dirs[0].kind as u8, RepoKind::WksCustom as u8);
assert_ends_with(dirs[0].to_str(), ".aipack/pack/custom");
assert_eq!(dirs[1].kind as u8, RepoKind::BaseCustom as u8);
assert_ends_with(dirs[1].to_str(), ".aipack-base/pack/custom");
assert_eq!(dirs[2].kind as u8, RepoKind::BaseInstalled as u8);
assert_ends_with(dirs[2].to_str(), ".aipack-base/pack/installed");
Ok(())
}
}