1use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use uuid::Uuid;
9
10pub fn is_sha256(hash: &str) -> bool {
13 hash.len() == 64
14 && hash
15 .bytes()
16 .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
17}
18
19pub const RUNNER_NATIVE_PATH: &str = "/api/runner/native";
22
23pub const RUNNER_WEB_PATH: &str = "/api/runner/web";
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct NativeSet {
31 pub tenant: String,
32 pub services: Vec<NativeService>,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub struct NativeService {
38 pub service_id: Uuid,
39 pub service_name: String,
40 pub version: i32,
41 pub sha256: Option<String>,
44 pub size: Option<u64>,
46 pub extensions: Vec<String>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct WebSet {
54 pub tenant: String,
55 pub apps: Vec<WebApp>,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct WebApp {
61 pub service_id: Uuid,
62 pub service_name: String,
63 pub version: i32,
64 pub sha256: Option<String>,
67 pub size: Option<u64>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub struct Environment {
75 pub environment: BTreeMap<String, String>,
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::host::tests::round_trip;
82 use serde_json::json;
83
84 #[test]
85 fn an_artifact_is_addressed_by_its_lowercase_sha256() {
86 let hash = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08";
87 assert!(is_sha256(hash));
88 for other in [
89 &hash[1..],
90 &format!("{hash}0"),
91 &hash.to_uppercase(),
92 &format!("../{}", &hash[3..]),
93 "environment",
94 ] {
95 assert!(!is_sha256(other), "{other}");
96 }
97 }
98
99 #[test]
100 fn what_a_runner_is_answered_round_trips() {
101 round_trip(
102 &NativeSet {
103 tenant: "acme".into(),
104 services: vec![NativeService {
105 service_id: Uuid::nil(),
106 service_name: "ext-pdf".into(),
107 version: 4,
108 sha256: Some("ab".into()),
109 size: Some(9),
110 extensions: vec!["pdf.render".into()],
111 }],
112 },
113 json!({
114 "tenant": "acme",
115 "services": [{
116 "service_id": "00000000-0000-0000-0000-000000000000",
117 "service_name": "ext-pdf",
118 "version": 4,
119 "sha256": "ab",
120 "size": 9,
121 "extensions": ["pdf.render"],
122 }],
123 }),
124 );
125 round_trip(
126 &WebSet {
127 tenant: "acme".into(),
128 apps: vec![WebApp {
129 service_id: Uuid::nil(),
130 service_name: "portal".into(),
131 version: 3,
132 sha256: None,
133 size: None,
134 }],
135 },
136 json!({
137 "tenant": "acme",
138 "apps": [{
139 "service_id": "00000000-0000-0000-0000-000000000000",
140 "service_name": "portal",
141 "version": 3,
142 "sha256": null,
143 "size": null,
144 }],
145 }),
146 );
147 round_trip(
148 &Environment {
149 environment: [("GOOGLE_KEY".to_string(), "k".to_string())].into(),
150 },
151 json!({"environment": {"GOOGLE_KEY": "k"}}),
152 );
153 }
154}