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    /// Set the process_start_time.
195    pub fn process_start_time(mut self, start_time: i64) -> Self {
196        self.event.process_start_time = Some(start_time);
197        self
198    }
199
200    // --- Optional field setters (for passing through Option values) ---
201
202    /// Set the framework if Some.
203    pub fn framework_opt(mut self, framework: Option<String>) -> Self {
204        if let Some(f) = framework {
205            self.event.framework = Some(f);
206        }
207        self
208    }
209
210    /// Set the timestamp if Some.
211    pub fn timestamp_opt(mut self, timestamp: Option<DateTime<Utc>>) -> Self {
212        if let Some(ts) = timestamp {
213            self.event.timestamp = ts;
214        }
215        self
216    }
217
218    /// Set the tool_use_id if Some.
219    pub fn tool_use_id_opt(mut self, tool_use_id: Option<String>) -> Self {
220        if let Some(id) = tool_use_id {
221            self.event.tool_use_id = Some(id);
222        }
223        self
224    }
225
226    /// Set the tool_name if Some.
227    pub fn tool_name_opt(mut self, tool_name: Option<String>) -> Self {
228        if let Some(name) = tool_name {
229            self.event.tool_name = Some(name);
230        }
231        self
232    }
233
234    /// Set the spawned_agent_id if Some.
235    pub fn spawned_agent_id_opt(mut self, spawned_agent_id: Option<String>) -> Self {
236        if let Some(id) = spawned_agent_id {
237            self.event.spawned_agent_id = Some(id);
238        }
239        self
240    }
241
242    /// Set the subagent_type if Some.
243    pub fn subagent_type_opt(mut self, subagent_type: Option<String>) -> Self {
244        if let Some(t) = subagent_type {
245            self.event.subagent_type = Some(t);
246        }
247        self
248    }
249
250    /// Set the permission_mode if Some.
251    pub fn permission_mode_opt(mut self, permission_mode: Option<String>) -> Self {
252        if let Some(m) = permission_mode {
253            self.event.permission_mode = Some(m);
254        }
255        self
256    }
257
258    /// Set the transcript_path if Some.
259    pub fn transcript_path_opt(mut self, transcript_path: Option<String>) -> Self {
260        if let Some(p) = transcript_path {
261            self.event.transcript_path = Some(p);
262        }
263        self
264    }
265
266    /// Set the cwd if Some.
267    pub fn cwd_opt(mut self, cwd: Option<String>) -> Self {
268        if let Some(c) = cwd {
269            self.event.cwd = Some(c);
270        }
271        self
272    }
273
274    /// Set the git_branch if Some.
275    pub fn git_branch_opt(mut self, git_branch: Option<String>) -> Self {
276        if let Some(b) = git_branch {
277            self.event.git_branch = Some(b);
278        }
279        self
280    }
281
282    /// Set the pid if Some.
283    pub fn pid_opt(mut self, pid: Option<i32>) -> Self {
284        if let Some(p) = pid {
285            self.event.pid = Some(p);
286        }
287        self
288    }
289
290    /// Set the process_start_time if Some.
291    pub fn process_start_time_opt(mut self, start_time: Option<i64>) -> Self {
292        if let Some(t) = start_time {
293            self.event.process_start_time = Some(t);
294        }
295        self
296    }
297
298    /// Set the model if Some.
299    pub fn model_opt(mut self, model: Option<String>) -> Self {
300        if let Some(m) = model {
301            self.event.model = Some(m);
302        }
303        self
304    }
305
306    /// Set the tokens_cache_read if Some.
307    pub fn tokens_cache_read_opt(mut self, tokens: Option<i64>) -> Self {
308        if let Some(t) = tokens {
309            self.event.tokens_cache_read = Some(t);
310        }
311        self
312    }
313
314    /// Set the tokens_cache_write if Some.
315    pub fn tokens_cache_write_opt(mut self, tokens: Option<i64>) -> Self {
316        if let Some(t) = tokens {
317            self.event.tokens_cache_write = Some(t);
318        }
319        self
320    }
321
322    /// Set the cost_usd if Some.
323    pub fn cost_opt(mut self, cost: Option<f64>) -> Self {
324        if let Some(c) = cost {
325            self.event.cost_usd = Some(c);
326        }
327        self
328    }
329
330    /// Set the duration_ms if Some.
331    pub fn duration_ms_opt(mut self, duration: Option<i64>) -> Self {
332        if let Some(d) = duration {
333            self.event.duration_ms = Some(d);
334        }
335        self
336    }
337
338    /// Set the metadata if Some.
339    pub fn metadata_opt(mut self, metadata: Option<String>) -> Self {
340        if let Some(m) = metadata {
341            self.event.metadata = Some(m);
342        }
343        self
344    }
345
346    /// Set the payload if Some.
347    pub fn payload_opt(mut self, payload: Option<String>) -> Self {
348        if let Some(p) = payload {
349            self.event.payload = Some(p);
350        }
351        self
352    }
353
354    /// Set the data source (hook, otel, transcript).
355    pub fn source(mut self, source: impl Into<String>) -> Self {
356        self.event.source = Some(source.into());
357        self
358    }
359
360    /// Set whether this is a sidechain request.
361    pub fn is_sidechain(mut self, is_sidechain: bool) -> Self {
362        self.event.is_sidechain = Some(is_sidechain);
363        self
364    }
365
366    /// Set is_sidechain if Some.
367    pub fn is_sidechain_opt(mut self, is_sidechain: Option<bool>) -> Self {
368        if let Some(s) = is_sidechain {
369            self.event.is_sidechain = Some(s);
370        }
371        self
372    }
373
374    /// Set the context window size.
375    pub fn context_size(mut self, context_size: i64) -> Self {
376        self.event.context_size = Some(context_size);
377        self
378    }
379}