Skip to main content

difflore_core/sources/
mod.rs

1//! Cross-vendor ingest sources: detect + read agent memory / rule files.
2
3use std::path::{Path, PathBuf};
4
5use chrono::{DateTime, Utc};
6
7use crate::errors::CoreError;
8
9mod claude_code_memory;
10mod simple_files;
11
12pub use claude_code_memory::ClaudeCodeMemorySource;
13pub use simple_files::{AgentsMdSource, ClaudeMdSource, CursorRulesSource};
14
15#[derive(Debug, Clone)]
16pub struct MemoryDoc {
17    pub source_id: &'static str,
18    pub path: PathBuf,
19    pub content: String,
20    pub modified_at: Option<DateTime<Utc>>,
21}
22
23pub trait Source: Send + Sync {
24    fn id(&self) -> &'static str;
25    fn label(&self) -> &'static str;
26    fn detect(&self, repo_root: &Path) -> bool;
27    fn read(&self, repo_root: &Path) -> Result<Vec<MemoryDoc>, CoreError>;
28}
29
30pub fn registered_sources() -> &'static [&'static dyn Source] {
31    static SOURCES: &[&dyn Source] = &[
32        &AgentsMdSource,
33        &ClaudeMdSource,
34        &ClaudeCodeMemorySource,
35        &CursorRulesSource,
36    ];
37    SOURCES
38}
39
40pub(crate) fn read_file_doc(
41    source_id: &'static str,
42    path: PathBuf,
43) -> Result<MemoryDoc, CoreError> {
44    let content = std::fs::read_to_string(&path)?;
45    let modified_at = std::fs::metadata(&path)
46        .and_then(|m| m.modified())
47        .ok()
48        .map(DateTime::<Utc>::from);
49    Ok(MemoryDoc {
50        source_id,
51        path,
52        content,
53        modified_at,
54    })
55}