// Example: the three verdicts, and what a diagnostic actually tells you.
//
// Run from the repository root:
//     moon run examples/diagnose
//
// The interesting part is the third case: the message parses, so the grammar has
// nothing to say, but two independent semantic rules fire and both point at a
// byte offset you can jump to in a capture.

///|
/// Shows the octets around `at` so a reported offset is obviously actionable.
fn context_around(input : Bytes, at : Int32) -> String {
  let start = if at > 18 { at - 18 } else { 0 }
  let stop = if at + 18 < input.length() { at + 18 } else { input.length() }
  @sipkit.ascii_of(input.exact_view(start~, end=stop))
}

///|
fn report(label : String, raw : Bytes) -> Unit {
  println("--- \{label}")
  let a = @sipkit.analyze(raw)
  println("    verdict: \{a.verdict()}")
  match a.syntax() {
    Some(pair) => {
      let (kind, at) = pair
      println("    rejected at byte \{at}: \{kind}")
      println("    ...\{context_around(raw, at)}...")
    }
    None => {
      let ds = a.diagnostics()
      if ds.length() == 0 {
        println("    no findings")
      }
      for d in ds {
        println("    [\{d.severity()}] \{d.code()} at byte \{d.offset()}")
        println("             \{d.detail()}")
      }
    }
  }
  println("")
}

///|
fn main {
  // 1. A clean request.
  report(
    "well-formed", b"OPTIONS sip:user@example.com SIP/2.0\r\nTo: \r\nFrom: ;tag=1\r\nCall-ID: ok-1\r\nCSeq: 1 OPTIONS\r\nVia: SIP/2.0/UDP host.example.com;branch=z9hG4bK1\r\nMax-Forwards: 70\r\nContent-Length: 0\r\n\r\n",
  )

  // 2. A grammar failure: the header block never terminates, so the parser runs
  //    out of octets while looking for the empty line.
  report(
    "syntax-rejected", b"OPTIONS sip:user@example.com SIP/2.0\r\nTo: \r\nFrom: ;tag=1\r\nCall-ID: broken-1\r\nCSeq: 1 OPTIONS\r\nVia: SIP/2.0/UDP host.example.com;branch=z9hG4bK1\r\nMax-Forwards: 70\r\nContent-Length: 0",
  )

  // 3. Syntactically perfect, semantically wrong twice: the CSeq names a
  //    different method than the start line, and Content-Length disagrees with
  //    the body that was actually framed.
  report(
    "semantic-rejected", b"OPTIONS sip:user@example.com SIP/2.0\r\nTo: \r\nFrom: ;tag=1\r\nCall-ID: sem-1\r\nCSeq: 1 INVITE\r\nVia: SIP/2.0/UDP host.example.com;branch=z9hG4bK1\r\nMax-Forwards: 70\r\nContent-Type: application/sdp\r\nContent-Length: 42\r\n\r\nv=0\r\n",
  )

  // 4. A warning-severity finding does not change the verdict, which is the
  //    distinction that lets a proxy forward instead of rejecting.
  report(
    "well-formed, with a warning", b"OPTIONS sip:user@example.com SIP/2.0\r\nTo: \r\nFrom: ;tag=1\r\nCall-ID: warn-1\r\nCSeq: 1 OPTIONS\r\nVia: SIP/2.0/UDP host.example.com\r\nContent-Length: 0\r\n\r\n",
  )
}