#[must_use]
pub fn format_xml_for_display(xml_content: &str) -> String {
if !xml_content.contains('<') {
return xml_content.to_string();
}
let pretty = pretty_print_xml(xml_content);
if pretty.is_empty() {
xml_content.to_string()
} else {
pretty
}
}
#[expect(
clippy::arithmetic_side_effects,
reason = "bounds-checked index arithmetic"
)]
fn pretty_print_xml(xml_content: &str) -> String {
#[derive(Copy, Clone)]
enum XmlMode {
Outside,
Tag { start: usize },
Content,
}
struct FormatterState {
result: String,
indent: usize,
mode: XmlMode,
}
let chars: Vec<char> = xml_content.chars().collect();
let final_state = chars.iter().enumerate().fold(
FormatterState {
result: String::new(),
indent: 0,
mode: XmlMode::Outside,
},
|mut state, (i, &c)| {
match c {
'<' => {
let next_char = chars.get(i + 1).copied();
let is_closing_tag = matches!(next_char, Some('/'));
if is_closing_tag {
if matches!(state.mode, XmlMode::Content) && state.indent > 0 {
state.result += "\n";
}
state.indent = state.indent.saturating_sub(1);
} else if matches!(state.mode, XmlMode::Content) {
state.result += "\n";
}
state.mode = XmlMode::Tag { start: i };
}
'>' => {
if let XmlMode::Tag { start } = state.mode {
let char_after_lt = chars.get(start + 1).copied().unwrap_or('\0');
let is_self_closing = i > 0 && chars[i - 1] == '/';
let is_declaration = matches!(chars.get(start + 1), Some('?'))
&& i > 0
&& chars[i - 1] == '?';
let skips_prefix = char_after_lt == '/' || char_after_lt == '?';
let tag_name_start = if skips_prefix { start + 2 } else { start + 1 };
let tag_name_end = i;
let tag_name: String = if tag_name_start < tag_name_end {
chars[tag_name_start..tag_name_end]
.iter()
.take_while(|&ch| !ch.is_whitespace() && *ch != '/')
.collect()
} else {
String::new()
};
let should_indent =
!is_self_closing && !is_declaration && !char_after_lt.is_whitespace();
if should_indent {
if !state.result.ends_with('\n') && !state.result.is_empty() {
state.result += "\n";
}
state.result += &" ".repeat(state.indent);
}
let segment: String = chars[start..=i].iter().collect();
state.result += &segment;
let should_increase_indent = !is_self_closing
&& !is_declaration
&& char_after_lt != '/'
&& !tag_name.is_empty();
if should_increase_indent {
state.indent = state.indent.saturating_add(1);
state.mode = XmlMode::Content;
} else {
state.mode = XmlMode::Outside;
}
} else {
let addition = c.to_string();
state.result += addition.as_str();
}
}
'\n' | '\r' | '\t' => {}
' ' => {
if matches!(state.mode, XmlMode::Tag { .. }) {
state.result += " ";
} else if matches!(state.mode, XmlMode::Content) {
if let Some(last_char) = state.result.chars().last() {
if last_char != ' ' && last_char != '\n' {
state.result += " ";
}
} else {
state.result += " ";
}
}
}
_ => {
if matches!(state.mode, XmlMode::Tag { .. } | XmlMode::Content) {
let addition = c.to_string();
state.result += addition.as_str();
}
}
}
state
},
);
final_state.result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_simple_xml() {
let xml = r"<ralph-plan><ralph-summary>Summary</ralph-summary></ralph-plan>";
let formatted = format_xml_for_display(xml);
assert!(formatted.contains("<ralph-plan>"));
assert!(formatted.contains("<ralph-summary>"));
assert!(formatted.contains("Summary"));
}
#[test]
fn test_format_nested_xml() {
let xml = r"<ralph-issues><ralph-issue>Issue 1</ralph-issue><ralph-issue>Issue 2</ralph-issue></ralph-issues>";
let formatted = format_xml_for_display(xml);
assert!(formatted.contains("<ralph-issues>"));
assert!(formatted.contains("<ralph-issue>"));
}
#[test]
fn test_format_with_attributes() {
let xml = r"<ralph-fix-result><ralph-status>all_issues_addressed</ralph-status></ralph-fix-result>";
let formatted = format_xml_for_display(xml);
assert!(formatted.contains("<ralph-fix-result>"));
assert!(formatted.contains("<ralph-status>"));
}
#[test]
fn test_format_empty_xml() {
let xml = "";
let formatted = format_xml_for_display(xml);
assert_eq!(formatted, "");
}
#[test]
fn test_format_invalid_xml_returns_original() {
let xml = "This is not XML at all";
let formatted = format_xml_for_display(xml);
assert_eq!(formatted, "This is not XML at all");
}
#[test]
fn test_format_xml_with_declaration() {
let xml = r#"<?xml version="1.0"?><ralph-plan><ralph-summary>Summary</ralph-summary></ralph-plan>"#;
let formatted = format_xml_for_display(xml);
assert!(formatted.contains("<?xml"));
assert!(formatted.contains("<ralph-plan>"));
}
}