///|
/// Parse a trace and reject deep validation errors.
pub fn parse_strict(input : String) -> Result[Trace, ParseError] {
  match parse(input) {
    Ok(trace) => {
      let items = deep_validate(trace)
      if items.any(fn(item) { item.severity == "error" }) {
        Err(InvalidDirective(diagnostics_report(items)))
      } else {
        Ok(trace)
      }
    }
    Err(error) => Err(error)
  }
}

///|
/// Parse and repair a document by sorting changes and dropping invalid ids.
pub fn parse_repaired(input : String) -> Result[Trace, ParseError] {
  parse(input).map(repair_trace)
}

///|
/// Parse a document or return None for any malformed input.
pub fn parse_optional(input : String) -> Trace? {
  match parse(input) {
    Ok(trace) => Some(trace)
    Err(_) => None
  }
}

///|
/// Parse a document using fixed-size input chunks.
pub fn parse_incremental(
  input : String,
  chunk_size : Int,
) -> Result[Array[Trace], ParseError] {
  parse_chunked(input, chunk_size)
}

///|
/// Parse all records and return the first error only after complete traversal.
pub fn parse_documents(documents : Array[String]) -> Array[ParseSummary] {
  documents.map(summarize_parse)
}

///|
/// Return common parser counters for a document.
pub fn parser_counters(input : String) -> (Int, Int, Int) {
  let summary = summarize_parse(input)
  (physical_line_count(input), summary.signal_count, summary.change_count)
}