use std::path::PathBuf;
use std::process;
use clap::{Parser, Subcommand};
use adaptive_memory::{
DEFAULT_LIMIT, MAX_STRENGTHEN_SET, MemoryError, MemoryStore, SearchParams, default_db_path,
};
#[derive(Parser)]
#[command(name = "adaptive-memory")]
#[command(about = "Adaptive memory system with spreading activation", long_about = None)]
struct Cli {
#[arg(long, global = true)]
db: Option<PathBuf>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Init,
Add {
text: String,
#[arg(short, long)]
source: Option<String>,
#[arg(short, long)]
datetime: Option<String>,
},
Amend {
id: i64,
text: String,
},
Search {
query: String,
#[arg(short, long, default_value_t = DEFAULT_LIMIT)]
limit: usize,
#[arg(long, default_value_t = 0.0)]
decay: f64,
#[arg(long, default_value_t = 0.5)]
energy_decay: f64,
#[arg(short, long, default_value_t = 0)]
context: usize,
},
Strengthen {
ids: String,
},
Connect {
ids: String,
},
Tail {
#[arg(default_value_t = 10)]
n: usize,
},
List {
#[arg(long)]
from: Option<i64>,
#[arg(long)]
to: Option<i64>,
#[arg(short, long)]
limit: Option<usize>,
},
Stats,
Stray {
#[arg(default_value_t = 10)]
n: usize,
},
}
fn main() {
let cli = Cli::parse();
let db_path = cli.db.unwrap_or_else(default_db_path);
let result = run(cli.command, &db_path);
if let Err(e) = result {
eprintln!("Error: {}", e);
process::exit(1);
}
}
fn run(command: Commands, db_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
match command {
Commands::Init => {
let store = MemoryStore::open(db_path)?;
let result = serde_json::json!({
"success": true,
"database": db_path.display().to_string(),
"message": "Database initialized successfully",
"max_memory_id": store.max_memory_id()
});
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Add {
text,
source,
datetime,
} => {
let mut store = MemoryStore::open(db_path)?;
let result = store.add_with_options(&text, source.as_deref(), datetime.as_deref())?;
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Amend { id, text } => {
let mut store = MemoryStore::open(db_path)?;
let result = store.amend(id, &text)?;
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Search {
query,
limit,
decay,
energy_decay,
context,
} => {
let store = MemoryStore::open(db_path)?;
let params = SearchParams {
limit,
decay_factor: decay,
energy_decay,
context,
};
let result = store.search(&query, ¶ms)?;
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Strengthen { ids } => {
let ids = parse_ids(&ids)?;
let mut store = MemoryStore::open(db_path)?;
let result = store.strengthen(&ids)?;
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Connect { ids } => {
let ids = parse_ids(&ids)?;
let mut store = MemoryStore::open(db_path)?;
let result = store.connect(&ids)?;
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Tail { n } => {
let store = MemoryStore::open(db_path)?;
let memories = store.tail(n)?;
let result = serde_json::json!({
"count": memories.len(),
"memories": memories
});
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::List { from, to, limit } => {
let store = MemoryStore::open(db_path)?;
let memories = store.list(from, to, limit)?;
let result = serde_json::json!({
"count": memories.len(),
"memories": memories
});
println!("{}", serde_json::to_string_pretty(&result)?);
}
Commands::Stats => {
let store = MemoryStore::open(db_path)?;
let stats = store.stats()?;
println!("{}", serde_json::to_string_pretty(&stats)?);
}
Commands::Stray { n } => {
let store = MemoryStore::open(db_path)?;
let memories = store.stray(n)?;
let result = serde_json::json!({
"count": memories.len(),
"memories": memories
});
println!("{}", serde_json::to_string_pretty(&result)?);
}
}
Ok(())
}
fn parse_ids(ids_str: &str) -> Result<Vec<i64>, MemoryError> {
let ids: Result<Vec<i64>, _> = ids_str
.split(',')
.map(|s| s.trim().parse::<i64>())
.collect();
let ids = ids.map_err(|e| MemoryError::InvalidInput(format!("Invalid ID format: {}", e)))?;
if ids.is_empty() {
return Err(MemoryError::InvalidInput(
"At least one memory ID is required".to_string(),
));
}
if ids.len() > MAX_STRENGTHEN_SET {
return Err(MemoryError::InvalidInput(format!(
"Cannot strengthen more than {} memories at once (got {})",
MAX_STRENGTHEN_SET,
ids.len()
)));
}
Ok(ids)
}