///|
let hex_digits : String = "0123456789abcdef"

///|
/// Write a JSON string literal with serde_json's escaping rules: only `"`,
/// `\` and control characters are escaped; everything else is emitted as-is.
pub fn write_escaped_str(buf : StringBuilder, s : StringView) -> Unit {
  buf.write_char('"')
  for c in s {
    match c {
      '"' => buf.write_string("\\\"")
      '\\' => buf.write_string("\\\\")
      '\b' => buf.write_string("\\b")
      '\t' => buf.write_string("\\t")
      '\n' => buf.write_string("\\n")
      '\u{0C}' => buf.write_string("\\f")
      '\r' => buf.write_string("\\r")
      c if c.to_int() < 0x20 => {
        let n = c.to_int()
        buf.write_string("\\u00")
        buf.write_char(hex_digits.get_char(n >> 4).unwrap())
        buf.write_char(hex_digits.get_char(n & 0xF).unwrap())
      }
      c => buf.write_char(c)
    }
  }
  buf.write_char('"')
}

///|
fn write_compact(buf : StringBuilder, v : Value) -> Unit {
  match v {
    Null => buf.write_string("null")
    Bool(true) => buf.write_string("true")
    Bool(false) => buf.write_string("false")
    Number(n) => buf.write_string(n.to_string())
    String(s) => write_escaped_str(buf, s)
    Array(xs) => {
      buf.write_char('[')
      for i, x in xs {
        if i > 0 {
          buf.write_char(',')
        }
        write_compact(buf, x)
      }
      buf.write_char(']')
    }
    Object(o) => {
      buf.write_char('{')
      let mut first = true
      for k, x in o {
        if !first {
          buf.write_char(',')
        }
        first = false
        write_escaped_str(buf, k)
        buf.write_char(':')
        write_compact(buf, x)
      }
      buf.write_char('}')
    }
  }
}

///|
fn write_indent(buf : StringBuilder, level : Int) -> Unit {
  for _ in 0.. Unit {
  match v {
    Array(xs) => {
      if xs.is_empty() {
        buf.write_string("[]")
        return
      }
      buf.write_char('[')
      for i, x in xs {
        buf.write_string(if i > 0 { ",\n" } else { "\n" })
        write_indent(buf, level + 1)
        write_pretty(buf, x, level + 1)
      }
      buf.write_char('\n')
      write_indent(buf, level)
      buf.write_char(']')
    }
    Object(o) => {
      if o.is_empty() {
        buf.write_string("{}")
        return
      }
      buf.write_char('{')
      let mut first = true
      for k, x in o {
        buf.write_string(if first { "\n" } else { ",\n" })
        first = false
        write_indent(buf, level + 1)
        write_escaped_str(buf, k)
        buf.write_string(": ")
        write_pretty(buf, x, level + 1)
      }
      buf.write_char('\n')
      write_indent(buf, level)
      buf.write_char('}')
    }
    _ => write_compact(buf, v)
  }
}