1use crate::digest::Digest;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12
13#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Platform {
18 pub properties: BTreeMap<String, String>,
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
26pub struct Command {
27 pub arguments: Vec<String>,
29 pub environment_variables: BTreeMap<String, String>,
31 pub output_files: Vec<String>,
33 pub output_directories: Vec<String>,
35 pub working_directory: String,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct FileNode {
42 pub name: String,
44 pub digest: Digest,
46 pub is_executable: bool,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct DirectoryNode {
53 pub name: String,
55 pub digest: Digest,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct SymlinkNode {
62 pub name: String,
64 pub target: String,
66}
67
68#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
73pub struct Directory {
74 pub files: Vec<FileNode>,
76 pub directories: Vec<DirectoryNode>,
78 pub symlinks: Vec<SymlinkNode>,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct Action {
88 pub command_digest: Digest,
90 pub input_root_digest: Digest,
92 pub platform: Platform,
94 pub cuenv_version: String,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct OutputFile {
104 pub path: String,
106 pub digest: Digest,
108 pub is_executable: bool,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct OutputDirectory {
118 pub path: String,
120 pub tree_digest: Digest,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct ExecutionMetadata {
127 pub worker: String,
129 pub duration_ms: u128,
131 pub created_at: DateTime<Utc>,
133}
134
135impl Default for ExecutionMetadata {
136 fn default() -> Self {
137 Self {
138 worker: String::new(),
139 duration_ms: 0,
140 created_at: DateTime::<Utc>::from_timestamp(0, 0).unwrap_or_else(Utc::now),
141 }
142 }
143}
144
145#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
149pub struct ActionResult {
150 pub output_files: Vec<OutputFile>,
152 pub output_directories: Vec<OutputDirectory>,
154 pub exit_code: i32,
156 pub stdout_digest: Option<Digest>,
158 pub stderr_digest: Option<Digest>,
160 pub execution_metadata: ExecutionMetadata,
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::digest::digest_of;
168
169 #[test]
170 fn action_digest_is_order_invariant_on_platform_properties() {
171 let mut a_props = BTreeMap::new();
172 a_props.insert("os".into(), "linux".into());
173 a_props.insert("arch".into(), "x86_64".into());
174 let a = Action {
175 command_digest: Digest::of_bytes(b"cmd"),
176 input_root_digest: Digest::of_bytes(b"root"),
177 platform: Platform {
178 properties: a_props,
179 },
180 cuenv_version: "0.30.8".into(),
181 };
182
183 let mut b_props = BTreeMap::new();
184 b_props.insert("arch".into(), "x86_64".into());
186 b_props.insert("os".into(), "linux".into());
187 let b = Action {
188 platform: Platform {
189 properties: b_props,
190 },
191 ..a.clone()
192 };
193
194 assert_eq!(digest_of(&a).unwrap(), digest_of(&b).unwrap());
195 }
196
197 #[test]
198 fn action_digest_changes_with_command_digest() {
199 let base = Action {
200 command_digest: Digest::of_bytes(b"cmd-1"),
201 input_root_digest: Digest::of_bytes(b"root"),
202 platform: Platform::default(),
203 cuenv_version: "0.30.8".into(),
204 };
205 let other = Action {
206 command_digest: Digest::of_bytes(b"cmd-2"),
207 ..base.clone()
208 };
209 assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
210 }
211
212 #[test]
213 fn action_digest_changes_with_input_root() {
214 let base = Action {
215 command_digest: Digest::of_bytes(b"cmd"),
216 input_root_digest: Digest::of_bytes(b"root-1"),
217 platform: Platform::default(),
218 cuenv_version: "0.30.8".into(),
219 };
220 let other = Action {
221 input_root_digest: Digest::of_bytes(b"root-2"),
222 ..base.clone()
223 };
224 assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
225 }
226
227 #[test]
228 fn action_digest_changes_with_cuenv_version() {
229 let base = Action {
230 command_digest: Digest::of_bytes(b"cmd"),
231 input_root_digest: Digest::of_bytes(b"root"),
232 platform: Platform::default(),
233 cuenv_version: "0.30.8".into(),
234 };
235 let other = Action {
236 cuenv_version: "0.31.0".into(),
237 ..base.clone()
238 };
239 assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
240 }
241}