///|
/// A caller-supplied resource record, never a compressed DNS message fragment.
pub struct Record {
  priv owner : Name
  priv rrtype : Int32
  priv ttl : UInt32
  priv data : Bytes
} derive(Eq, Debug)

///|
pub extend Record with Eq::{equal, not_equal}

///|
pub extend Record with @debug.Debug::{to_repr}

///|
pub fn Record::new(
  owner : Name,
  rrtype : Int32,
  ttl : UInt32,
  data : Bytes,
) -> Result[Record, DnsError] {
  if rrtype < 1 || rrtype > 65535 || rrtype == 41 || rrtype == 255 {
    return Err(Malformed("invalid RRset type"))
  }
  if data.length() > 65535 {
    return Err(ResourceLimit("RDATA exceeds wire field"))
  }
  if ttl >= 0x80000000U {
    return Err(Malformed("TTL exceeds RFC2181 limit"))
  }
  Ok({ owner, rrtype, ttl, data, })
}

///|
pub fn Record::owner(self : Record) -> Name {
  self.owner
}

///|
pub fn Record::rrtype(self : Record) -> Int32 {
  self.rrtype
}

///|
pub fn Record::rdata(self : Record) -> Bytes {
  self.data
}

///|
pub fn Record::ttl(self : Record) -> UInt32 {
  self.ttl
}

///|
/// Canonical RDATA for enabled types; known unimplemented name-bearing types fail closed.
pub fn Record::canonical_rdata(self : Record) -> Result[Bytes, DnsError] {
  let data = self.data
  let t = self.rrtype
  if t == 1 {
    return if data.length() == 4 {
      Ok(data)
    } else {
      Err(Malformed("A length"))
    }
  }
  if t == 28 {
    return if data.length() == 16 {
      Ok(data)
    } else {
      Err(Malformed("AAAA length"))
    }
  }
  if t == 2 || t == 5 || t == 12 || t == 39 {
    return match Name::from_wire(data) {
      Ok(n) => Ok(n.wire())
      Err(e) => Err(e)
    }
  }
  if t == 15 || t == 33 {
    let prefix = if t == 15 { 2 } else { 6 }
    let name = match read_name(data, prefix) {
      Ok(x) => x
      Err(e) => return Err(e)
    }
    if name.1 != data.length() {
      return Err(Malformed("trailing name-bearing RDATA"))
    }
    let out : Array[Byte] = []
    append_bytes(out, data[:prefix].to_owned())
    append_bytes(out, name.0.wire())
    return Ok(Bytes::from_array(out))
  }
  if t == 6 {
    let (mname, p) = match read_name(data, 0) {
      Ok(x) => x
      Err(e) => return Err(e)
    }
    let (rname, q) = match read_name(data, p) {
      Ok(x) => x
      Err(e) => return Err(e)
    }
    if data.length() - q != 20 {
      return Err(Malformed("SOA numeric fields"))
    }
    let out : Array[Byte] = []
    append_bytes(out, mname.wire())
    append_bytes(out, rname.wire())
    append_bytes(out, data[q:].to_owned())
    return Ok(Bytes::from_array(out))
  }
  if t == 47 {
    let (next, p) = match read_name(data, 0) {
      Ok(x) => x
      Err(e) => return Err(e)
    }
    if p == data.length() {
      return Err(Malformed("NSEC requires type bitmap"))
    }
    let out : Array[Byte] = []
    append_bytes(out, next.wire())
    append_bytes(out, data[p:].to_owned())
    return Ok(Bytes::from_array(out))
  }
  if t == 16 || t == 99 {
    let mut p = 0
    if data.is_empty() {
      return Err(Malformed("empty TXT RDATA"))
    }
    while p < data.length() {
      let n = data[p].to_int()
      p += n + 1
      if p > data.length() {
        return Err(Malformed("truncated TXT string"))
      }
    }
    return Ok(data)
  }
  if t == 3 ||
    t == 4 ||
    t == 7 ||
    t == 8 ||
    t == 9 ||
    t == 14 ||
    t == 17 ||
    t == 18 ||
    t == 21 ||
    t == 24 ||
    t == 26 ||
    t == 30 ||
    t == 35 ||
    t == 36 ||
    t == 38 ||
    t == 45 ||
    t == 55 ||
    t == 58 ||
    t == 64 ||
    t == 65 ||
    t == 107 ||
    t == 250 ||
    t == 251 ||
    t == 252 {
    return Err(
      Unsupported("canonicalization for this known RR type is disabled"),
    )
  }
  // RFC3597 unknown types are opaque and cannot carry DNS compression pointers.
  Ok(data)
}