dx-forge 0.1.3

Production-ready VCS and orchestration engine for DX tools with Git-like versioning, dual-watcher architecture, traffic branch system, and component injection
Documentation
use anyhow::Result;
use chrono::{DateTime, Utc};
use rusqlite::params;
use serde::{Deserialize, Serialize};
use std::path::Path;
use uuid::Uuid;

use crate::storage::Database;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Annotation {
    pub id: Uuid,
    pub file_path: String,
    pub anchor_id: Option<Uuid>,
    pub line: usize,
    pub content: String,
    pub author: String,
    pub created_at: DateTime<Utc>,
    pub is_ai: bool,
}

impl Annotation {
    pub fn new(file_path: String, line: usize, content: String, is_ai: bool) -> Self {
        let author = if is_ai {
            "AI Agent".to_string()
        } else {
            whoami::username()
        };

        Self {
            id: Uuid::new_v4(),
            file_path,
            anchor_id: None,
            line,
            content,
            author,
            created_at: Utc::now(),
            is_ai,
        }
    }
}

pub fn store_annotation(db: &Database, annotation: &Annotation) -> Result<()> {
    let conn = db.conn.lock();

    conn.execute(
        "INSERT INTO annotations (id, file_path, anchor_id, line, content, author, created_at, is_ai)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
        params![
            annotation.id.to_string(),
            annotation.file_path,
            annotation.anchor_id.map(|id| id.to_string()),
            annotation.line as i64,
            annotation.content,
            annotation.author,
            annotation.created_at.to_rfc3339(),
            annotation.is_ai,
        ],
    )?;

    Ok(())
}

pub fn get_annotations(db: &Database, file: &Path, line: Option<usize>) -> Result<Vec<Annotation>> {
    let conn = db.conn.lock();

    let query = if let Some(l) = line {
        format!(
            "SELECT id, file_path, anchor_id, line, content, author, created_at, is_ai
             FROM annotations
             WHERE file_path = '{}' AND line = {}
             ORDER BY created_at DESC",
            file.display(),
            l
        )
    } else {
        format!(
            "SELECT id, file_path, anchor_id, line, content, author, created_at, is_ai
             FROM annotations
             WHERE file_path = '{}'
             ORDER BY created_at DESC",
            file.display()
        )
    };

    let mut stmt = conn.prepare(&query)?;
    let annotations = stmt.query_map([], |row| {
        let id: String = row.get(0)?;
        let file_path: String = row.get(1)?;
        let anchor_id: Option<String> = row.get(2)?;
        let line: i64 = row.get(3)?;
        let content: String = row.get(4)?;
        let author: String = row.get(5)?;
        let created_at: String = row.get(6)?;
        let is_ai: bool = row.get(7)?;

        Ok(Annotation {
            id: Uuid::parse_str(&id).unwrap(),
            file_path,
            anchor_id: anchor_id.as_ref().and_then(|s| Uuid::parse_str(s).ok()),
            line: line as usize,
            content,
            author,
            created_at: chrono::DateTime::parse_from_rfc3339(&created_at)
                .unwrap()
                .into(),
            is_ai,
        })
    })?;

    Ok(annotations.collect::<Result<Vec<_>, _>>()?)
}