ringkernel_procint/models/
activity.rs

1//! Activity definitions for process mining.
2
3use rkyv::{Archive, Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Activity identifier type.
7pub type ActivityId = u32;
8
9/// Activity definition with metadata.
10#[derive(Debug, Clone, Archive, Serialize, Deserialize)]
11pub struct Activity {
12    /// Unique activity identifier.
13    pub id: ActivityId,
14    /// Activity name.
15    pub name: String,
16    /// Activity category/type.
17    pub category: ActivityCategory,
18    /// Expected duration in milliseconds.
19    pub expected_duration_ms: u32,
20    /// Cost per execution.
21    pub cost: f32,
22    /// Required resources.
23    pub required_resources: Vec<String>,
24}
25
26impl Activity {
27    /// Create a new activity.
28    pub fn new(id: ActivityId, name: impl Into<String>) -> Self {
29        Self {
30            id,
31            name: name.into(),
32            category: ActivityCategory::Task,
33            expected_duration_ms: 0,
34            cost: 0.0,
35            required_resources: Vec::new(),
36        }
37    }
38
39    /// Set the expected duration.
40    pub fn with_duration(mut self, duration_ms: u32) -> Self {
41        self.expected_duration_ms = duration_ms;
42        self
43    }
44
45    /// Set the category.
46    pub fn with_category(mut self, category: ActivityCategory) -> Self {
47        self.category = category;
48        self
49    }
50}
51
52/// Activity category classification.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Archive, Serialize, Deserialize)]
54#[repr(u8)]
55pub enum ActivityCategory {
56    /// Start event.
57    Start = 0,
58    /// End event.
59    End = 1,
60    /// Regular task/activity.
61    #[default]
62    Task = 2,
63    /// Decision/gateway.
64    Gateway = 3,
65    /// Sub-process.
66    SubProcess = 4,
67    /// System/automated task.
68    System = 5,
69    /// Manual/human task.
70    Manual = 6,
71}
72
73/// Activity registry for managing activity definitions.
74#[derive(Debug, Clone, Default)]
75pub struct ActivityRegistry {
76    /// Activities by ID.
77    activities: HashMap<ActivityId, Activity>,
78    /// Name to ID mapping.
79    name_to_id: HashMap<String, ActivityId>,
80    /// Next available ID.
81    next_id: ActivityId,
82}
83
84impl ActivityRegistry {
85    /// Create a new empty registry.
86    pub fn new() -> Self {
87        Self::default()
88    }
89
90    /// Register an activity.
91    pub fn register(&mut self, activity: Activity) -> ActivityId {
92        let id = activity.id;
93        self.name_to_id.insert(activity.name.clone(), id);
94        self.activities.insert(id, activity);
95        self.next_id = self.next_id.max(id + 1);
96        id
97    }
98
99    /// Create and register a new activity by name.
100    pub fn get_or_create(&mut self, name: &str) -> ActivityId {
101        if let Some(&id) = self.name_to_id.get(name) {
102            id
103        } else {
104            let id = self.next_id;
105            self.next_id += 1;
106            let activity = Activity::new(id, name);
107            self.register(activity)
108        }
109    }
110
111    /// Get activity by ID.
112    pub fn get(&self, id: ActivityId) -> Option<&Activity> {
113        self.activities.get(&id)
114    }
115
116    /// Get activity by name.
117    pub fn get_by_name(&self, name: &str) -> Option<&Activity> {
118        self.name_to_id
119            .get(name)
120            .and_then(|id| self.activities.get(id))
121    }
122
123    /// Get activity name by ID.
124    pub fn get_name(&self, id: ActivityId) -> Option<&str> {
125        self.activities.get(&id).map(|a| a.name.as_str())
126    }
127
128    /// Get all activities.
129    pub fn all(&self) -> impl Iterator<Item = &Activity> {
130        self.activities.values()
131    }
132
133    /// Number of registered activities.
134    pub fn len(&self) -> usize {
135        self.activities.len()
136    }
137
138    /// Check if registry is empty.
139    pub fn is_empty(&self) -> bool {
140        self.activities.is_empty()
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_activity_registry() {
150        let mut registry = ActivityRegistry::new();
151
152        let id1 = registry.get_or_create("Register");
153        let id2 = registry.get_or_create("Process");
154        let id3 = registry.get_or_create("Register"); // Should return same ID
155
156        assert_eq!(id1, id3);
157        assert_ne!(id1, id2);
158        assert_eq!(registry.len(), 2);
159    }
160}