///|
/// Convert a CborValue to CBOR Diagnostic Notation (RFC 8949 Section 8)
pub fn diagnostic(value : CborValue) -> String {
  let builder = StringBuilder::new()
  diagnostic_to_builder(value, builder)
  builder.to_string()
}

///|
fn diagnostic_to_builder(value : CborValue, builder : StringBuilder) -> Unit {
  match value {
    Unsigned(i) => builder.write_string(i.to_string())
    Integer(i) => builder.write_string(i.to_string())
    Bytes(b) => {
      builder.write_string("h'")
      let hex = [
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
        'f',
      ]
      for i = 0; i < b.length(); i = i + 1 {
        let val = b[i].to_int()
        let c1 = hex[val >> 4]
        let c2 = hex[val & 0x0F]
        builder.write_char(c1)
        builder.write_char(c2)
      }
      builder.write_string("'")
    }
    Text(s) => {
      builder.write_string("\"")
      for c in s {
        if c == '"' {
          builder.write_string("\\\"")
        } else if c == '\\' {
          builder.write_string("\\\\")
        } else if c == '\n' {
          builder.write_string("\\n")
        } else if c == '\r' {
          builder.write_string("\\r")
        } else if c == '\t' {
          builder.write_string("\\t")
        } else {
          builder.write_char(c)
        }
      }
      builder.write_string("\"")
    }
    Array(arr) => {
      builder.write_string("[")
      for i = 0; i < arr.length(); i = i + 1 {
        if i > 0 {
          builder.write_string(", ")
        }
        diagnostic_to_builder(arr[i], builder)
      }
      builder.write_string("]")
    }
    Map(m) => {
      builder.write_string("{")
      for i = 0; i < m.length(); i = i + 1 {
        if i > 0 {
          builder.write_string(", ")
        }
        diagnostic_to_builder(m[i].0, builder)
        builder.write_string(": ")
        diagnostic_to_builder(m[i].1, builder)
      }
      builder.write_string("}")
    }
    Tag(t, v) => {
      builder.write_string(t.to_string())
      builder.write_string("(")
      diagnostic_to_builder(v, builder)
      builder.write_string(")")
    }
    Simple(s) =>
      if s == 20 {
        builder.write_string("false")
      } else if s == 21 {
        builder.write_string("true")
      } else if s == 22 {
        builder.write_string("null")
      } else if s == 23 {
        builder.write_string("undefined")
      } else {
        builder.write_string("simple(")
        builder.write_string(s.to_int().to_string())
        builder.write_string(")")
      }
    Float64(f) => builder.write_string(f.to_string())
  }
}

///|
pub impl Show for CborValue with fn output(self, logger) {
  logger.write_string(diagnostic(self))
}