greentic_session/
model.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashSet;
3use time::{Duration, OffsetDateTime};
4use uuid::Uuid;
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct SessionId(pub Uuid);
9
10impl SessionId {
11 pub fn new() -> Self {
13 Self(Uuid::new_v4())
14 }
15
16 pub fn as_uuid(&self) -> &Uuid {
18 &self.0
19 }
20}
21
22impl Default for SessionId {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct SessionKey(pub String);
31
32impl SessionKey {
33 pub fn as_str(&self) -> &str {
35 &self.0
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
41pub struct SessionCursor {
42 pub flow_id: String,
43 pub node_id: String,
44 pub wait_reason: Option<String>,
45 pub outbox_seq: u64,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50pub struct OutboxEntry {
51 pub seq: u64,
52 pub payload_sha256: [u8; 32],
53 pub created_at: OffsetDateTime,
54}
55
56#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
57pub struct SessionMeta {
58 pub tenant_id: String,
59 pub team_id: Option<String>,
60 pub user_id: Option<String>,
61 pub labels: serde_json::Map<String, serde_json::Value>,
62}
63
64#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct Session {
66 pub id: SessionId,
67 pub key: SessionKey,
68 pub cursor: SessionCursor,
69 pub meta: SessionMeta,
70 pub outbox: Vec<OutboxEntry>,
71 pub updated_at: OffsetDateTime,
72 pub ttl_secs: u32,
73}
74
75impl Session {
76 pub fn tenant_id(&self) -> &str {
78 &self.meta.tenant_id
79 }
80
81 pub fn normalize(&mut self) {
83 self.dedupe_outbox();
84 if self.ttl_secs == 0 {
85 self.ttl_secs = 0;
87 }
88 }
89
90 pub fn dedupe_outbox(&mut self) {
92 let mut seen = HashSet::new();
93 self.outbox
94 .retain(|entry| seen.insert((entry.seq, entry.payload_sha256)));
95 }
96
97 pub fn expires_at(&self) -> Option<OffsetDateTime> {
99 if self.ttl_secs == 0 {
100 return None;
101 }
102 let ttl = Duration::seconds(self.ttl_secs as i64);
103 Some(self.updated_at + ttl)
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
109pub struct Cas(pub u64);
110
111impl Cas {
112 pub const fn initial() -> Self {
114 Self(1)
115 }
116
117 pub const fn none() -> Self {
119 Self(0)
120 }
121
122 pub const fn value(self) -> u64 {
124 self.0
125 }
126
127 pub const fn next(self) -> Self {
129 Self(self.0.wrapping_add(1))
130 }
131}
132
133impl From<u64> for Cas {
134 fn from(value: u64) -> Self {
135 Self(value)
136 }
137}
138
139impl From<Cas> for u64 {
140 fn from(cas: Cas) -> Self {
141 cas.0
142 }
143}