1use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9use super::RunType;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RunTree {
17 pub id: Uuid,
19
20 pub name: String,
22
23 pub run_type: RunType,
25
26 pub inputs: serde_json::Value,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub outputs: Option<serde_json::Value>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub error: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub parent_run_id: Option<Uuid>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub trace_id: Option<Uuid>,
44
45 pub start_time: DateTime<Utc>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub end_time: Option<DateTime<Utc>>,
51
52 #[serde(default)]
54 pub metadata: HashMap<String, serde_json::Value>,
55
56 #[serde(default)]
58 pub tags: Vec<String>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub project_name: Option<String>,
63
64 #[serde(default)]
66 pub serialized: serde_json::Value,
67}
68
69impl RunTree {
70 pub fn new(name: impl Into<String>, run_type: RunType, inputs: serde_json::Value) -> Self {
72 Self {
73 id: Uuid::now_v7(),
74 name: name.into(),
75 run_type,
76 inputs,
77 outputs: None,
78 error: None,
79 parent_run_id: None,
80 trace_id: None,
81 start_time: Utc::now(),
82 end_time: None,
83 metadata: HashMap::new(),
84 tags: Vec::new(),
85 project_name: None,
86 serialized: serde_json::Value::Null,
87 }
88 }
89
90 pub fn create_child(
92 &self,
93 name: impl Into<String>,
94 run_type: RunType,
95 inputs: serde_json::Value,
96 ) -> Self {
97 let mut child = Self::new(name, run_type, inputs);
98 child.parent_run_id = Some(self.id);
99 child.trace_id = self.trace_id.or(Some(self.id));
100 child.project_name = self.project_name.clone();
101 child
102 }
103
104 pub fn end(&mut self, outputs: serde_json::Value) {
106 self.outputs = Some(outputs);
107 self.end_time = Some(Utc::now());
108 }
109
110 pub fn end_with_error(&mut self, error: impl Into<String>) {
112 self.error = Some(error.into());
113 self.end_time = Some(Utc::now());
114 }
115
116 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
118 self.tags.push(tag.into());
119 self
120 }
121
122 pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
124 self.metadata.insert(key.into(), value);
125 self
126 }
127
128 pub fn with_project(mut self, project: impl Into<String>) -> Self {
130 self.project_name = Some(project.into());
131 self
132 }
133
134 pub fn duration_ms(&self) -> Option<i64> {
136 self.end_time
137 .map(|end| (end - self.start_time).num_milliseconds())
138 }
139}
140
141#[derive(Debug, Serialize)]
143pub struct RunCreate {
144 pub id: String,
145 pub name: String,
146 pub run_type: String,
147 pub inputs: serde_json::Value,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub outputs: Option<serde_json::Value>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub error: Option<String>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub parent_run_id: Option<String>,
154 pub start_time: String,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub end_time: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub session_name: Option<String>,
159 #[serde(default, skip_serializing_if = "Vec::is_empty")]
160 pub tags: Vec<String>,
161 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
162 pub metadata: HashMap<String, serde_json::Value>,
163}
164
165impl From<&RunTree> for RunCreate {
166 fn from(run: &RunTree) -> Self {
167 Self {
168 id: run.id.to_string(),
169 name: run.name.clone(),
170 run_type: run.run_type.as_str().to_string(),
171 inputs: run.inputs.clone(),
172 outputs: run.outputs.clone(),
173 error: run.error.clone(),
174 parent_run_id: run.parent_run_id.map(|id| id.to_string()),
175 start_time: run.start_time.to_rfc3339(),
176 end_time: run.end_time.map(|t| t.to_rfc3339()),
177 session_name: run.project_name.clone(),
178 tags: run.tags.clone(),
179 metadata: run.metadata.clone(),
180 }
181 }
182}
183
184#[derive(Debug, Serialize)]
186pub struct RunUpdate {
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub outputs: Option<serde_json::Value>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub error: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub end_time: Option<String>,
193}
194
195impl From<&RunTree> for RunUpdate {
196 fn from(run: &RunTree) -> Self {
197 Self {
198 outputs: run.outputs.clone(),
199 error: run.error.clone(),
200 end_time: run.end_time.map(|t| t.to_rfc3339()),
201 }
202 }
203}