mi6_core/model/event/
builder.rs

1//! Fluent builder for creating Event instances.
2
3use super::types::Event;
4use crate::model::event_type::EventType;
5use chrono::{DateTime, Utc};
6
7/// Builder for creating Event instances with an ergonomic API.
8///
9/// Provides helper methods that group related fields together and accept
10/// non-Option values directly, reducing verbosity compared to chained `with_*` calls.
11///
12/// # Method variants
13///
14/// - **Regular setters** (e.g., `framework()`, `model()`) accept values directly and always
15///   set the field.
16/// - **Optional setters** (e.g., `framework_opt()`, `model_opt()`) accept `Option<T>` and
17///   only set the field if `Some` is passed; `None` preserves any previously set value.
18/// - **Grouped helpers** (e.g., `tokens()`, `cache_tokens()`, `tool()`) set multiple related
19///   fields at once for common patterns.
20///
21/// # Example
22///
23/// ```
24/// use mi6_core::{EventBuilder, EventType};
25///
26/// // Building an API request event
27/// let event = EventBuilder::new("machine-1", EventType::ApiRequest, "session-123")
28///     .framework("claude")
29///     .tokens(1000, 500)
30///     .cache_tokens(200, 0)
31///     .cost(0.05)
32///     .model("claude-3-opus")
33///     .build();
34///
35/// // Building a hook event
36/// let event = EventBuilder::new("machine-1", EventType::PreToolUse, "session-123")
37///     .framework("claude")
38///     .tool("toolu_123", "Bash")
39///     .payload(r#"{"command": "ls"}"#)
40///     .build();
41///
42/// // Using _opt methods to conditionally set fields
43/// let maybe_model: Option<String> = Some("claude-3-opus".to_string());
44/// let event = EventBuilder::new("machine-1", EventType::ApiRequest, "session-123")
45///     .model_opt(maybe_model)  // Sets model to "claude-3-opus"
46///     .model_opt(None)         // Does nothing, preserves "claude-3-opus"
47///     .build();
48/// ```
49#[derive(Debug, Clone)]
50pub struct EventBuilder {
51    event: Event,
52}
53
54impl EventBuilder {
55    /// Create a new EventBuilder with required fields.
56    ///
57    /// The timestamp is set to the current time; use `timestamp()` to override.
58    pub fn new(
59        machine_id: impl Into<String>,
60        event_type: EventType,
61        session_id: impl Into<String>,
62    ) -> Self {
63        Self {
64            event: Event::new(machine_id.into(), event_type, session_id.into()),
65        }
66    }
67
68    /// Build the final Event.
69    pub fn build(self) -> Event {
70        self.event
71    }
72
73    // --- Grouped helper methods ---
74
75    /// Set input and output tokens together.
76    pub fn tokens(mut self, input: i64, output: i64) -> Self {
77        self.event.tokens_input = Some(input);
78        self.event.tokens_output = Some(output);
79        self
80    }
81
82    /// Set cache read and cache write tokens together.
83    pub fn cache_tokens(mut self, read: i64, write: i64) -> Self {
84        self.event.tokens_cache_read = Some(read);
85        self.event.tokens_cache_write = Some(write);
86        self
87    }
88
89    /// Set tool_use_id and tool_name together (common for tool events).
90    pub fn tool(mut self, tool_use_id: impl Into<String>, tool_name: impl Into<String>) -> Self {
91        self.event.tool_use_id = Some(tool_use_id.into());
92        self.event.tool_name = Some(tool_name.into());
93        self
94    }
95
96    // --- Individual field setters (non-Option convenience) ---
97
98    /// Set the framework.
99    pub fn framework(mut self, framework: impl Into<String>) -> Self {
100        self.event.framework = Some(framework.into());
101        self
102    }
103
104    /// Set the model.
105    pub fn model(mut self, model: impl Into<String>) -> Self {
106        self.event.model = Some(model.into());
107        self
108    }
109
110    /// Set the cost in USD.
111    pub fn cost(mut self, cost_usd: f64) -> Self {
112        self.event.cost_usd = Some(cost_usd);
113        self
114    }
115
116    /// Set the duration in milliseconds.
117    pub fn duration_ms(mut self, duration: i64) -> Self {
118        self.event.duration_ms = Some(duration);
119        self
120    }
121
122    /// Set the timestamp.
123    pub fn timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
124        self.event.timestamp = timestamp;
125        self
126    }
127
128    /// Set the payload JSON string.
129    pub fn payload(mut self, payload: impl Into<String>) -> Self {
130        self.event.payload = Some(payload.into());
131        self
132    }
133
134    /// Set the metadata JSON string.
135    pub fn metadata(mut self, metadata: impl Into<String>) -> Self {
136        self.event.metadata = Some(metadata.into());
137        self
138    }
139
140    /// Set the tool_use_id.
141    pub fn tool_use_id(mut self, tool_use_id: impl Into<String>) -> Self {
142        self.event.tool_use_id = Some(tool_use_id.into());
143        self
144    }
145
146    /// Set the tool_name.
147    pub fn tool_name(mut self, tool_name: impl Into<String>) -> Self {
148        self.event.tool_name = Some(tool_name.into());
149        self
150    }
151
152    /// Set the spawned_agent_id.
153    pub fn spawned_agent_id(mut self, spawned_agent_id: impl Into<String>) -> Self {
154        self.event.spawned_agent_id = Some(spawned_agent_id.into());
155        self
156    }
157
158    /// Set the subagent_type.
159    pub fn subagent_type(mut self, subagent_type: impl Into<String>) -> Self {
160        self.event.subagent_type = Some(subagent_type.into());
161        self
162    }
163
164    /// Set the permission_mode.
165    pub fn permission_mode(mut self, permission_mode: impl Into<String>) -> Self {
166        self.event.permission_mode = Some(permission_mode.into());
167        self
168    }
169
170    /// Set the transcript_path.
171    pub fn transcript_path(mut self, transcript_path: impl Into<String>) -> Self {
172        self.event.transcript_path = Some(transcript_path.into());
173        self
174    }
175
176    /// Set the cwd.
177    pub fn cwd(mut self, cwd: impl Into<String>) -> Self {
178        self.event.cwd = Some(cwd.into());
179        self
180    }
181
182    /// Set the git_branch.
183    pub fn git_branch(mut self, git_branch: impl Into<String>) -> Self {
184        self.event.git_branch = Some(git_branch.into());
185        self
186    }
187
188    /// Set the pid.
189    pub fn pid(mut self, pid: i32) -> Self {
190        self.event.pid = Some(pid);
191        self
192    }
193
194    // --- Optional field setters (for passing through Option values) ---
195
196    /// Set the framework if Some.
197    pub fn framework_opt(mut self, framework: Option<String>) -> Self {
198        if let Some(f) = framework {
199            self.event.framework = Some(f);
200        }
201        self
202    }
203
204    /// Set the timestamp if Some.
205    pub fn timestamp_opt(mut self, timestamp: Option<DateTime<Utc>>) -> Self {
206        if let Some(ts) = timestamp {
207            self.event.timestamp = ts;
208        }
209        self
210    }
211
212    /// Set the tool_use_id if Some.
213    pub fn tool_use_id_opt(mut self, tool_use_id: Option<String>) -> Self {
214        if let Some(id) = tool_use_id {
215            self.event.tool_use_id = Some(id);
216        }
217        self
218    }
219
220    /// Set the tool_name if Some.
221    pub fn tool_name_opt(mut self, tool_name: Option<String>) -> Self {
222        if let Some(name) = tool_name {
223            self.event.tool_name = Some(name);
224        }
225        self
226    }
227
228    /// Set the spawned_agent_id if Some.
229    pub fn spawned_agent_id_opt(mut self, spawned_agent_id: Option<String>) -> Self {
230        if let Some(id) = spawned_agent_id {
231            self.event.spawned_agent_id = Some(id);
232        }
233        self
234    }
235
236    /// Set the subagent_type if Some.
237    pub fn subagent_type_opt(mut self, subagent_type: Option<String>) -> Self {
238        if let Some(t) = subagent_type {
239            self.event.subagent_type = Some(t);
240        }
241        self
242    }
243
244    /// Set the permission_mode if Some.
245    pub fn permission_mode_opt(mut self, permission_mode: Option<String>) -> Self {
246        if let Some(m) = permission_mode {
247            self.event.permission_mode = Some(m);
248        }
249        self
250    }
251
252    /// Set the transcript_path if Some.
253    pub fn transcript_path_opt(mut self, transcript_path: Option<String>) -> Self {
254        if let Some(p) = transcript_path {
255            self.event.transcript_path = Some(p);
256        }
257        self
258    }
259
260    /// Set the cwd if Some.
261    pub fn cwd_opt(mut self, cwd: Option<String>) -> Self {
262        if let Some(c) = cwd {
263            self.event.cwd = Some(c);
264        }
265        self
266    }
267
268    /// Set the git_branch if Some.
269    pub fn git_branch_opt(mut self, git_branch: Option<String>) -> Self {
270        if let Some(b) = git_branch {
271            self.event.git_branch = Some(b);
272        }
273        self
274    }
275
276    /// Set the pid if Some.
277    pub fn pid_opt(mut self, pid: Option<i32>) -> Self {
278        if let Some(p) = pid {
279            self.event.pid = Some(p);
280        }
281        self
282    }
283
284    /// Set the model if Some.
285    pub fn model_opt(mut self, model: Option<String>) -> Self {
286        if let Some(m) = model {
287            self.event.model = Some(m);
288        }
289        self
290    }
291
292    /// Set the tokens_cache_read if Some.
293    pub fn tokens_cache_read_opt(mut self, tokens: Option<i64>) -> Self {
294        if let Some(t) = tokens {
295            self.event.tokens_cache_read = Some(t);
296        }
297        self
298    }
299
300    /// Set the tokens_cache_write if Some.
301    pub fn tokens_cache_write_opt(mut self, tokens: Option<i64>) -> Self {
302        if let Some(t) = tokens {
303            self.event.tokens_cache_write = Some(t);
304        }
305        self
306    }
307
308    /// Set the cost_usd if Some.
309    pub fn cost_opt(mut self, cost: Option<f64>) -> Self {
310        if let Some(c) = cost {
311            self.event.cost_usd = Some(c);
312        }
313        self
314    }
315
316    /// Set the duration_ms if Some.
317    pub fn duration_ms_opt(mut self, duration: Option<i64>) -> Self {
318        if let Some(d) = duration {
319            self.event.duration_ms = Some(d);
320        }
321        self
322    }
323
324    /// Set the metadata if Some.
325    pub fn metadata_opt(mut self, metadata: Option<String>) -> Self {
326        if let Some(m) = metadata {
327            self.event.metadata = Some(m);
328        }
329        self
330    }
331
332    /// Set the payload if Some.
333    pub fn payload_opt(mut self, payload: Option<String>) -> Self {
334        if let Some(p) = payload {
335            self.event.payload = Some(p);
336        }
337        self
338    }
339
340    /// Set the data source (hook, otel, transcript).
341    pub fn source(mut self, source: impl Into<String>) -> Self {
342        self.event.source = Some(source.into());
343        self
344    }
345
346    /// Set whether this is a sidechain request.
347    pub fn is_sidechain(mut self, is_sidechain: bool) -> Self {
348        self.event.is_sidechain = Some(is_sidechain);
349        self
350    }
351
352    /// Set is_sidechain if Some.
353    pub fn is_sidechain_opt(mut self, is_sidechain: Option<bool>) -> Self {
354        if let Some(s) = is_sidechain {
355            self.event.is_sidechain = Some(s);
356        }
357        self
358    }
359}