use crate::runner::ndjson::NdjsonResult;
use crate::types::RunnerMode;
#[derive(Debug, Clone, Default)]
pub struct WslOptions {
pub distro: Option<String>,
pub claude_path: Option<String>,
}
#[derive(Debug, Clone)]
pub struct BufferConfig {
pub stdout_cap_bytes: usize,
pub stderr_cap_bytes: usize,
#[allow(dead_code)] pub stderr_receipt_cap_bytes: usize,
}
impl Default for BufferConfig {
fn default() -> Self {
Self {
stdout_cap_bytes: 2 * 1024 * 1024, stderr_cap_bytes: 256 * 1024, stderr_receipt_cap_bytes: 2048, }
}
}
#[derive(Debug)]
pub struct ClaudeResponse {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
pub runner_used: RunnerMode,
pub runner_distro: Option<String>,
pub timed_out: bool,
pub ndjson_result: NdjsonResult,
#[allow(dead_code)] pub stdout_truncated: bool,
#[allow(dead_code)] pub stderr_truncated: bool,
#[allow(dead_code)] pub stdout_total_bytes: usize,
#[allow(dead_code)] pub stderr_total_bytes: usize,
}
impl ClaudeResponse {
#[must_use]
#[allow(dead_code)] pub fn stderr_for_receipt(&self, max_bytes: usize) -> String {
if self.stderr.len() <= max_bytes {
self.stderr.clone()
} else {
let bytes = self.stderr.as_bytes();
let start = bytes.len().saturating_sub(max_bytes);
String::from_utf8_lossy(&bytes[start..]).to_string()
}
}
}
#[cfg(test)]
mod tests {
use super::{BufferConfig, ClaudeResponse, WslOptions};
use crate::runner::ndjson::NdjsonResult;
use crate::types::RunnerMode;
#[test]
fn test_wsl_options_default() {
let options = WslOptions::default();
assert!(options.distro.is_none());
assert!(options.claude_path.is_none());
}
#[test]
fn test_buffer_config_default() {
let config = BufferConfig::default();
assert_eq!(config.stdout_cap_bytes, 2 * 1024 * 1024); assert_eq!(config.stderr_cap_bytes, 256 * 1024); assert_eq!(config.stderr_receipt_cap_bytes, 2048); }
#[test]
fn test_buffer_config_custom() {
let config = BufferConfig {
stdout_cap_bytes: 1024,
stderr_cap_bytes: 512,
stderr_receipt_cap_bytes: 256,
};
assert_eq!(config.stdout_cap_bytes, 1024);
assert_eq!(config.stderr_cap_bytes, 512);
assert_eq!(config.stderr_receipt_cap_bytes, 256);
}
#[test]
fn test_claude_response_stderr_for_receipt_no_truncation() {
let response = ClaudeResponse {
stdout: String::new(),
stderr: "Short error message".to_string(),
exit_code: 0,
runner_used: RunnerMode::Native,
runner_distro: None,
timed_out: false,
ndjson_result: NdjsonResult::NoValidJson {
tail_excerpt: String::new(),
},
stdout_truncated: false,
stderr_truncated: false,
stdout_total_bytes: 0,
stderr_total_bytes: 20,
};
let stderr_receipt = response.stderr_for_receipt(2048);
assert_eq!(stderr_receipt, "Short error message");
}
#[test]
fn test_claude_response_stderr_for_receipt_with_truncation() {
let long_stderr = "x".repeat(3000);
let response = ClaudeResponse {
stdout: String::new(),
stderr: long_stderr,
exit_code: 0,
runner_used: RunnerMode::Native,
runner_distro: None,
timed_out: false,
ndjson_result: NdjsonResult::NoValidJson {
tail_excerpt: String::new(),
},
stdout_truncated: false,
stderr_truncated: false,
stdout_total_bytes: 0,
stderr_total_bytes: 3000,
};
let stderr_receipt = response.stderr_for_receipt(2048);
assert_eq!(stderr_receipt.len(), 2048);
assert_eq!(stderr_receipt, "x".repeat(2048));
}
#[test]
fn test_claude_response_stderr_for_receipt_exact_limit() {
let stderr = "x".repeat(2048);
let response = ClaudeResponse {
stdout: String::new(),
stderr: stderr.clone(),
exit_code: 0,
runner_used: RunnerMode::Native,
runner_distro: None,
timed_out: false,
ndjson_result: NdjsonResult::NoValidJson {
tail_excerpt: String::new(),
},
stdout_truncated: false,
stderr_truncated: false,
stdout_total_bytes: 0,
stderr_total_bytes: 2048,
};
let stderr_receipt = response.stderr_for_receipt(2048);
assert_eq!(stderr_receipt.len(), 2048);
assert_eq!(stderr_receipt, stderr);
}
#[test]
fn test_claude_response_stderr_for_receipt_custom_limit() {
let stderr = "Hello, world! This is a test message.".to_string();
let response = ClaudeResponse {
stdout: String::new(),
stderr: stderr.clone(),
exit_code: 0,
runner_used: RunnerMode::Native,
runner_distro: None,
timed_out: false,
ndjson_result: NdjsonResult::NoValidJson {
tail_excerpt: String::new(),
},
stdout_truncated: false,
stderr_truncated: false,
stdout_total_bytes: 0,
stderr_total_bytes: stderr.len(),
};
let stderr_receipt = response.stderr_for_receipt(10);
assert_eq!(stderr_receipt.len(), 10);
assert_eq!(stderr_receipt, "t message.");
}
}