grpctestify 1.6.1

gRPC testing utility written in Rust
Documentation
pub mod assert;
pub mod cli;
pub mod commands;
pub mod config;
pub mod diagnostics;
pub mod execution;
pub mod grpc;
pub mod logging;
pub mod lsp;
pub mod optimizer;
pub mod parser;
pub mod plugins;
pub mod polyfill;
pub mod report;
pub mod semantics;
pub mod state;
pub mod time;
pub mod utils;

pub use parser::parse_gctf;
pub use parser::validate_document;

/// Format/serialize a GCTF document to string
pub fn serialize_gctf(doc: &parser::GctfDocument) -> String {
    use std::fmt::Write;
    let mut output = String::new();

    for section in &doc.sections {
        let _ = write!(output, "--- {} ---", section.section_type.as_str());
        output.push('\n');

        match &section.content {
            parser::ast::SectionContent::Single(s) => {
                let _ = writeln!(output, "{}", s.trim());
            }
            parser::ast::SectionContent::Json(val) => {
                // Try to format as pretty JSON, fall back to raw if it fails (JSON5/comments)
                if let Ok(pretty) = serde_json::to_string_pretty(val) {
                    let _ = writeln!(output, "{}", pretty);
                } else {
                    // Preserve raw content for JSON5 with comments
                    let raw = section.raw_content.trim();
                    let _ = writeln!(output, "{}", raw);
                }
            }
            parser::ast::SectionContent::JsonLines(lines) => {
                // Each line is a separate JSON object - keep on single line for idempotency
                for val in lines {
                    if let Ok(compact) = serde_json::to_string(val) {
                        let _ = writeln!(output, "{}", compact);
                    }
                }
            }
            parser::ast::SectionContent::KeyValues(kv) => {
                // Sort keys for deterministic output
                let mut sorted: Vec<_> = kv.iter().collect();
                sorted.sort_by(|a, b| a.0.cmp(b.0));
                for (k, v) in sorted {
                    let _ = writeln!(output, "{}: {}", k, v);
                }
            }
            parser::ast::SectionContent::Assertions(lines) => {
                for line in lines {
                    let _ = writeln!(output, "{}", line.trim());
                }
            }
            parser::ast::SectionContent::Empty => {}
            parser::ast::SectionContent::Extract(vars) => {
                let mut sorted: Vec<_> = vars.iter().collect();
                sorted.sort_by(|a, b| a.0.cmp(b.0));
                for (k, v) in sorted {
                    let _ = writeln!(output, "{}: {}", k, v);
                }
            }
            parser::ast::SectionContent::Meta(meta) => {
                if let Ok(yaml) = serde_yaml_ng::to_string(meta) {
                    output.push_str(yaml.trim_end());
                }
            }
        }
        output.push('\n');
    }

    output.trim_end().to_string() + "\n"
}