pub mod a2a;
pub mod compose;
pub mod control;
pub mod docker;
pub mod factory;
pub mod health;
pub mod heartbeat;
pub mod http;
pub mod local;
pub mod logging;
pub mod metrics;
pub mod pool;
pub mod retry;
pub mod shutdown;
pub mod tracing;
pub mod watcher;
pub use a2a::*;
pub use compose::*;
pub use control::*;
pub use docker::*;
pub use factory::*;
pub use health::*;
pub use heartbeat::*;
pub use http::*;
pub use local::*;
pub use logging::*;
pub use metrics::*;
pub use pool::*;
pub use retry::*;
pub use shutdown::*;
pub use tracing::*;
pub use watcher::*;
pub const MAX_STDERR_BYTES: usize = 8 * 1024;
pub fn parse_worker_output(stdout: &str, stderr: &str) -> Option<serde_json::Value> {
let trimmed_out = stdout.trim();
let trimmed_err = stderr.trim();
match (trimmed_out.is_empty(), trimmed_err.is_empty()) {
(true, true) => None,
(true, false) => Some(serde_json::json!({"stderr": trimmed_err})),
(false, true) => match serde_json::from_str(trimmed_out) {
Ok(value) => Some(value),
Err(_) => Some(serde_json::json!({"stdout": trimmed_out})),
},
(false, false) => match serde_json::from_str::<serde_json::Value>(trimmed_out) {
Ok(mut value) => {
if let Some(obj) = value.as_object_mut() {
obj.insert("stderr".into(), serde_json::Value::String(trimmed_err.into()));
}
Some(value)
}
Err(_) => Some(serde_json::json!({"stdout": trimmed_out, "stderr": trimmed_err})),
},
}
}
pub fn truncate_stderr(s: &str) -> &str {
if s.len() <= MAX_STDERR_BYTES {
s
} else {
let mut boundary = MAX_STDERR_BYTES;
while !s.is_char_boundary(boundary) {
boundary -= 1;
}
&s[..boundary]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_worker_output_json() {
let result = parse_worker_output(r#"{"key":"value"}"#, "");
assert_eq!(result, Some(serde_json::json!({"key": "value"})));
}
#[test]
fn test_parse_worker_output_plain_text() {
let result = parse_worker_output("hello world", "");
assert_eq!(result, Some(serde_json::json!({"stdout": "hello world"})));
}
#[test]
fn test_parse_worker_output_empty() {
assert_eq!(parse_worker_output("", ""), None);
assert_eq!(parse_worker_output(" \n", ""), None);
}
#[test]
fn test_parse_worker_output_json_with_whitespace() {
let result = parse_worker_output(" {\"key\": 42} \n", "");
assert_eq!(result, Some(serde_json::json!({"key": 42})));
}
#[test]
fn test_parse_worker_output_stderr_only() {
let result = parse_worker_output("", "error: something went wrong");
assert_eq!(
result,
Some(serde_json::json!({"stderr": "error: something went wrong"}))
);
}
#[test]
fn test_parse_worker_output_json_with_stderr() {
let result = parse_worker_output(r#"{"result":1}"#, "warning: deprecated");
assert_eq!(
result,
Some(serde_json::json!({"result": 1, "stderr": "warning: deprecated"}))
);
}
#[test]
fn test_parse_worker_output_text_with_stderr() {
let result = parse_worker_output("hello", "some warning");
assert_eq!(
result,
Some(serde_json::json!({"stdout": "hello", "stderr": "some warning"}))
);
}
#[test]
fn test_parse_worker_output_both_empty() {
assert_eq!(parse_worker_output("", ""), None);
assert_eq!(parse_worker_output(" ", " "), None);
}
#[test]
fn test_truncate_stderr_short() {
let s = "short error";
assert_eq!(truncate_stderr(s), s);
}
#[test]
fn test_truncate_stderr_at_limit() {
let s = "a".repeat(MAX_STDERR_BYTES);
assert_eq!(truncate_stderr(&s).len(), MAX_STDERR_BYTES);
}
#[test]
fn test_truncate_stderr_over_limit() {
let s = "a".repeat(MAX_STDERR_BYTES + 100);
assert_eq!(truncate_stderr(&s).len(), MAX_STDERR_BYTES);
}
}