use crate::workspace::Workspace;
use std::path::Path;
include!("file_based_extraction/io.rs");
pub mod paths {
pub const PLAN_XML: &str = ".agent/tmp/plan.xml";
pub const DEVELOPMENT_RESULT_XML: &str = ".agent/tmp/development_result.xml";
pub const ISSUES_XML: &str = ".agent/tmp/issues.xml";
pub const FIX_RESULT_XML: &str = ".agent/tmp/fix_result.xml";
pub const COMMIT_MESSAGE_XML: &str = ".agent/tmp/commit_message.xml";
}
#[must_use]
pub fn resolve_absolute_path(relative_path: &str) -> String {
resolve_with_current_dir(relative_path)
}
#[must_use]
pub fn resolve_absolute_path_at(repo_root: &std::path::Path, relative_path: &str) -> String {
repo_root.join(relative_path).display().to_string()
}
pub fn try_extract_from_file_with_workspace(
workspace: &dyn Workspace,
xml_path: &Path,
) -> Option<String> {
if !workspace.exists(xml_path) {
return None;
}
let content = workspace.read(xml_path).ok()?;
let trimmed = content.trim();
if trimmed.is_empty() || !trimmed.starts_with('<') {
return None;
}
Some(trimmed.to_string())
}
pub fn has_valid_xml_output(workspace: &dyn Workspace, xml_path: &Path) -> bool {
if !workspace.exists(xml_path) {
return false;
}
workspace.read(xml_path).is_ok_and(|content| {
let trimmed = content.trim();
!trimmed.is_empty() && trimmed.starts_with('<')
})
}
pub fn archive_xml_file_with_workspace(workspace: &dyn Workspace, xml_path: &Path) {
if workspace.exists(xml_path) {
let processed_path = xml_path.with_extension("xml.processed");
let _ = workspace.rename(xml_path, &processed_path);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::workspace::MemoryWorkspace;
#[test]
fn test_try_extract_from_file_success() {
let workspace = MemoryWorkspace::new_test().with_file(
"test.xml",
"<ralph-plan><ralph-summary>Test</ralph-summary></ralph-plan>",
);
let result = try_extract_from_file_with_workspace(&workspace, Path::new("test.xml"));
assert!(result.is_some());
assert!(result.unwrap().contains("<ralph-plan>"));
}
#[test]
fn test_try_extract_from_file_not_exists() {
let workspace = MemoryWorkspace::new_test();
let result =
try_extract_from_file_with_workspace(&workspace, Path::new("nonexistent/file.xml"));
assert!(result.is_none());
}
#[test]
fn test_try_extract_from_file_empty() {
let workspace = MemoryWorkspace::new_test().with_file("empty.xml", "");
let result = try_extract_from_file_with_workspace(&workspace, Path::new("empty.xml"));
assert!(result.is_none());
}
#[test]
fn test_try_extract_from_file_whitespace_only() {
let workspace = MemoryWorkspace::new_test().with_file("whitespace.xml", " \n \n ");
let result = try_extract_from_file_with_workspace(&workspace, Path::new("whitespace.xml"));
assert!(result.is_none());
}
#[test]
fn test_try_extract_from_file_not_xml() {
let workspace =
MemoryWorkspace::new_test().with_file("not_xml.txt", "This is plain text, not XML");
let result = try_extract_from_file_with_workspace(&workspace, Path::new("not_xml.txt"));
assert!(result.is_none());
}
#[test]
fn test_try_extract_from_file_trims_whitespace() {
let workspace = MemoryWorkspace::new_test()
.with_file("padded.xml", " \n<ralph-plan>Test</ralph-plan>\n ");
let result = try_extract_from_file_with_workspace(&workspace, Path::new("padded.xml"));
assert!(result.is_some());
let xml = result.unwrap();
assert!(xml.starts_with('<'));
assert!(xml.ends_with('>'));
}
#[test]
fn test_archive_xml_file_moves_file() {
let workspace =
MemoryWorkspace::new_test().with_file("to_archive.xml", "<test>content</test>");
archive_xml_file_with_workspace(&workspace, Path::new("to_archive.xml"));
assert!(!workspace.exists(Path::new("to_archive.xml")));
assert!(workspace.exists(Path::new("to_archive.xml.processed")));
assert_eq!(
workspace
.read(Path::new("to_archive.xml.processed"))
.unwrap(),
"<test>content</test>"
);
}
#[test]
fn test_archive_xml_file_handles_missing() {
let workspace = MemoryWorkspace::new_test();
archive_xml_file_with_workspace(&workspace, Path::new("nonexistent.xml"));
}
#[test]
fn test_archive_xml_file_overwrites_existing_processed() {
let workspace = MemoryWorkspace::new_test()
.with_file("test.xml.processed", "<old>data</old>")
.with_file("test.xml", "<new>data</new>");
archive_xml_file_with_workspace(&workspace, Path::new("test.xml"));
assert!(!workspace.exists(Path::new("test.xml")));
assert!(workspace.exists(Path::new("test.xml.processed")));
assert_eq!(
workspace.read(Path::new("test.xml.processed")).unwrap(),
"<new>data</new>"
);
}
#[test]
fn test_paths_constants() {
assert_eq!(paths::PLAN_XML, ".agent/tmp/plan.xml");
assert_eq!(paths::ISSUES_XML, ".agent/tmp/issues.xml");
assert_eq!(
paths::DEVELOPMENT_RESULT_XML,
".agent/tmp/development_result.xml"
);
assert_eq!(paths::FIX_RESULT_XML, ".agent/tmp/fix_result.xml");
assert_eq!(paths::COMMIT_MESSAGE_XML, ".agent/tmp/commit_message.xml");
}
#[test]
fn test_resolve_absolute_path_with_cwd() {
let result = resolve_absolute_path(".agent/tmp/issues.xml");
assert!(result.contains(".agent/tmp/issues.xml"));
assert!(result.len() > ".agent/tmp/issues.xml".len());
if std::env::current_dir().is_ok() {
#[cfg(unix)]
assert!(result.starts_with('/'));
#[cfg(windows)]
assert!(result.contains(":\\") || result.starts_with("\\\\"));
}
}
#[test]
fn test_resolve_absolute_path_fallback() {
let input = ".agent/tmp/test.xml";
let result = resolve_absolute_path(input);
assert!(result.contains("test.xml"));
}
#[test]
fn test_resolve_absolute_path_xsd() {
let result = resolve_absolute_path(".agent/tmp/issues.xsd");
assert!(result.contains(".agent/tmp/issues.xsd"));
}
}