//! Concrete reporter implementations.
//!
//! Each reporter lives in its own submodule and is re-exported here so
//! callers can write `use mcptest_core::report::reporters::PrettyReporter;`
//! regardless of which file the impl lives in.

mod agent;
mod annotations;
mod failure_source;
mod gitlab;
mod html;
mod json;
mod junit;
mod markdown;
mod matrix;
mod ndjson;
mod openinference;
mod pretty;
mod sarif;
mod tap;

pub use agent::{
    agent_failures, agent_failures_with, estimate_tokens, AgentFailure, AgentReporter,
    DEFAULT_TOKEN_BUDGET,
};
pub use annotations::{emit_annotations, emit_warning, AnnotationsReporter};
pub use failure_source::{classify_report_failures, failure_source_breakdown, failure_source_json};
pub use gitlab::GitlabReporter;
pub use html::HtmlReporter;
pub use json::JsonReporter;
pub use junit::JunitReporter;
pub use markdown::MarkdownReporter;
pub use matrix::{MatrixMarkdownReporter, MatrixReporter};
pub use ndjson::NdjsonReporter;
pub use openinference::{OpeninferenceReporter, OPENINFERENCE_SCHEMA_VERSION};
pub use pretty::PrettyReporter;
pub use sarif::SarifReporter;
pub use tap::TapReporter;

/// Escape the five XML predefined entities so attribute and CDATA-ish
/// contexts stay safe. Shared by JUnit (and any future XML reporter).
pub(crate) fn xml_escape(s: &str) -> String {
    let mut out = String::with_capacity(s.len());
    for ch in s.chars() {
        match ch {
            '&' => out.push_str("&amp;"),
            '<' => out.push_str("&lt;"),
            '>' => out.push_str("&gt;"),
            '"' => out.push_str("&quot;"),
            '\'' => out.push_str("&apos;"),
            _ => out.push(ch),
        }
    }
    out
}
