1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum SisterType {
12 Memory,
14 Vision,
15 Codebase,
16 Identity,
17 Time,
18 Contract,
19
20 Comm,
22 Planning,
23 Cognition,
24 Reality,
25
26 Attention,
28 Affect,
29 Motivation,
30 Learning,
31 Bond,
32 Meaning,
33 Wonder,
34 Imagination,
35 Conscience,
36 Meta,
37 Duration,
38}
39
40impl SisterType {
41 pub fn file_extension(&self) -> &'static str {
43 match self {
44 Self::Memory => "amem",
45 Self::Vision => "avis",
46 Self::Codebase => "acb",
47 Self::Identity => "aid",
48 Self::Time => "atime",
49 Self::Contract => "acon",
50 Self::Comm => "acomm",
51 Self::Planning => "aplan",
52 Self::Cognition => "acog",
53 Self::Reality => "areal",
54 Self::Attention => "aatt",
55 Self::Affect => "aaff",
56 Self::Motivation => "amot",
57 Self::Learning => "alrn",
58 Self::Bond => "abond",
59 Self::Meaning => "amean",
60 Self::Wonder => "awond",
61 Self::Imagination => "aimag",
62 Self::Conscience => "acons",
63 Self::Meta => "ameta",
64 Self::Duration => "adur",
65 }
66 }
67
68 pub fn mcp_prefix(&self) -> &'static str {
70 match self {
71 Self::Memory => "memory",
72 Self::Vision => "vision",
73 Self::Codebase => "codebase",
74 Self::Identity => "identity",
75 Self::Time => "time",
76 Self::Contract => "contract",
77 Self::Comm => "comm",
78 Self::Planning => "planning",
79 Self::Cognition => "cognition",
80 Self::Reality => "reality",
81 Self::Attention => "attention",
82 Self::Affect => "affect",
83 Self::Motivation => "motivation",
84 Self::Learning => "learning",
85 Self::Bond => "bond",
86 Self::Meaning => "meaning",
87 Self::Wonder => "wonder",
88 Self::Imagination => "imagination",
89 Self::Conscience => "conscience",
90 Self::Meta => "meta",
91 Self::Duration => "duration",
92 }
93 }
94
95 pub fn to_byte(&self) -> u8 {
97 match self {
98 Self::Memory => 0x01,
99 Self::Vision => 0x02,
100 Self::Codebase => 0x03,
101 Self::Identity => 0x04,
102 Self::Time => 0x05,
103 Self::Contract => 0x06,
104 Self::Comm => 0x07,
105 Self::Planning => 0x08,
106 Self::Cognition => 0x09,
107 Self::Reality => 0x0A,
108 Self::Attention => 0x0B,
109 Self::Affect => 0x0C,
110 Self::Motivation => 0x0D,
111 Self::Learning => 0x0E,
112 Self::Bond => 0x0F,
113 Self::Meaning => 0x10,
114 Self::Wonder => 0x11,
115 Self::Imagination => 0x12,
116 Self::Conscience => 0x13,
117 Self::Meta => 0x14,
118 Self::Duration => 0x15,
119 }
120 }
121
122 pub fn from_byte(byte: u8) -> Option<Self> {
124 match byte {
125 0x01 => Some(Self::Memory),
126 0x02 => Some(Self::Vision),
127 0x03 => Some(Self::Codebase),
128 0x04 => Some(Self::Identity),
129 0x05 => Some(Self::Time),
130 0x06 => Some(Self::Contract),
131 0x07 => Some(Self::Comm),
132 0x08 => Some(Self::Planning),
133 0x09 => Some(Self::Cognition),
134 0x0A => Some(Self::Reality),
135 0x0B => Some(Self::Attention),
136 0x0C => Some(Self::Affect),
137 0x0D => Some(Self::Motivation),
138 0x0E => Some(Self::Learning),
139 0x0F => Some(Self::Bond),
140 0x10 => Some(Self::Meaning),
141 0x11 => Some(Self::Wonder),
142 0x12 => Some(Self::Imagination),
143 0x13 => Some(Self::Conscience),
144 0x14 => Some(Self::Meta),
145 0x15 => Some(Self::Duration),
146 _ => None,
147 }
148 }
149}
150
151impl std::fmt::Display for SisterType {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 write!(f, "{}", self.mcp_prefix())
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
159pub struct Version {
160 pub major: u8,
161 pub minor: u8,
162 pub patch: u8,
163}
164
165impl Version {
166 pub fn new(major: u8, minor: u8, patch: u8) -> Self {
167 Self {
168 major,
169 minor,
170 patch,
171 }
172 }
173
174 pub fn is_compatible_with(&self, other: &Version) -> bool {
177 self.major == other.major
178 }
179
180 pub fn can_read(&self, file_version: &Version) -> bool {
183 self.major >= file_version.major
184 }
185}
186
187impl std::fmt::Display for Version {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
190 }
191}
192
193impl From<(u8, u8, u8)> for Version {
194 fn from((major, minor, patch): (u8, u8, u8)) -> Self {
195 Self {
196 major,
197 minor,
198 patch,
199 }
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
205#[serde(rename_all = "snake_case")]
206pub enum Status {
207 Starting,
208 Ready,
209 Busy,
210 Degraded,
211 ShuttingDown,
212 Error,
213}
214
215impl std::fmt::Display for Status {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 match self {
218 Self::Starting => write!(f, "starting"),
219 Self::Ready => write!(f, "ready"),
220 Self::Busy => write!(f, "busy"),
221 Self::Degraded => write!(f, "degraded"),
222 Self::ShuttingDown => write!(f, "shutting_down"),
223 Self::Error => write!(f, "error"),
224 }
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
230pub struct Capability {
231 pub name: String,
232 pub description: String,
233}
234
235impl Capability {
236 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
237 Self {
238 name: name.into(),
239 description: description.into(),
240 }
241 }
242}
243
244#[derive(Debug, Clone, Default, Serialize, Deserialize)]
246pub struct ResourceUsage {
247 pub memory_bytes: usize,
248 pub disk_bytes: usize,
249 pub open_handles: usize,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct HealthStatus {
255 pub healthy: bool,
257
258 pub status: Status,
260
261 #[serde(with = "duration_serde")]
263 pub uptime: std::time::Duration,
264
265 pub resources: ResourceUsage,
267
268 pub warnings: Vec<String>,
270
271 pub last_error: Option<String>,
273}
274
275impl Default for HealthStatus {
276 fn default() -> Self {
277 Self {
278 healthy: true,
279 status: Status::Ready,
280 uptime: std::time::Duration::ZERO,
281 resources: ResourceUsage::default(),
282 warnings: vec![],
283 last_error: None,
284 }
285 }
286}
287
288pub type Metadata = HashMap<String, serde_json::Value>;
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
293pub struct UniqueId(pub Uuid);
294
295impl UniqueId {
296 pub fn new() -> Self {
297 Self(Uuid::new_v4())
298 }
299
300 pub fn from_uuid(uuid: Uuid) -> Self {
301 Self(uuid)
302 }
303
304 pub fn nil() -> Self {
305 Self(Uuid::nil())
306 }
307}
308
309impl Default for UniqueId {
310 fn default() -> Self {
311 Self::new()
312 }
313}
314
315impl std::fmt::Display for UniqueId {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 write!(f, "{}", self.0)
318 }
319}
320
321impl From<Uuid> for UniqueId {
322 fn from(uuid: Uuid) -> Self {
323 Self(uuid)
324 }
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
329pub struct Timestamp(pub DateTime<Utc>);
330
331impl Timestamp {
332 pub fn now() -> Self {
333 Self(Utc::now())
334 }
335
336 pub fn from_datetime(dt: DateTime<Utc>) -> Self {
337 Self(dt)
338 }
339}
340
341impl Default for Timestamp {
342 fn default() -> Self {
343 Self::now()
344 }
345}
346
347impl std::fmt::Display for Timestamp {
348 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349 write!(f, "{}", self.0.to_rfc3339())
350 }
351}
352
353mod duration_serde {
355 use serde::{Deserialize, Deserializer, Serialize, Serializer};
356 use std::time::Duration;
357
358 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
359 where
360 S: Serializer,
361 {
362 duration.as_secs_f64().serialize(serializer)
363 }
364
365 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
366 where
367 D: Deserializer<'de>,
368 {
369 let secs = f64::deserialize(deserializer)?;
370 Ok(Duration::from_secs_f64(secs))
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377
378 #[test]
379 fn test_sister_type_byte_roundtrip() {
380 for sister in [
381 SisterType::Memory,
382 SisterType::Vision,
383 SisterType::Codebase,
384 SisterType::Identity,
385 ] {
386 let byte = sister.to_byte();
387 let recovered = SisterType::from_byte(byte).unwrap();
388 assert_eq!(sister, recovered);
389 }
390 }
391
392 #[test]
393 fn test_version_compatibility() {
394 let v1 = Version::new(1, 0, 0);
395 let v1_1 = Version::new(1, 1, 0);
396 let v2 = Version::new(2, 0, 0);
397
398 assert!(v1.is_compatible_with(&v1_1));
399 assert!(!v1.is_compatible_with(&v2));
400 assert!(v2.can_read(&v1));
401 assert!(!v1.can_read(&v2));
402 }
403}