Skip to main content

agentic_forge_core/engine/
mod.rs

1//! Engine layer — ForgeEngine, WriteEngine, QueryEngine.
2
3pub mod query;
4pub mod validator;
5pub mod write;
6
7pub use query::QueryEngine;
8pub use write::WriteEngine;
9
10use crate::index::ForgeIndexes;
11use crate::storage::BlueprintStore;
12use crate::types::blueprint::Blueprint;
13use crate::types::ids::BlueprintId;
14use crate::types::intent::Domain;
15use crate::types::ForgeResult;
16
17#[derive(Debug)]
18pub struct ForgeEngine {
19    pub store: BlueprintStore,
20    pub indexes: ForgeIndexes,
21    dirty: bool,
22}
23
24impl ForgeEngine {
25    pub fn new() -> Self {
26        Self {
27            store: BlueprintStore::new(),
28            indexes: ForgeIndexes::new(),
29            dirty: false,
30        }
31    }
32
33    pub fn with_store(store: BlueprintStore) -> Self {
34        Self {
35            store,
36            indexes: ForgeIndexes::new(),
37            dirty: false,
38        }
39    }
40
41    pub fn writer(&mut self) -> WriteEngine<'_> {
42        WriteEngine::new(self)
43    }
44
45    pub fn reader(&self) -> QueryEngine<'_> {
46        QueryEngine::new(self)
47    }
48
49    pub fn is_dirty(&self) -> bool {
50        self.dirty || self.store.is_dirty()
51    }
52
53    pub fn mark_dirty(&mut self) {
54        self.dirty = true;
55    }
56
57    pub fn mark_clean(&mut self) {
58        self.dirty = false;
59        self.store.mark_clean();
60    }
61
62    pub fn create_blueprint(
63        &mut self,
64        name: impl Into<String>,
65        description: impl Into<String>,
66        domain: Domain,
67    ) -> ForgeResult<BlueprintId> {
68        let bp = Blueprint::new(name, description, domain);
69        let id = bp.id;
70        self.store.save(bp)?;
71        self.indexes.add_blueprint(id);
72        self.dirty = true;
73        Ok(id)
74    }
75
76    pub fn blueprint_count(&self) -> usize {
77        self.store.count()
78    }
79}
80
81impl Default for ForgeEngine {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_engine_creation() {
93        let engine = ForgeEngine::new();
94        assert_eq!(engine.blueprint_count(), 0);
95        assert!(!engine.is_dirty());
96    }
97
98    #[test]
99    fn test_engine_create_blueprint() {
100        let mut engine = ForgeEngine::new();
101        let id = engine
102            .create_blueprint("Test", "A test", Domain::Api)
103            .unwrap();
104        assert_eq!(engine.blueprint_count(), 1);
105        assert!(engine.is_dirty());
106        let bp = engine.store.load(&id).unwrap();
107        assert_eq!(bp.name, "Test");
108    }
109
110    #[test]
111    fn test_engine_writer_reader() {
112        let mut engine = ForgeEngine::new();
113        let id = engine
114            .create_blueprint("Test", "A test", Domain::Web)
115            .unwrap();
116        {
117            let reader = engine.reader();
118            let bp = reader.get_blueprint(&id).unwrap();
119            assert_eq!(bp.name, "Test");
120        }
121        {
122            let mut writer = engine.writer();
123            writer.rename_blueprint(&id, "Renamed").unwrap();
124        }
125        let reader = engine.reader();
126        assert_eq!(reader.get_blueprint(&id).unwrap().name, "Renamed");
127    }
128
129    #[test]
130    fn test_engine_dirty_tracking() {
131        let mut engine = ForgeEngine::new();
132        assert!(!engine.is_dirty());
133        engine
134            .create_blueprint("Test", "Test", Domain::Cli)
135            .unwrap();
136        assert!(engine.is_dirty());
137        engine.mark_clean();
138        assert!(!engine.is_dirty());
139    }
140}