use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[allow(unused_imports)]
use crate::event_bus::EventBus;
use crate::space::{CrossRefEntry, Space, SpaceManager};
use anyhow::Context;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct SpaceInfo {
pub id: String,
pub name: String,
pub source: String,
pub active: bool,
pub paths: Vec<String>,
pub interaction_count: u64,
pub knowledge_visible: bool,
pub last_active: String,
}
impl From<&Space> for SpaceInfo {
fn from(space: &Space) -> Self {
Self {
id: space.id.to_string(),
name: space.name.clone(),
source: space.source.to_string(),
active: space.active,
paths: space
.paths
.iter()
.map(|p| p.to_string_lossy().to_string())
.collect(),
interaction_count: space.interaction_count,
knowledge_visible: space.knowledge_visible,
last_active: space.last_active_at.to_rfc3339(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct KnowledgeFlowInfo {
pub from: String,
pub to: String,
pub flow_type: String,
pub entry_count: usize,
pub timestamp: String,
}
impl From<&CrossRefEntry> for KnowledgeFlowInfo {
fn from(entry: &CrossRefEntry) -> Self {
Self {
from: entry.from.to_string(),
to: entry.to.to_string(),
flow_type: entry.flow.to_string(),
entry_count: entry.entry_ids.len(),
timestamp: entry.timestamp.to_rfc3339(),
}
}
}
#[allow(dead_code)]
pub struct SpaceApi {
pub(crate) space_manager: Arc<SpaceManager>,
#[allow(dead_code)]
pub(crate) event_bus: EventBus,
}
impl SpaceApi {
pub fn new(space_manager: Arc<SpaceManager>, event_bus: EventBus) -> Self {
Self {
space_manager,
event_bus,
}
}
pub fn list_spaces(&self) -> Vec<SpaceInfo> {
self.space_manager
.list()
.iter()
.map(SpaceInfo::from)
.collect()
}
pub fn current_space(&self) -> Option<SpaceInfo> {
self.space_manager
.current_space()
.as_ref()
.map(SpaceInfo::from)
}
pub async fn get_space(&self, id: &str) -> Option<SpaceInfo> {
let space_id = uuid::Uuid::parse_str(id).ok()?;
self.space_manager
.get_space(&space_id)
.await
.ok()
.flatten()
.as_ref()
.map(SpaceInfo::from)
}
pub async fn activate(&self, id: &str) -> anyhow::Result<()> {
let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
self.space_manager
.activate(&space_id)
.await
.context("Failed to activate Space")
}
pub async fn archive(&self, id: &str) -> anyhow::Result<()> {
let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
let space = self
.space_manager
.get_space(&space_id)
.await?
.context("Space not found")?;
self.space_manager
.activate(&self.space_manager.default_space_id())
.await?;
tracing::info!(space_id = %space_id, name = %space.name, "Space archived");
Ok(())
}
pub async fn merge(&self, survivor_id: &str, absorbed_id: &str) -> anyhow::Result<()> {
let survivor = uuid::Uuid::parse_str(survivor_id).context("Invalid survivor Space ID")?;
let absorbed = uuid::Uuid::parse_str(absorbed_id).context("Invalid absorbed Space ID")?;
self.space_manager
.merge_spaces(survivor, absorbed)
.await
.context("Failed to merge Spaces")
}
pub async fn restore(&self, id: &str) -> anyhow::Result<()> {
let space_id = uuid::Uuid::parse_str(id).context("Invalid Space ID")?;
self.space_manager
.restore_from_archive(&space_id)
.await
.context("Failed to restore Space")
}
pub fn knowledge_flow(&self) -> Vec<KnowledgeFlowInfo> {
self.space_manager
.knowledge_bridge()
.map(|bridge| {
bridge
.recent_references()
.iter()
.map(KnowledgeFlowInfo::from)
.collect()
})
.unwrap_or_default()
}
pub fn knowledge_flow_for(&self, id: &str) -> Option<Vec<KnowledgeFlowInfo>> {
let space_id = uuid::Uuid::parse_str(id).ok()?;
Some(
self.space_manager
.knowledge_bridge()?
.references_for(space_id)
.iter()
.map(KnowledgeFlowInfo::from)
.collect(),
)
}
}