1use std::fmt;
37use std::path::Path;
38use thiserror::Error;
39
40pub const VERSION: &str = env!("CARGO_PKG_VERSION");
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[repr(i32)]
46pub enum ExitCode {
47 Success = 0,
49 GeneralFailure = 1,
51 UsageConfigError = 64,
53 DataError = 65,
55 ResourceUnavailable = 69,
57 InternalError = 70,
59 CantCreateOutput = 73,
61 IoError = 74,
63 CompletedUnreported = 75,
65 PartialFailure = 76,
67}
68
69impl From<ExitCode> for i32 {
70 fn from(code: ExitCode) -> i32 {
71 code as i32
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum AgentState {
78 Initializing,
80 Running,
82 Completing,
84 Terminated,
86 Failed,
88}
89
90impl fmt::Display for AgentState {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 AgentState::Initializing => write!(f, "initializing"),
94 AgentState::Running => write!(f, "running"),
95 AgentState::Completing => write!(f, "completing"),
96 AgentState::Terminated => write!(f, "terminated"),
97 AgentState::Failed => write!(f, "failed"),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
104#[repr(u8)]
105pub enum LogLevel {
106 Trace = 0,
107 Debug = 1,
108 Info = 2,
109 Warn = 3,
110 Error = 4,
111 Fatal = 5,
112}
113
114#[derive(Clone)]
128pub struct Secret {
129 value: String,
130}
131
132impl Secret {
133 pub fn new(value: impl Into<String>) -> Self {
135 Self {
136 value: value.into(),
137 }
138 }
139
140 pub fn reveal(&self) -> &str {
142 &self.value
143 }
144}
145
146impl fmt::Display for Secret {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "[REDACTED]")
149 }
150}
151
152impl fmt::Debug for Secret {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 write!(f, "Secret([REDACTED])")
155 }
156}
157
158#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
160pub struct Artifact {
161 pub filename: String,
163 pub size_bytes: u64,
165 pub checksum_algorithm: String,
167 pub checksum_value: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub compression: Option<String>,
172 #[serde(default)]
174 pub encrypted: bool,
175}
176
177#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
179pub struct Manifest {
180 pub version: String,
182 pub job_id: String,
184 pub agent: String,
186 pub agent_version: String,
188 pub role: String,
190 pub created_at: String,
192 pub artifacts: Vec<Artifact>,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub source_metadata: Option<serde_json::Value>,
197}
198
199#[derive(Error, Debug)]
201pub enum AgentError {
202 #[error("Placeholder: {0}")]
203 Placeholder(String),
204
205 #[error("Configuration error: {0}")]
206 Config(String),
207
208 #[error("Bus connection error: {0}")]
209 Bus(String),
210
211 #[error("IO error: {0}")]
212 Io(#[from] std::io::Error),
213
214 #[error("JSON error: {0}")]
215 Json(#[from] serde_json::Error),
216
217 #[error("Path traversal detected: {0}")]
218 PathTraversal(String),
219}
220
221const PLACEHOLDER_MESSAGE: &str = concat!(
222 "baqup-agent is currently a placeholder crate. ",
223 "Full implementation coming soon. ",
224 "See https://github.com/baqupio/baqup for updates."
225);
226
227pub fn compute_checksum(_path: &Path, _algorithm: &str) -> Result<String, AgentError> {
238 Err(AgentError::Placeholder(PLACEHOLDER_MESSAGE.to_string()))
239}
240
241pub fn validate_path(_path: &Path, _boundary: &Path) -> Result<bool, AgentError> {
256 Err(AgentError::Placeholder(PLACEHOLDER_MESSAGE.to_string()))
257}
258
259pub fn atomic_write(_staging_dir: &Path, _job_id: &str) -> Result<String, AgentError> {
270 Err(AgentError::Placeholder(PLACEHOLDER_MESSAGE.to_string()))
271}
272
273pub fn load_config(
283 _schema_path: &str,
284) -> Result<std::collections::HashMap<String, serde_json::Value>, AgentError> {
285 Err(AgentError::Placeholder(PLACEHOLDER_MESSAGE.to_string()))
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_exit_codes() {
294 assert_eq!(ExitCode::Success as i32, 0);
295 assert_eq!(ExitCode::GeneralFailure as i32, 1);
296 assert_eq!(ExitCode::UsageConfigError as i32, 64);
297 assert_eq!(ExitCode::DataError as i32, 65);
298 assert_eq!(ExitCode::ResourceUnavailable as i32, 69);
299 assert_eq!(ExitCode::InternalError as i32, 70);
300 assert_eq!(ExitCode::CantCreateOutput as i32, 73);
301 assert_eq!(ExitCode::IoError as i32, 74);
302 assert_eq!(ExitCode::CompletedUnreported as i32, 75);
303 assert_eq!(ExitCode::PartialFailure as i32, 76);
304 }
305
306 #[test]
307 fn test_agent_state_display() {
308 assert_eq!(AgentState::Initializing.to_string(), "initializing");
309 assert_eq!(AgentState::Running.to_string(), "running");
310 assert_eq!(AgentState::Completing.to_string(), "completing");
311 assert_eq!(AgentState::Terminated.to_string(), "terminated");
312 assert_eq!(AgentState::Failed.to_string(), "failed");
313 }
314
315 #[test]
316 fn test_secret_redaction() {
317 let secret = Secret::new("my-password");
318 assert_eq!(format!("{}", secret), "[REDACTED]");
319 assert_eq!(format!("{:?}", secret), "Secret([REDACTED])");
320 assert_eq!(secret.reveal(), "my-password");
321 }
322
323 #[test]
324 fn test_log_level_ordering() {
325 assert!(LogLevel::Trace < LogLevel::Debug);
326 assert!(LogLevel::Debug < LogLevel::Info);
327 assert!(LogLevel::Info < LogLevel::Warn);
328 assert!(LogLevel::Warn < LogLevel::Error);
329 assert!(LogLevel::Error < LogLevel::Fatal);
330 }
331}