Files
zesdex/crates/zesdex-cms/src/domain/memory.rs
T

79 lines
2.5 KiB
Rust
Raw Normal View History

//! Pure Memory entity — long-term agent memory with frontmatter metadata
//! and free-form markdown content.
//!
//! # Architecture
//! This is a pure data structure with **no I/O logic**. Load/save
//! responsibilities live in [`MemoryRepository`](super::repository::MemoryRepository).
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
/// A single memory entry (lesson, reference, etc.) with frontmatter
/// metadata and free-form markdown content.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memory {
pub name: String,
pub description: String,
pub content: String,
pub kind: String,
pub created_at: i64,
pub updated_at: i64,
pub outcome: Option<String>,
pub lifecycle: String,
pub scope: Option<String>,
pub before_snippet: Option<String>,
pub after_snippet: Option<String>,
pub provenances: Vec<String>,
}
impl Memory {
/// Convert an arbitrary string into a filesystem-safe slug.
///
/// Flow: lowercase → replace non-alphanumeric chars with `-` →
/// collapse/trim repeated `-`.
///
/// Returns `None` if the result is empty or exceeds 80 characters.
pub fn slugify(s: &str) -> Option<String> {
let slug: String = s
.to_lowercase()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
.collect();
let slug: String = slug
.split('-')
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join("-");
if slug.is_empty() || slug.len() > 80 {
return None;
}
Some(slug)
}
/// Compute the on-disk path for a memory of the given name.
///
/// This is a **pure** computation: it takes `memory_dir` as a parameter
/// and performs no I/O itself.
///
/// Falls back to `"memory.md"` when `name` slugifies to nothing.
pub fn path(memory_dir: &Path, name: &str) -> PathBuf {
let slug = Self::slugify(name).unwrap_or_else(|| "memory".to_string());
let clean: String = format!("{slug}.md")
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || c == '.' || c == '-' {
c
} else {
'-'
}
})
.collect();
let clean = clean.trim_start_matches('.').to_string();
memory_dir.join(if clean.is_empty() {
"memory.md".to_string()
} else {
clean
})
}
}