agent_workspace_contract/
io.rs1use super::*;
2
3pub const WORKSPACE_VERSION_CONFLICT_CODE: &str = "VERSION_CONFLICT";
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct WorkspaceVersionConflict {
7 pub expected: String,
8 pub actual: Option<String>,
9}
10
11impl fmt::Display for WorkspaceVersionConflict {
12 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
13 write!(
14 formatter,
15 "workspace file revision conflict: expected {}, actual {}",
16 self.expected,
17 self.actual.as_deref().unwrap_or("<missing>")
18 )
19 }
20}
21
22impl std::error::Error for WorkspaceVersionConflict {}
23
24pub fn content_revision(bytes: &[u8]) -> String {
26 use sha2::{Digest as _, Sha256};
27 format!("sha256:{:x}", Sha256::digest(bytes))
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct VersionedFile {
32 pub bytes: Vec<u8>,
33 pub revision: String,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct DirEntry {
39 pub name: String,
40 pub is_dir: bool,
41 pub size: Option<u64>,
42}
43
44#[cfg(test)]
45mod revision_tests {
46 use super::*;
47
48 #[test]
49 fn revision_is_sha256_and_separates_known_fnv_collision_inputs() {
50 let left = content_revision(b"costarring");
53 let right = content_revision(b"liquid");
54 assert_ne!(left, right);
55 for revision in [left, right] {
56 let digest = revision.strip_prefix("sha256:").unwrap();
57 assert_eq!(digest.len(), 64);
58 assert!(digest.bytes().all(|byte| byte.is_ascii_hexdigit()));
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65pub struct CmdOutput {
66 pub exit_code: i32,
67 pub stdout: String,
68 pub stderr: String,
69 pub stdout_truncated: bool,
70 pub stderr_truncated: bool,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub struct PortUrl {
76 pub port: u16,
77 pub url: String,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub host: Option<String>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub token: Option<SecretRef>,
84 #[serde(default)]
85 pub signed: bool,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub expires_in_seconds: Option<u64>,
89}
90
91#[derive(Clone, PartialEq, Eq)]
96pub enum ProviderPortAuthorization {
97 AccessToken(String),
98 TrafficAccessToken(String),
99 Bearer(String),
100}
101
102impl std::fmt::Debug for ProviderPortAuthorization {
103 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 Self::AccessToken(_) => formatter.write_str("AccessToken([REDACTED])"),
106 Self::TrafficAccessToken(_) => formatter.write_str("TrafficAccessToken([REDACTED])"),
107 Self::Bearer(_) => formatter.write_str("Bearer([REDACTED])"),
108 }
109 }
110}
111
112#[derive(Clone, PartialEq, Eq)]
115pub struct ProviderPortTarget {
116 pub url: String,
117 pub authorization: Option<ProviderPortAuthorization>,
118}
119
120impl std::fmt::Debug for ProviderPortTarget {
121 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 formatter
123 .debug_struct("ProviderPortTarget")
124 .field("url", &self.url)
125 .field("authorization", &self.authorization)
126 .finish()
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub struct StaticPreviewServer {
132 pub id: String,
133 pub port: u16,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct GrepMatch {
139 pub file: String,
140 pub line_number: usize,
141 pub line: String,
142}