use crate::digest::Digest;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Platform {
pub properties: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Command {
pub arguments: Vec<String>,
pub environment_variables: BTreeMap<String, String>,
pub output_files: Vec<String>,
pub output_directories: Vec<String>,
pub working_directory: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileNode {
pub name: String,
pub digest: Digest,
pub is_executable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectoryNode {
pub name: String,
pub digest: Digest,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SymlinkNode {
pub name: String,
pub target: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Directory {
pub files: Vec<FileNode>,
pub directories: Vec<DirectoryNode>,
pub symlinks: Vec<SymlinkNode>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Action {
pub command_digest: Digest,
pub input_root_digest: Digest,
pub platform: Platform,
pub cuenv_version: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OutputFile {
pub path: String,
pub digest: Digest,
pub is_executable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OutputDirectory {
pub path: String,
pub tree_digest: Digest,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionMetadata {
pub worker: String,
pub duration_ms: u128,
pub created_at: DateTime<Utc>,
}
impl Default for ExecutionMetadata {
fn default() -> Self {
Self {
worker: String::new(),
duration_ms: 0,
created_at: DateTime::<Utc>::from_timestamp(0, 0).unwrap_or_else(Utc::now),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ActionResult {
pub output_files: Vec<OutputFile>,
pub output_directories: Vec<OutputDirectory>,
pub exit_code: i32,
pub stdout_digest: Option<Digest>,
pub stderr_digest: Option<Digest>,
pub execution_metadata: ExecutionMetadata,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::digest::digest_of;
#[test]
fn action_digest_is_order_invariant_on_platform_properties() {
let mut a_props = BTreeMap::new();
a_props.insert("os".into(), "linux".into());
a_props.insert("arch".into(), "x86_64".into());
let a = Action {
command_digest: Digest::of_bytes(b"cmd"),
input_root_digest: Digest::of_bytes(b"root"),
platform: Platform {
properties: a_props,
},
cuenv_version: "0.30.8".into(),
};
let mut b_props = BTreeMap::new();
b_props.insert("arch".into(), "x86_64".into());
b_props.insert("os".into(), "linux".into());
let b = Action {
platform: Platform {
properties: b_props,
},
..a.clone()
};
assert_eq!(digest_of(&a).unwrap(), digest_of(&b).unwrap());
}
#[test]
fn action_digest_changes_with_command_digest() {
let base = Action {
command_digest: Digest::of_bytes(b"cmd-1"),
input_root_digest: Digest::of_bytes(b"root"),
platform: Platform::default(),
cuenv_version: "0.30.8".into(),
};
let other = Action {
command_digest: Digest::of_bytes(b"cmd-2"),
..base.clone()
};
assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
}
#[test]
fn action_digest_changes_with_input_root() {
let base = Action {
command_digest: Digest::of_bytes(b"cmd"),
input_root_digest: Digest::of_bytes(b"root-1"),
platform: Platform::default(),
cuenv_version: "0.30.8".into(),
};
let other = Action {
input_root_digest: Digest::of_bytes(b"root-2"),
..base.clone()
};
assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
}
#[test]
fn action_digest_changes_with_cuenv_version() {
let base = Action {
command_digest: Digest::of_bytes(b"cmd"),
input_root_digest: Digest::of_bytes(b"root"),
platform: Platform::default(),
cuenv_version: "0.30.8".into(),
};
let other = Action {
cuenv_version: "0.31.0".into(),
..base.clone()
};
assert_ne!(digest_of(&base).unwrap(), digest_of(&other).unwrap());
}
}