use std::path::{Component, Path, PathBuf};
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use mcplease::session::SessionStore;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct SharedContextData {
pub context_path: Option<PathBuf>,
}
#[derive(Debug, fieldwork::Fieldwork)]
pub struct FsTools {
#[fieldwork(get, get_mut)]
shared_context_store: SessionStore<SharedContextData>,
}
fn normalize_path(path: &Path) -> PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
PathBuf::from(c.as_os_str())
} else {
PathBuf::new()
};
for component in components {
match component {
Component::Prefix(..) => unreachable!(),
Component::RootDir => {
ret.push(component.as_os_str());
}
Component::CurDir => {}
Component::ParentDir => {
ret.pop();
}
Component::Normal(c) => {
ret.push(c);
}
}
}
ret
}
impl FsTools {
pub fn new() -> Result<Self> {
let mut shared_path = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
shared_path.push(".ai-tools");
shared_path.push("sessions");
shared_path.push("shared-context.json");
let shared_context_store = SessionStore::new(Some(shared_path))?;
Ok(Self {
shared_context_store,
})
}
fn default_session_id(&self) -> &'static str {
"default"
}
pub(crate) fn resolve_path(
&mut self,
path_str: &str,
session_id: Option<&str>,
) -> Result<PathBuf> {
let path = PathBuf::from(&*shellexpand::tilde(path_str));
if path.is_absolute() {
return Ok(normalize_path(&path));
}
let session_id = session_id.unwrap_or_else(|| self.default_session_id());
match self.get_context(Some(session_id))? {
Some(context) => Ok(normalize_path(&context.join(path_str))),
None => Err(anyhow!(
"Use set_working_directory first or provide an absolute path.",
)),
}
}
pub fn get_context(&mut self, session_id: Option<&str>) -> Result<Option<PathBuf>> {
let session_data = self
.shared_context_store
.get_or_create(session_id.unwrap_or_else(|| self.default_session_id()))?;
Ok(session_data.context_path.clone())
}
pub(crate) fn set_working_directory(
&mut self,
path: PathBuf,
session_id: Option<&str>,
) -> Result<()> {
self.shared_context_store.update(
session_id.unwrap_or_else(|| self.default_session_id()),
|shared_data| {
shared_data.context_path = Some(path);
},
)
}
}