1use crate::types::{AgentId, RequestId, SessionId, Timestamp};
2use std::collections::HashMap;
3use std::sync::Arc;
4use std::time::Duration;
5use tokio_util::sync::CancellationToken;
6
7pub type TraceId = String;
8
9#[derive(Clone)]
10pub struct Context {
11 pub request_id: RequestId,
12 pub trace_id: Option<TraceId>,
13 pub parent_span_id: Option<String>,
14 pub agent_id: Option<AgentId>,
15 pub session_id: Option<SessionId>,
16 pub started_at: Timestamp,
17 pub deadline: Option<Timestamp>,
18 cancellation: CancellationToken,
19 extensions: Arc<HashMap<String, String>>,
20}
21
22impl Context {
23 pub fn new() -> Self {
24 Self {
25 request_id: RequestId::new(),
26 trace_id: None,
27 parent_span_id: None,
28 agent_id: None,
29 session_id: None,
30 started_at: Timestamp::now(),
31 deadline: None,
32 cancellation: CancellationToken::new(),
33 extensions: Arc::new(HashMap::new()),
34 }
35 }
36
37 pub fn with_timeout(mut self, timeout: Duration) -> Self {
38 let deadline_ms = self.started_at.unix_millis() + timeout.as_millis() as i64;
39 self.deadline = Timestamp::from_unix_millis(deadline_ms);
40 self
41 }
42
43 pub fn with_deadline(mut self, deadline: Timestamp) -> Self {
44 self.deadline = Some(deadline);
45 self
46 }
47
48 pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
49 self.trace_id = Some(trace_id.into());
50 self
51 }
52
53 pub fn with_agent(mut self, agent_id: AgentId) -> Self {
54 self.agent_id = Some(agent_id);
55 self
56 }
57
58 pub fn with_session(mut self, session_id: SessionId) -> Self {
59 self.session_id = Some(session_id);
60 self
61 }
62
63 pub fn with_cancellation(mut self, token: CancellationToken) -> Self {
64 self.cancellation = token;
65 self
66 }
67
68 pub fn child(&self) -> Self {
69 Self {
70 request_id: RequestId::new(),
71 trace_id: self.trace_id.clone(),
72 parent_span_id: Some(self.request_id.to_string()),
73 agent_id: self.agent_id,
74 session_id: self.session_id,
75 started_at: Timestamp::now(),
76 deadline: self.deadline,
77 cancellation: self.cancellation.child_token(),
78 extensions: Arc::clone(&self.extensions),
79 }
80 }
81
82 pub fn is_cancelled(&self) -> bool {
83 self.cancellation.is_cancelled()
84 }
85
86 pub fn cancel(&self) {
87 self.cancellation.cancel()
88 }
89
90 pub fn cancellation_token(&self) -> CancellationToken {
91 self.cancellation.clone()
92 }
93
94 pub fn is_timeout(&self) -> bool {
95 if let Some(deadline) = self.deadline {
96 Timestamp::now() > deadline
97 } else {
98 false
99 }
100 }
101
102 pub fn is_done(&self) -> bool {
103 self.is_cancelled() || self.is_timeout()
104 }
105
106 pub fn remaining(&self) -> Option<Duration> {
107 self.deadline.map(|d| {
108 let now = Timestamp::now().unix_millis();
109 let deadline = d.unix_millis();
110 if deadline > now {
111 Duration::from_millis((deadline - now) as u64)
112 } else {
113 Duration::ZERO
114 }
115 })
116 }
117
118 pub fn elapsed(&self) -> Duration {
119 self.started_at.elapsed()
120 }
121
122 pub fn get_extension(&self, key: &str) -> Option<&String> {
123 self.extensions.get(key)
124 }
125}
126
127impl Default for Context {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl std::fmt::Debug for Context {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 f.debug_struct("Context")
136 .field("request_id", &self.request_id)
137 .field("trace_id", &self.trace_id)
138 .field("agent_id", &self.agent_id)
139 .field("session_id", &self.session_id)
140 .field("started_at", &self.started_at)
141 .field("deadline", &self.deadline)
142 .field("is_cancelled", &self.is_cancelled())
143 .finish()
144 }
145}