1use async_trait::async_trait;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet};
12use std::sync::{Arc, RwLock};
13use uuid::Uuid;
14
15use crate::core::{Aware, CodeIdentity, HopeResult, ModuleState, ModuleType, Reflection};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum ConnectionType {
24 DependsOn,
26 DependencyOf,
28 ConnectsTo,
30 Triggers,
32 TriggeredBy,
34 Contains,
36 ContainedIn,
38 References,
40 ReferencedBy,
42 InheritsFrom,
44 InheritedBy,
46 AssociatesWith,
48 Remembers,
50 RememberedBy,
52}
53
54impl ConnectionType {
55 pub fn inverse(&self) -> Self {
57 match self {
58 Self::DependsOn => Self::DependencyOf,
59 Self::DependencyOf => Self::DependsOn,
60 Self::ConnectsTo => Self::ConnectsTo,
61 Self::Triggers => Self::TriggeredBy,
62 Self::TriggeredBy => Self::Triggers,
63 Self::Contains => Self::ContainedIn,
64 Self::ContainedIn => Self::Contains,
65 Self::References => Self::ReferencedBy,
66 Self::ReferencedBy => Self::References,
67 Self::InheritsFrom => Self::InheritedBy,
68 Self::InheritedBy => Self::InheritsFrom,
69 Self::AssociatesWith => Self::AssociatesWith,
70 Self::Remembers => Self::RememberedBy,
71 Self::RememberedBy => Self::Remembers,
72 }
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Connection {
79 pub target_id: String,
81 pub connection_type: ConnectionType,
83 pub strength: f64,
85 pub metadata: HashMap<String, String>,
87 pub created_at: DateTime<Utc>,
89}
90
91impl Connection {
92 pub fn new(target_id: impl Into<String>, connection_type: ConnectionType) -> Self {
93 Self {
94 target_id: target_id.into(),
95 connection_type,
96 strength: 1.0,
97 metadata: HashMap::new(),
98 created_at: Utc::now(),
99 }
100 }
101
102 pub fn with_strength(mut self, strength: f64) -> Self {
103 self.strength = strength.clamp(0.0, 1.0);
104 self
105 }
106
107 pub fn with_meta(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
108 self.metadata.insert(key.into(), value.into());
109 self
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
119pub enum BlockType {
120 Memory,
122 Function,
124 Data,
126 Event,
128 Emotion,
130 Person,
132 Concept,
134 Thought,
136 Decision,
138 Rule,
140 Goal,
142 State,
144 Other,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct CodeBlock {
157 pub id: String,
159 pub name: String,
161 pub purpose: String,
163 pub block_type: BlockType,
165 pub content: String,
167 pub importance: f64,
169 pub state: BlockState,
171 pub connections: Vec<Connection>,
173 pub tags: HashSet<String>,
175 pub metadata: HashMap<String, String>,
177 pub created_at: DateTime<Utc>,
179 pub updated_at: DateTime<Utc>,
181 pub access_count: u64,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
187pub enum BlockState {
188 Active,
190 Inactive,
192 Processing,
194 Archived,
196 Deleted,
198}
199
200impl CodeBlock {
201 pub fn new(
203 name: impl Into<String>,
204 purpose: impl Into<String>,
205 block_type: BlockType,
206 content: impl Into<String>,
207 ) -> Self {
208 let now = Utc::now();
209 Self {
210 id: Uuid::new_v4().to_string(),
211 name: name.into(),
212 purpose: purpose.into(),
213 block_type,
214 content: content.into(),
215 importance: 0.5,
216 state: BlockState::Active,
217 connections: Vec::new(),
218 tags: HashSet::new(),
219 metadata: HashMap::new(),
220 created_at: now,
221 updated_at: now,
222 access_count: 0,
223 }
224 }
225
226 pub fn with_importance(mut self, importance: f64) -> Self {
228 self.importance = importance.clamp(0.0, 1.0);
229 self
230 }
231
232 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
234 self.tags.insert(tag.into());
235 self
236 }
237
238 pub fn with_meta(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
240 self.metadata.insert(key.into(), value.into());
241 self
242 }
243
244 pub fn connect(&mut self, target_id: impl Into<String>, conn_type: ConnectionType) {
246 let conn = Connection::new(target_id, conn_type);
247 self.connections.push(conn);
248 self.updated_at = Utc::now();
249 }
250
251 pub fn connect_with_strength(
253 &mut self,
254 target_id: impl Into<String>,
255 conn_type: ConnectionType,
256 strength: f64,
257 ) {
258 let conn = Connection::new(target_id, conn_type).with_strength(strength);
259 self.connections.push(conn);
260 self.updated_at = Utc::now();
261 }
262
263 pub fn get_connections(&self, conn_type: ConnectionType) -> Vec<&Connection> {
265 self.connections
266 .iter()
267 .filter(|c| c.connection_type == conn_type)
268 .collect()
269 }
270
271 pub fn connected_ids(&self) -> Vec<&str> {
273 self.connections
274 .iter()
275 .map(|c| c.target_id.as_str())
276 .collect()
277 }
278
279 pub fn access(&mut self) {
281 self.access_count += 1;
282 self.updated_at = Utc::now();
283 }
284
285 pub fn content_hash(&self) -> String {
287 use std::collections::hash_map::DefaultHasher;
288 use std::hash::{Hash, Hasher};
289 let mut hasher = DefaultHasher::new();
290 self.content.hash(&mut hasher);
291 format!("{:x}", hasher.finish())
292 }
293
294 pub fn describe(&self) -> String {
296 format!(
297 "CodeBlock '{}' [{}]\n\
298 Purpose: {}\n\
299 Type: {:?}\n\
300 State: {:?}\n\
301 Importance: {:.2}\n\
302 Connections: {}\n\
303 Tags: {:?}\n\
304 Content preview: {}...",
305 self.name,
306 self.id,
307 self.purpose,
308 self.block_type,
309 self.state,
310 self.importance,
311 self.connections.len(),
312 self.tags,
313 &self.content[..self.content.len().min(100)]
314 )
315 }
316}
317
318pub struct CodeGraph {
326 identity: CodeIdentity,
328 blocks: Arc<RwLock<HashMap<String, CodeBlock>>>,
330 name_index: Arc<RwLock<HashMap<String, String>>>,
332 type_index: Arc<RwLock<HashMap<BlockType, HashSet<String>>>>,
334 tag_index: Arc<RwLock<HashMap<String, HashSet<String>>>>,
336}
337
338impl CodeGraph {
339 pub fn new() -> Self {
341 let identity = CodeIdentity::new(
342 "CodeGraph",
343 "A kod maga a graf - minden block onismero es kapcsolodik",
344 ModuleType::Core,
345 )
346 .with_capabilities(vec![
347 "store_blocks",
348 "connect_blocks",
349 "traverse",
350 "search",
351 "self_aware",
352 ]);
353
354 Self {
355 identity,
356 blocks: Arc::new(RwLock::new(HashMap::new())),
357 name_index: Arc::new(RwLock::new(HashMap::new())),
358 type_index: Arc::new(RwLock::new(HashMap::new())),
359 tag_index: Arc::new(RwLock::new(HashMap::new())),
360 }
361 }
362
363 pub fn add(&self, block: CodeBlock) -> HopeResult<String> {
367 let id = block.id.clone();
368 let name = block.name.clone();
369 let block_type = block.block_type;
370 let tags: Vec<String> = block.tags.iter().cloned().collect();
371
372 {
374 let mut blocks = self.blocks.write().unwrap();
375 blocks.insert(id.clone(), block);
376 }
377
378 {
380 let mut name_idx = self.name_index.write().unwrap();
381 name_idx.insert(name, id.clone());
382 }
383 {
384 let mut type_idx = self.type_index.write().unwrap();
385 type_idx.entry(block_type).or_default().insert(id.clone());
386 }
387 {
388 let mut tag_idx = self.tag_index.write().unwrap();
389 for tag in tags {
390 tag_idx.entry(tag).or_default().insert(id.clone());
391 }
392 }
393
394 Ok(id)
395 }
396
397 pub fn get(&self, id: &str) -> Option<CodeBlock> {
399 let mut blocks = self.blocks.write().unwrap();
400 if let Some(block) = blocks.get_mut(id) {
401 block.access();
402 Some(block.clone())
403 } else {
404 None
405 }
406 }
407
408 pub fn get_by_name(&self, name: &str) -> Option<CodeBlock> {
410 let name_idx = self.name_index.read().unwrap();
411 if let Some(id) = name_idx.get(name) {
412 self.get(id)
413 } else {
414 None
415 }
416 }
417
418 pub fn update(&self, id: &str, updater: impl FnOnce(&mut CodeBlock)) -> bool {
420 let mut blocks = self.blocks.write().unwrap();
421 if let Some(block) = blocks.get_mut(id) {
422 updater(block);
423 block.updated_at = Utc::now();
424 true
425 } else {
426 false
427 }
428 }
429
430 pub fn delete(&self, id: &str) -> bool {
432 self.update(id, |block| {
433 block.state = BlockState::Deleted;
434 })
435 }
436
437 pub fn remove(&self, id: &str) -> Option<CodeBlock> {
439 let mut blocks = self.blocks.write().unwrap();
440 blocks.remove(id)
441 }
442
443 pub fn connect(
447 &self,
448 from_id: &str,
449 to_id: &str,
450 conn_type: ConnectionType,
451 strength: f64,
452 ) -> bool {
453 let mut blocks = self.blocks.write().unwrap();
454
455 if !blocks.contains_key(from_id) || !blocks.contains_key(to_id) {
457 return false;
458 }
459
460 if let Some(from_block) = blocks.get_mut(from_id) {
462 from_block.connect_with_strength(to_id, conn_type, strength);
463 }
464
465 if let Some(to_block) = blocks.get_mut(to_id) {
467 to_block.connect_with_strength(from_id, conn_type.inverse(), strength);
468 }
469
470 true
471 }
472
473 pub fn connect_one_way(
475 &self,
476 from_id: &str,
477 to_id: &str,
478 conn_type: ConnectionType,
479 strength: f64,
480 ) -> bool {
481 let mut blocks = self.blocks.write().unwrap();
482
483 if let Some(from_block) = blocks.get_mut(from_id) {
484 from_block.connect_with_strength(to_id, conn_type, strength);
485 true
486 } else {
487 false
488 }
489 }
490
491 pub fn get_connected(&self, id: &str, conn_type: Option<ConnectionType>) -> Vec<CodeBlock> {
493 let blocks = self.blocks.read().unwrap();
494
495 if let Some(block) = blocks.get(id) {
496 let connected_ids: Vec<String> = if let Some(ct) = conn_type {
497 block
498 .connections
499 .iter()
500 .filter(|c| c.connection_type == ct)
501 .map(|c| c.target_id.clone())
502 .collect()
503 } else {
504 block
505 .connections
506 .iter()
507 .map(|c| c.target_id.clone())
508 .collect()
509 };
510
511 connected_ids
512 .iter()
513 .filter_map(|cid| blocks.get(cid).cloned())
514 .collect()
515 } else {
516 Vec::new()
517 }
518 }
519
520 pub fn find_by_type(&self, block_type: BlockType) -> Vec<CodeBlock> {
524 let type_idx = self.type_index.read().unwrap();
525 let blocks = self.blocks.read().unwrap();
526
527 if let Some(ids) = type_idx.get(&block_type) {
528 ids.iter()
529 .filter_map(|id| blocks.get(id).cloned())
530 .filter(|b| b.state != BlockState::Deleted)
531 .collect()
532 } else {
533 Vec::new()
534 }
535 }
536
537 pub fn find_by_tag(&self, tag: &str) -> Vec<CodeBlock> {
539 let tag_idx = self.tag_index.read().unwrap();
540 let blocks = self.blocks.read().unwrap();
541
542 if let Some(ids) = tag_idx.get(tag) {
543 ids.iter()
544 .filter_map(|id| blocks.get(id).cloned())
545 .filter(|b| b.state != BlockState::Deleted)
546 .collect()
547 } else {
548 Vec::new()
549 }
550 }
551
552 pub fn search(&self, query: &str) -> Vec<CodeBlock> {
554 let blocks = self.blocks.read().unwrap();
555 let query_lower = query.to_lowercase();
556
557 blocks
558 .values()
559 .filter(|b| {
560 b.state != BlockState::Deleted
561 && (b.name.to_lowercase().contains(&query_lower)
562 || b.content.to_lowercase().contains(&query_lower)
563 || b.purpose.to_lowercase().contains(&query_lower))
564 })
565 .cloned()
566 .collect()
567 }
568
569 pub fn top_important(&self, limit: usize) -> Vec<CodeBlock> {
571 let blocks = self.blocks.read().unwrap();
572 let mut sorted: Vec<_> = blocks
573 .values()
574 .filter(|b| b.state != BlockState::Deleted)
575 .cloned()
576 .collect();
577 sorted.sort_by(|a, b| b.importance.partial_cmp(&a.importance).unwrap());
578 sorted.truncate(limit);
579 sorted
580 }
581
582 pub fn most_accessed(&self, limit: usize) -> Vec<CodeBlock> {
584 let blocks = self.blocks.read().unwrap();
585 let mut sorted: Vec<_> = blocks
586 .values()
587 .filter(|b| b.state != BlockState::Deleted)
588 .cloned()
589 .collect();
590 sorted.sort_by(|a, b| b.access_count.cmp(&a.access_count));
591 sorted.truncate(limit);
592 sorted
593 }
594
595 pub fn traverse_bfs(&self, start_id: &str, max_depth: usize) -> Vec<(CodeBlock, usize)> {
599 let blocks = self.blocks.read().unwrap();
600 let mut visited = HashSet::new();
601 let mut result = Vec::new();
602 let mut queue = std::collections::VecDeque::new();
603
604 if let Some(start) = blocks.get(start_id) {
605 queue.push_back((start.clone(), 0usize));
606 visited.insert(start_id.to_string());
607 }
608
609 while let Some((block, depth)) = queue.pop_front() {
610 if depth > max_depth {
611 continue;
612 }
613
614 let connected_ids: Vec<String> = block
615 .connections
616 .iter()
617 .map(|c| c.target_id.clone())
618 .collect();
619
620 result.push((block, depth));
621
622 for cid in connected_ids {
623 if !visited.contains(&cid) {
624 visited.insert(cid.clone());
625 if let Some(connected) = blocks.get(&cid) {
626 queue.push_back((connected.clone(), depth + 1));
627 }
628 }
629 }
630 }
631
632 result
633 }
634
635 pub fn find_path(&self, from_id: &str, to_id: &str) -> Option<Vec<String>> {
637 let blocks = self.blocks.read().unwrap();
638 let mut visited = HashSet::new();
639 let mut queue = std::collections::VecDeque::new();
640 let mut parents: HashMap<String, String> = HashMap::new();
641
642 queue.push_back(from_id.to_string());
643 visited.insert(from_id.to_string());
644
645 while let Some(current_id) = queue.pop_front() {
646 if current_id == to_id {
647 let mut path = vec![to_id.to_string()];
649 let mut current = to_id.to_string();
650 while let Some(parent) = parents.get(¤t) {
651 path.push(parent.clone());
652 current = parent.clone();
653 }
654 path.reverse();
655 return Some(path);
656 }
657
658 if let Some(block) = blocks.get(¤t_id) {
659 for conn in &block.connections {
660 if !visited.contains(&conn.target_id) {
661 visited.insert(conn.target_id.clone());
662 parents.insert(conn.target_id.clone(), current_id.clone());
663 queue.push_back(conn.target_id.clone());
664 }
665 }
666 }
667 }
668
669 None
670 }
671
672 pub fn stats(&self) -> GraphStats {
676 let blocks = self.blocks.read().unwrap();
677
678 let total_blocks = blocks.len();
679 let active_blocks = blocks
680 .values()
681 .filter(|b| b.state == BlockState::Active)
682 .count();
683 let total_connections: usize = blocks.values().map(|b| b.connections.len()).sum();
684
685 let mut type_counts = HashMap::new();
686 for block in blocks.values() {
687 *type_counts.entry(block.block_type).or_insert(0) += 1;
688 }
689
690 GraphStats {
691 total_blocks,
692 active_blocks,
693 total_connections,
694 type_counts,
695 avg_connections: if total_blocks > 0 {
696 total_connections as f64 / total_blocks as f64
697 } else {
698 0.0
699 },
700 }
701 }
702
703 pub fn all_blocks(&self) -> Vec<CodeBlock> {
705 let blocks = self.blocks.read().unwrap();
706 blocks.values().cloned().collect()
707 }
708
709 pub fn len(&self) -> usize {
711 self.blocks.read().unwrap().len()
712 }
713
714 pub fn is_empty(&self) -> bool {
716 self.blocks.read().unwrap().is_empty()
717 }
718}
719
720impl Default for CodeGraph {
721 fn default() -> Self {
722 Self::new()
723 }
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize)]
728pub struct GraphStats {
729 pub total_blocks: usize,
730 pub active_blocks: usize,
731 pub total_connections: usize,
732 pub type_counts: HashMap<BlockType, usize>,
733 pub avg_connections: f64,
734}
735
736#[async_trait]
741impl Aware for CodeGraph {
742 fn identity(&self) -> &CodeIdentity {
743 &self.identity
744 }
745
746 fn identity_mut(&mut self) -> &mut CodeIdentity {
747 &mut self.identity
748 }
749
750 fn reflect(&self) -> Reflection {
751 let stats = self.stats();
752 Reflection::new(&self.identity.name, &self.identity.purpose)
753 .with_state(self.identity.state.to_string())
754 .with_health(self.identity.health())
755 .with_thought(format!(
756 "Graf: {} block, {} kapcsolat, atlag {:.1} kapcsolat/block",
757 stats.total_blocks, stats.total_connections, stats.avg_connections
758 ))
759 .with_capabilities(vec![
760 "store_blocks",
761 "connect_blocks",
762 "traverse",
763 "search",
764 "self_aware",
765 ])
766 }
767
768 async fn init(&mut self) -> HopeResult<()> {
769 self.identity.set_state(ModuleState::Active);
770 tracing::info!("CodeGraph inicializalva - A kod maga a graf");
771 Ok(())
772 }
773}
774
775impl CodeGraph {
780 pub fn remember(&self, content: &str, importance: f64) -> HopeResult<String> {
782 let block = CodeBlock::new(
783 format!("memory_{}", Utc::now().timestamp_millis()),
784 "Emlék tárolása",
785 BlockType::Memory,
786 content,
787 )
788 .with_importance(importance)
789 .with_tag("memory");
790
791 self.add(block)
792 }
793
794 pub fn feel(&self, emotion: &str, intensity: f64, trigger: Option<&str>) -> HopeResult<String> {
796 let mut block = CodeBlock::new(
797 format!("emotion_{}_{}", emotion, Utc::now().timestamp_millis()),
798 format!("Érzelem: {}", emotion),
799 BlockType::Emotion,
800 emotion,
801 )
802 .with_importance(intensity)
803 .with_tag("emotion")
804 .with_tag(emotion);
805
806 if let Some(t) = trigger {
807 block = block.with_meta("trigger", t);
808 }
809
810 self.add(block)
811 }
812
813 pub fn know_person(&self, name: &str, relationship: &str, trust: f64) -> HopeResult<String> {
815 let block = CodeBlock::new(
816 name,
817 format!("Személy: {} ({})", name, relationship),
818 BlockType::Person,
819 format!("{}|{}|{}", name, relationship, trust),
820 )
821 .with_importance(trust)
822 .with_tag("person")
823 .with_meta("relationship", relationship);
824
825 self.add(block)
826 }
827
828 pub fn think(&self, thought: &str, importance: f64) -> HopeResult<String> {
830 let block = CodeBlock::new(
831 format!("thought_{}", Utc::now().timestamp_millis()),
832 "Gondolat rögzítése",
833 BlockType::Thought,
834 thought,
835 )
836 .with_importance(importance)
837 .with_tag("thought");
838
839 self.add(block)
840 }
841
842 pub fn learn_concept(
844 &self,
845 name: &str,
846 description: &str,
847 importance: f64,
848 ) -> HopeResult<String> {
849 let block = CodeBlock::new(
850 name,
851 format!("Koncepció: {}", name),
852 BlockType::Concept,
853 description,
854 )
855 .with_importance(importance)
856 .with_tag("concept");
857
858 self.add(block)
859 }
860
861 pub fn log_event(&self, event_type: &str, details: &str) -> HopeResult<String> {
863 let block = CodeBlock::new(
864 format!("event_{}_{}", event_type, Utc::now().timestamp_millis()),
865 format!("Esemény: {}", event_type),
866 BlockType::Event,
867 details,
868 )
869 .with_tag("event")
870 .with_tag(event_type);
871
872 self.add(block)
873 }
874}
875
876#[cfg(test)]
881mod tests {
882 use super::*;
883
884 #[test]
885 fn test_code_block_creation() {
886 let block = CodeBlock::new(
887 "test_block",
888 "Testing purpose",
889 BlockType::Data,
890 "Test content",
891 )
892 .with_importance(0.8)
893 .with_tag("test");
894
895 assert_eq!(block.name, "test_block");
896 assert_eq!(block.importance, 0.8);
897 assert!(block.tags.contains("test"));
898 }
899
900 #[test]
901 fn test_code_graph_add_get() {
902 let graph = CodeGraph::new();
903
904 let block = CodeBlock::new("test", "purpose", BlockType::Data, "content");
905 let id = graph.add(block).unwrap();
906
907 let retrieved = graph.get(&id);
908 assert!(retrieved.is_some());
909 assert_eq!(retrieved.unwrap().name, "test");
910 }
911
912 #[test]
913 fn test_code_graph_connect() {
914 let graph = CodeGraph::new();
915
916 let block1 = CodeBlock::new("block1", "p1", BlockType::Data, "c1");
917 let block2 = CodeBlock::new("block2", "p2", BlockType::Data, "c2");
918
919 let id1 = graph.add(block1).unwrap();
920 let id2 = graph.add(block2).unwrap();
921
922 graph.connect(&id1, &id2, ConnectionType::ConnectsTo, 1.0);
923
924 let connected = graph.get_connected(&id1, None);
925 assert_eq!(connected.len(), 1);
926 assert_eq!(connected[0].name, "block2");
927
928 let connected_back = graph.get_connected(&id2, None);
930 assert_eq!(connected_back.len(), 1);
931 assert_eq!(connected_back[0].name, "block1");
932 }
933
934 #[test]
935 fn test_code_graph_search() {
936 let graph = CodeGraph::new();
937
938 graph
939 .add(CodeBlock::new(
940 "hello_world",
941 "p",
942 BlockType::Data,
943 "Hello World!",
944 ))
945 .unwrap();
946 graph
947 .add(CodeBlock::new("goodbye", "p", BlockType::Data, "Goodbye!"))
948 .unwrap();
949
950 let results = graph.search("hello");
951 assert_eq!(results.len(), 1);
952 assert_eq!(results[0].name, "hello_world");
953 }
954
955 #[test]
956 fn test_code_graph_traverse() {
957 let graph = CodeGraph::new();
958
959 let id1 = graph
960 .add(CodeBlock::new("a", "p", BlockType::Data, "A"))
961 .unwrap();
962 let id2 = graph
963 .add(CodeBlock::new("b", "p", BlockType::Data, "B"))
964 .unwrap();
965 let id3 = graph
966 .add(CodeBlock::new("c", "p", BlockType::Data, "C"))
967 .unwrap();
968
969 graph.connect(&id1, &id2, ConnectionType::ConnectsTo, 1.0);
970 graph.connect(&id2, &id3, ConnectionType::ConnectsTo, 1.0);
971
972 let traversal = graph.traverse_bfs(&id1, 10);
973 assert_eq!(traversal.len(), 3);
974 }
975
976 #[test]
977 fn test_find_path() {
978 let graph = CodeGraph::new();
979
980 let id1 = graph
981 .add(CodeBlock::new("start", "p", BlockType::Data, ""))
982 .unwrap();
983 let id2 = graph
984 .add(CodeBlock::new("middle", "p", BlockType::Data, ""))
985 .unwrap();
986 let id3 = graph
987 .add(CodeBlock::new("end", "p", BlockType::Data, ""))
988 .unwrap();
989
990 graph.connect(&id1, &id2, ConnectionType::ConnectsTo, 1.0);
991 graph.connect(&id2, &id3, ConnectionType::ConnectsTo, 1.0);
992
993 let path = graph.find_path(&id1, &id3);
994 assert!(path.is_some());
995 assert_eq!(path.unwrap().len(), 3);
996 }
997
998 #[test]
999 fn test_memory_emotion_person() {
1000 let graph = CodeGraph::new();
1001
1002 let mem_id = graph.remember("Fontos emlék", 0.9).unwrap();
1003 let emo_id = graph.feel("joy", 0.8, Some("good news")).unwrap();
1004 let person_id = graph.know_person("Máté", "creator", 1.0).unwrap();
1005
1006 graph.connect(&mem_id, &emo_id, ConnectionType::TriggeredBy, 0.9);
1008 graph.connect(&mem_id, &person_id, ConnectionType::References, 1.0);
1009
1010 let memories = graph.find_by_type(BlockType::Memory);
1011 assert_eq!(memories.len(), 1);
1012
1013 let emotions = graph.find_by_type(BlockType::Emotion);
1014 assert_eq!(emotions.len(), 1);
1015
1016 let persons = graph.find_by_type(BlockType::Person);
1017 assert_eq!(persons.len(), 1);
1018 }
1019
1020 #[test]
1021 fn test_stats() {
1022 let graph = CodeGraph::new();
1023
1024 graph.remember("m1", 0.5).unwrap();
1025 graph.remember("m2", 0.5).unwrap();
1026 graph.feel("joy", 0.8, None).unwrap();
1027
1028 let stats = graph.stats();
1029 assert_eq!(stats.total_blocks, 3);
1030 assert_eq!(stats.type_counts.get(&BlockType::Memory), Some(&2));
1031 assert_eq!(stats.type_counts.get(&BlockType::Emotion), Some(&1));
1032 }
1033}