use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PalaceId(pub String);
impl PalaceId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for PalaceId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Palace {
pub id: PalaceId,
pub name: String,
pub description: Option<String>,
pub created_at: DateTime<Utc>,
pub data_dir: PathBuf,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Wing {
pub id: Uuid,
pub palace_id: PalaceId,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum RoomType {
Frontend,
Backend,
Testing,
Planning,
Documentation,
Research,
Configuration,
Meetings,
General,
Custom(String),
}
impl RoomType {
pub fn parse(name: &str) -> Self {
match name.to_lowercase().as_str() {
"frontend" => RoomType::Frontend,
"backend" => RoomType::Backend,
"testing" | "tests" | "test" => RoomType::Testing,
"planning" => RoomType::Planning,
"documentation" | "docs" | "doc" => RoomType::Documentation,
"research" => RoomType::Research,
"configuration" | "config" => RoomType::Configuration,
"meetings" | "meeting" => RoomType::Meetings,
"general" | "" => RoomType::General,
other => RoomType::Custom(other.to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Room {
pub id: Uuid,
pub wing_id: Uuid,
pub room_type: RoomType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Drawer {
pub id: Uuid,
pub room_id: Uuid,
pub content: String,
pub importance: f32,
pub source_file: Option<PathBuf>,
pub created_at: DateTime<Utc>,
pub tags: Vec<String>,
#[serde(default)]
pub last_accessed_at: Option<DateTime<Utc>>,
#[serde(default)]
pub access_count: u32,
}
impl Drawer {
pub fn new(room_id: Uuid, content: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
room_id,
content: content.into(),
importance: 0.5,
source_file: None,
created_at: Utc::now(),
tags: Vec::new(),
last_accessed_at: None,
access_count: 0,
}
}
pub fn accumulated_boost(&self, config: &crate::memory_core::decay::DecayConfig) -> f32 {
(self.access_count as f32 * config.access_boost).min(config.access_boost_cap)
}
pub fn record_access(&mut self) {
self.last_accessed_at = Some(Utc::now());
self.access_count = self.access_count.saturating_add(1);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn drawer_new_has_default_importance() {
let d = Drawer::new(Uuid::new_v4(), "hello");
assert_eq!(d.importance, 0.5);
assert_eq!(d.content, "hello");
assert!(d.tags.is_empty());
}
#[test]
fn room_type_parse() {
assert_eq!(RoomType::parse("backend"), RoomType::Backend);
assert_eq!(RoomType::parse("Backend"), RoomType::Backend);
assert_eq!(RoomType::parse("docs"), RoomType::Documentation);
assert_eq!(RoomType::parse("general"), RoomType::General);
assert_eq!(RoomType::parse("ops"), RoomType::Custom("ops".to_string()));
}
#[test]
fn palace_id_display_matches_str() {
let id = PalaceId::new("trusty-memory");
assert_eq!(id.to_string(), "trusty-memory");
assert_eq!(id.as_str(), "trusty-memory");
}
}