use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Context, Result};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::project::{Project, ProjectManager, ProjectSource};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct ProjectInfo {
pub id: String,
pub name: String,
pub description: String,
pub source: String,
pub paths: Vec<String>,
pub tags: Vec<String>,
pub emoji: String,
pub memory_visible: bool,
pub created_at: String,
pub updated_at: String,
pub last_active_at: String,
}
impl From<&Project> for ProjectInfo {
fn from(project: &Project) -> Self {
Self {
id: project.id.to_string(),
name: project.name.clone(),
description: project.description.clone(),
source: project.source.to_string(),
paths: project
.paths
.iter()
.map(|p| p.to_string_lossy().to_string())
.collect(),
tags: project.tags.clone(),
emoji: project.emoji.clone(),
memory_visible: project.memory_visible,
created_at: project.created_at.to_rfc3339(),
updated_at: project.updated_at.to_rfc3339(),
last_active_at: project.last_active_at.to_rfc3339(),
}
}
}
#[allow(dead_code)]
pub struct ProjectApi {
pub(crate) project_manager: Arc<ProjectManager>,
}
impl ProjectApi {
pub fn new(project_manager: Arc<ProjectManager>) -> Self {
Self { project_manager }
}
pub fn list_projects(&self) -> Vec<ProjectInfo> {
self.project_manager
.list_projects()
.iter()
.map(ProjectInfo::from)
.collect()
}
pub fn get_project(&self, id: &str) -> Option<ProjectInfo> {
let project_id = Uuid::parse_str(id).ok()?;
self.project_manager
.get_project(project_id)
.as_ref()
.map(ProjectInfo::from)
}
pub fn create_project(
&self,
name: String,
paths: Vec<String>,
tags: Vec<String>,
emoji: Option<String>,
description: Option<String>,
) -> Result<ProjectInfo> {
let paths: Vec<PathBuf> = paths.into_iter().map(PathBuf::from).collect();
let project = self.project_manager.create_project(
name,
paths,
tags,
emoji,
description,
ProjectSource::Manual,
)?;
Ok(ProjectInfo::from(&project))
}
#[allow(clippy::too_many_arguments)]
pub fn update_project(
&self,
id: &str,
name: Option<String>,
paths: Option<Vec<String>>,
tags: Option<Vec<String>>,
emoji: Option<String>,
description: Option<String>,
memory_visible: Option<bool>,
) -> Result<ProjectInfo> {
let project_id = Uuid::parse_str(id).context("Invalid project ID")?;
let paths = paths.map(|v| v.into_iter().map(PathBuf::from).collect());
let mut project = self.project_manager.update_project(
project_id,
name,
paths,
tags,
emoji,
description,
)?;
if let Some(visible) = memory_visible {
project.memory_visible = visible;
project.updated_at = Utc::now();
self.project_manager.save_project(&project)?;
}
Ok(ProjectInfo::from(&project))
}
pub fn remove_project(&self, id: &str) -> Result<()> {
let project_id = Uuid::parse_str(id).context("Invalid project ID")?;
self.project_manager.remove_project(project_id)
}
pub fn link_memory(&self, project_id: &str, memory_id: &str) -> Result<()> {
let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
self.project_manager.link_memory(pid, memory_id)
}
pub fn unlink_memory(&self, project_id: &str, memory_id: &str) -> Result<()> {
let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
self.project_manager.unlink_memory(pid, memory_id)
}
pub fn get_project_memory_ids(&self, project_id: &str) -> Result<Vec<String>> {
let pid = Uuid::parse_str(project_id).context("Invalid project ID")?;
self.project_manager.get_project_memory_ids(pid)
}
}