///|
/// Signature types (RFC 9580 §5.2.1).
pub(all) enum SigType {
  BinaryDocument
  CanonicalTextDocument
  Standalone
  GenericCertification
  PersonaCertification
  CasualCertification
  PositiveCertification
  SubkeyBinding
  PrimaryKeyBinding
  DirectSignature
  KeyRevocation
  SubkeyRevocation
  CertificationRevocation
  Timestamp
  ThirdPartyConfirmation
  Unknown(Int32)
} derive(Eq, Debug)

///|
pub fn SigType::number(self : SigType) -> Int32 {
  match self {
    BinaryDocument => 0
    CanonicalTextDocument => 1
    Standalone => 2
    GenericCertification => 16
    PersonaCertification => 17
    CasualCertification => 18
    PositiveCertification => 19
    SubkeyBinding => 24
    PrimaryKeyBinding => 25
    DirectSignature => 31
    KeyRevocation => 32
    SubkeyRevocation => 40
    CertificationRevocation => 48
    Timestamp => 64
    ThirdPartyConfirmation => 80
    Unknown(n) => n
  }
}

///|
pub fn sig_type_of_number(n : Int32) -> SigType {
  match n {
    0 => BinaryDocument
    1 => CanonicalTextDocument
    2 => Standalone
    16 => GenericCertification
    17 => PersonaCertification
    18 => CasualCertification
    19 => PositiveCertification
    24 => SubkeyBinding
    25 => PrimaryKeyBinding
    31 => DirectSignature
    32 => KeyRevocation
    40 => SubkeyRevocation
    48 => CertificationRevocation
    64 => Timestamp
    80 => ThirdPartyConfirmation
    _ => Unknown(n)
  }
}

///|
/// Signature subpacket type IDs (subset, RFC 9580 §5.2.3.1).
pub const SIGSUB_CREATION_TIME : Int32 = 2

///|
pub const SIGSUB_SIG_EXPIRATION_TIME : Int32 = 3

///|
pub const SIGSUB_EXPORTABLE_CERTIFICATION : Int32 = 4

///|
pub const SIGSUB_PREFERRED_SYM_ALGOS : Int32 = 11

///|
pub const SIGSUB_PREFERRED_HASH_ALGOS : Int32 = 21

///|
pub const SIGSUB_PREFERRED_COMP_ALGOS : Int32 = 22

///|
pub const SIGSUB_ISSUER_KEY_ID : Int32 = 16

///|
pub const SIGSUB_TRUST_SIGNATURE : Int32 = 5

///|
pub const SIGSUB_REGULAR_EXPRESSION : Int32 = 6

///|
pub const SIGSUB_REVOCABLE : Int32 = 7

///|
pub const SIGSUB_KEY_EXPIRATION_TIME : Int32 = 9

///|
pub const SIGSUB_NOTATION_DATA : Int32 = 20

///|
pub const SIGSUB_KEY_SERVER_PREFERENCES : Int32 = 23

///|
pub const SIGSUB_PREFERRED_KEY_SERVER : Int32 = 24

///|
pub const SIGSUB_PRIMARY_USER_ID : Int32 = 25

///|
pub const SIGSUB_POLICY_URI : Int32 = 26

///|
pub const SIGSUB_SIGNATURE_TARGET : Int32 = 31

///|
pub const SIGSUB_KEY_FLAGS : Int32 = 27

///|
/// Preferred AEAD Ciphersuites (RFC 9580 §5.2.3.15). Table 5 of RFC 9580
/// assigns type 39; type 34 is Reserved, so using it here made the
/// preference invisible to every other implementation (and hid theirs
/// from us) while our own round-trip tests stayed green.
pub const SIGSUB_PREFERRED_AEAD : Int32 = 39

///|
/// The Features flags octet (RFC 9580 §5.2.3.32): 0x01 = v1 SEIPD,
/// 0x08 = v2 SEIPD.
pub const FEATURE_SEIPD_V1 : Int32 = 0x01

///|
pub const FEATURE_SEIPD_V2 : Int32 = 0x08

///|
pub const SIGSUB_SIGNER_USER_ID : Int32 = 28

///|
pub const SIGSUB_REVOCATION_REASON : Int32 = 29

///|
pub const SIGSUB_FEATURES : Int32 = 30

///|
pub const SIGSUB_EMBEDDED_SIGNATURE : Int32 = 32

///|
pub const SIGSUB_ISSUER_FINGERPRINT : Int32 = 33

///|
/// A raw signature subpacket: critical bit, type id, body.
pub(all) struct SigSubpacket {
  critical : Bool
  sub_type : Int32
  body : Bytes
} derive(Debug, Eq)

///|
pub fn SigSubpacket::critical(self : SigSubpacket) -> Bool {
  self.critical
}

///|
pub fn SigSubpacket::sub_type(self : SigSubpacket) -> Int32 {
  self.sub_type
}

///|
pub fn SigSubpacket::body(self : SigSubpacket) -> Bytes {
  self.body
}

///|
/// A version 4 or version 6 signature packet body (RFC 9580 §5.2.3).
/// Version 6 differs in the subpacket-area length widths (4 octets), the
/// trailing salt, the trailer's version octet, and raw (non-MPI) Ed25519
/// signature material.
pub(all) struct SignaturePacket {
  version : Int32
  sig_type : SigType
  pub_algo : PubKeyAlgo
  hash_algo : Int32
  hashed_subpackets : Array[SigSubpacket]
  unhashed_subpackets : Array[SigSubpacket]
  /// The hashed subpacket area exactly as it appeared on the wire; third
  /// party signatures must be hashed over these raw bytes, not a
  /// re-serialization (subpacket length encodings may differ).
  raw_hashed_area : Bytes
  /// First two octets of the digest (fast check).
  fast_hash : Int32
  /// v6 only: the 16/24/32-octet salt, hashed before the data and NOT
  /// counted in the trailer length. Empty for v4.
  salt : Bytes
  /// Algorithm-specific signature material in wire order (RSA: one MPI;
  /// EdDSA legacy: two MPIs; Ed25519 v6: one raw 64-octet blob).
  sig_params : Array[Bytes]
} derive(Debug)

///|
pub fn SignaturePacket::version(self : SignaturePacket) -> Int32 {
  self.version
}

///|
pub fn SignaturePacket::salt(self : SignaturePacket) -> Bytes {
  self.salt
}

///|
pub fn SignaturePacket::sig_type(self : SignaturePacket) -> SigType {
  self.sig_type
}

///|
pub fn SignaturePacket::pub_algo(self : SignaturePacket) -> PubKeyAlgo {
  self.pub_algo
}

///|
pub fn SignaturePacket::hash_algo(self : SignaturePacket) -> Int32 {
  self.hash_algo
}

///|
pub fn SignaturePacket::hashed_subpackets(
  self : SignaturePacket,
) -> Array[SigSubpacket] {
  self.hashed_subpackets
}

///|
pub fn SignaturePacket::unhashed_subpackets(
  self : SignaturePacket,
) -> Array[SigSubpacket] {
  self.unhashed_subpackets
}

///|
pub fn SignaturePacket::fast_hash(self : SignaturePacket) -> Int32 {
  self.fast_hash
}

///|
pub fn SignaturePacket::sig_params(self : SignaturePacket) -> Array[Bytes] {
  self.sig_params
}

///|
fn parse_subpackets(view : BytesView) -> Array[SigSubpacket] raise {
  let out : Array[SigSubpacket] = []
  let r = Reader::new(view)
  while r.remaining() > 0 {
    let first = r.read_u8()
    let len : Int32 = if first < 192 {
      first
    } else if first < 255 {
      let second = r.read_u8()
      ((first - 192) << 8) + second + 192
    } else {
      r.read_u32()
    }
    let body = r.read_bytes(len)
    guard body.length() >= 1 else {
      @error.fail("packet: zero-length signature subpacket")
    }
    let t = body[0].to_int()
    out.push(SigSubpacket::{
      critical: (t & 0x80) != 0,
      sub_type: t & 0x7F,
      body: body[1:body.length()].to_owned(),
    })
  }
  out
}

///|
fn serialize_subpackets(subs : Array[SigSubpacket]) -> Bytes {
  let w = Writer::new()
  for sp in subs {
    let body_len = sp.body.length() + 1
    if body_len < 192 {
      w.u8(body_len)
    } else if body_len < 8384 {
      let enc = body_len - 192 + 192 * 256
      w.u8(enc >> 8)
      w.u8(enc)
    } else {
      w.u8(255)
      w.u32(body_len)
    }
    let type_octet = if sp.critical { sp.sub_type | 0x80 } else { sp.sub_type }
    w.u8(type_octet)
    w.bytes(sp.body.exact_view())
  }
  w.to_bytes()
}

///|
/// RFC 9580 §9.5 Table 23: the v6 signature salt size for a hash
/// algorithm. Only SHA-2/SHA-3 have v6 salt sizes (MD5/SHA-1/RIPEMD-160
/// are "N/A").
pub fn salt_size_of_hash(hash_id : Int32) -> Int32 raise {
  match hash_id {
    8 | 11 | 12 => 16 // SHA-256, SHA-224, SHA3-256
    9 => 24 // SHA-384
    10 | 14 => 32 // SHA-512, SHA3-512
    _ =>
      @error.fail(
        "packet: hash algorithm \{hash_id} has no v6 signature salt size",
      )
  }
}

///|
pub fn SignaturePacket::parse(body : BytesView) -> SignaturePacket raise {
  let r = Reader::new(body)
  let version = r.read_u8()
  guard version == 4 || version == 6 else {
    @error.fail("packet: unsupported signature version \{version}")
  }
  let v6 = version == 6
  let sig_type = sig_type_of_number(r.read_u8())
  let pub_algo = pub_key_algo_of_number(r.read_u8())
  let hash_algo = r.read_u8()
  let hashed_len : Int32 = if v6 { r.read_u32() } else { r.read_u16() }
  let raw_hashed = r.read_bytes(hashed_len)
  let hashed = parse_subpackets(raw_hashed.exact_view())
  let unhashed_len : Int32 = if v6 { r.read_u32() } else { r.read_u16() }
  let unhashed = parse_subpackets(r.read_bytes(unhashed_len).exact_view())
  let fast = r.read_u16()
  let salt : Bytes = if v6 {
    let salt_len = r.read_u8()
    let expected = salt_size_of_hash(hash_algo)
    guard salt_len == expected else {
      @error.fail(
        "packet: v6 signature salt of \{salt_len} octets does not match the hash algorithm (expected \{expected})",
      )
    }
    r.read_bytes(salt_len)
  } else {
    Bytes::new(0)
  }
  let params : Array[Bytes] = []
  match pub_algo {
    RsaEncryptOrSign | RsaEncryptOnly | RsaSignOnly => params.push(r.read_mpi())
    EdDsaLegacy => {
      // RFC 9580 §5.2.3.3.1: "Ed25519Legacy MUST NOT be used in Signature
      // packets version 6 or above" — only for reading historic v4 keys.
      guard version < 6 else {
        @error.fail(
          "packet: EdDsaLegacy must not be used in a v6 signature packet",
        )
      }
      // EdDSA legacy carries two MPIs: r then s (RFC 9580 §5.2.3.1).
      params.push(r.read_mpi())
      params.push(r.read_mpi())
    }
    Ed25519 => {
      // v6 Ed25519 carries 64 raw octets, not MPIs (§5.2.3.4).
      let sig = r.read_rest()
      guard sig.length() == 64 else {
        @error.fail(
          "packet: an Ed25519 signature must be 64 octets, got \{sig.length()}",
        )
      }
      params.push(sig)
    }
    _ =>
      // Unknown material is carried raw so the packet still round-trips.
      params.push(r.read_rest())
  }
  SignaturePacket::{
    version,
    sig_type,
    pub_algo,
    hash_algo,
    hashed_subpackets: hashed,
    unhashed_subpackets: unhashed,
    raw_hashed_area: raw_hashed,
    fast_hash: fast,
    salt,
    sig_params: params,
  }
}

///|
pub fn SignaturePacket::to_body(self : SignaturePacket) -> Bytes {
  let w = Writer::new()
  let v6 = self.version >= 6
  w.u8(self.version)
  w.u8(self.sig_type.number())
  w.u8(self.pub_algo.number())
  w.u8(self.hash_algo)
  let hashed = match self.raw_hashed_area {
    b if b.length() > 0 => b
    _ => serialize_subpackets(self.hashed_subpackets)
  }
  if v6 {
    w.u32(hashed.length())
  } else {
    w.u16(hashed.length())
  }
  w.bytes(hashed.exact_view())
  let unhashed = serialize_subpackets(self.unhashed_subpackets)
  if v6 {
    w.u32(unhashed.length())
  } else {
    w.u16(unhashed.length())
  }
  w.bytes(unhashed.exact_view())
  w.u16(self.fast_hash)
  if v6 {
    w.u8(self.salt.length())
    w.bytes(self.salt.exact_view())
  }
  for p in self.sig_params {
    match self.pub_algo {
      Ed25519 => w.bytes(p.exact_view())
      _ => w.mpi(p.exact_view())
    }
  }
  w.to_bytes()
}

///|
/// The signature subpacket types this implementation recognizes: exactly
/// the set with constants defined above. RFC 9580 §5.2.3.6: an evaluator
/// that encounters an unknown subpacket marked critical SHOULD consider
/// the whole signature in error (the bit is honored in both the hashed
/// and the unhashed area), so verifiers consult this set via
/// `SignaturePacket::has_unknown_critical_subpacket`.
pub fn is_known_sigsub_type(t : Int32) -> Bool {
  t == SIGSUB_CREATION_TIME ||
  t == SIGSUB_SIG_EXPIRATION_TIME ||
  t == SIGSUB_EXPORTABLE_CERTIFICATION ||
  t == SIGSUB_TRUST_SIGNATURE ||
  t == SIGSUB_REGULAR_EXPRESSION ||
  t == SIGSUB_REVOCABLE ||
  t == SIGSUB_KEY_EXPIRATION_TIME ||
  t == SIGSUB_PREFERRED_SYM_ALGOS ||
  t == SIGSUB_ISSUER_KEY_ID ||
  t == SIGSUB_NOTATION_DATA ||
  t == SIGSUB_PREFERRED_HASH_ALGOS ||
  t == SIGSUB_PREFERRED_COMP_ALGOS ||
  t == SIGSUB_KEY_SERVER_PREFERENCES ||
  t == SIGSUB_PREFERRED_KEY_SERVER ||
  t == SIGSUB_PRIMARY_USER_ID ||
  t == SIGSUB_POLICY_URI ||
  t == SIGSUB_KEY_FLAGS ||
  t == SIGSUB_SIGNER_USER_ID ||
  t == SIGSUB_REVOCATION_REASON ||
  t == SIGSUB_FEATURES ||
  t == SIGSUB_SIGNATURE_TARGET ||
  t == SIGSUB_EMBEDDED_SIGNATURE ||
  t == SIGSUB_ISSUER_FINGERPRINT ||
  t == SIGSUB_PREFERRED_AEAD
}

///|
/// True when any hashed or unhashed subpacket is marked critical but not
/// in the recognized set — the signature MUST be treated as in error.
pub fn SignaturePacket::has_unknown_critical_subpacket(
  self : SignaturePacket,
) -> Bool {
  let unknown_critical = fn(sp : SigSubpacket) -> Bool {
    sp.critical && !is_known_sigsub_type(sp.sub_type)
  }
  for sp in self.hashed_subpackets {
    if unknown_critical(sp) {
      return true
    }
  }
  for sp in self.unhashed_subpackets {
    if unknown_critical(sp) {
      return true
    }
  }
  false
}

///|
/// First subpacket body of the given type from `subs`.
pub fn find_subpacket(subs : Array[SigSubpacket], t : Int32) -> Bytes? {
  for sp in subs {
    if sp.sub_type == t {
      return Some(sp.body)
    }
  }
  None
}

///|
/// Signature creation time from the hashed subpackets (seconds since epoch).
pub fn SignaturePacket::creation_time(self : SignaturePacket) -> Int32? {
  match find_subpacket(self.hashed_subpackets, SIGSUB_CREATION_TIME) {
    Some(b) =>
      if b.length() == 4 {
        Some(
          (b[0].to_int() << 24) |
          (b[1].to_int() << 16) |
          (b[2].to_int() << 8) |
          b[3].to_int(),
        )
      } else {
        None
      }
    None => None
  }
}

///|
/// Issuer key id, preferring the type 16 (Issuer Key ID) subpacket; falls
/// back to the issuer-fingerprint subpacket's trailing key id.
pub fn SignaturePacket::issuer_key_id(self : SignaturePacket) -> Bytes? {
  match find_subpacket(self.hashed_subpackets, SIGSUB_ISSUER_KEY_ID) {
    Some(b) => return Some(b)
    None => ()
  }
  match find_subpacket(self.unhashed_subpackets, SIGSUB_ISSUER_KEY_ID) {
    Some(b) => return Some(b)
    None => ()
  }
  let fp = match
    find_subpacket(self.hashed_subpackets, SIGSUB_ISSUER_FINGERPRINT) {
    Some(f) => f
    None =>
      match
        find_subpacket(self.unhashed_subpackets, SIGSUB_ISSUER_FINGERPRINT) {
        Some(f) => f
        None => return None
      }
  }
  // Body: version octet then fingerprint (20 octets for v4, 32 for v6).
  if fp.length() >= 9 {
    let fp_version = fp[0].to_int()
    if fp_version >= 5 {
      // v6: the key id is the HIGH-order 64 bits of the fingerprint.
      Some(fp[1:9].to_owned())
    } else {
      Some(fp[fp.length() - 8:fp.length()].to_owned())
    }
  } else {
    None
  }
}

///|
/// Key flags octets from the hashed subpackets.
pub fn SignaturePacket::key_flags(self : SignaturePacket) -> Bytes? {
  match find_subpacket(self.hashed_subpackets, SIGSUB_KEY_FLAGS) {
    Some(b) => Some(b)
    None => None
  }
}

///|
/// 4-octet big-endian seconds value of a lifetime subpacket body, or 0
/// when absent/malformed (0 = "never expires", so callers need not
/// distinguish the two).
fn lifetime_secs(subs : Array[SigSubpacket], sub_type : Int32) -> Int32 {
  match find_subpacket(subs, sub_type) {
    Some(b) =>
      if b.length() == 4 {
        (b[0].to_int() << 24) |
        (b[1].to_int() << 16) |
        (b[2].to_int() << 8) |
        b[3].to_int()
      } else {
        0
      }
    None => 0
  }
}

///|
/// The Key Expiration Time subpacket (RFC 9580 §5.2.3.6): seconds after
/// the associated key's creation time; 0 = the key never expires.
pub fn SignaturePacket::key_expiration_time(self : SignaturePacket) -> Int32 {
  lifetime_secs(self.hashed_subpackets, SIGSUB_KEY_EXPIRATION_TIME)
}

///|
/// The Signature Expiration Time subpacket (RFC 9580 §5.2.3.10): seconds
/// after the signature's creation time; 0 = the signature never expires.
pub fn SignaturePacket::sig_expiration_time(self : SignaturePacket) -> Int32 {
  lifetime_secs(self.hashed_subpackets, SIGSUB_SIG_EXPIRATION_TIME)
}

///|
/// The Features flags octet (RFC 9580 §5.2.3.32), when present.
pub fn SignaturePacket::features(self : SignaturePacket) -> Bytes? {
  find_subpacket(self.hashed_subpackets, SIGSUB_FEATURES)
}

///|
/// True when the Features subpacket advertises the v2 SEIPD flag
/// (RFC 9580 §5.2.3.32/§13.7).
pub fn SignaturePacket::supports_seipd_v2(self : SignaturePacket) -> Bool {
  match self.features() {
    Some(f) => f.length() > 0 && (f[0].to_int() & FEATURE_SEIPD_V2) != 0
    None => false
  }
}

///|
/// The Preferred AEAD Ciphersuites list as (cipher, AEAD) pairs, most
/// preferred first (RFC 9580 §5.2.3.15). A trailing odd octet is ignored.
pub fn SignaturePacket::preferred_aead(
  self : SignaturePacket,
) -> Array[(Int32, Int32)]? {
  match find_subpacket(self.hashed_subpackets, SIGSUB_PREFERRED_AEAD) {
    Some(b) => {
      let out : Array[(Int32, Int32)] = []
      let mut i = 0
      while i + 1 < b.length() {
        out.push((b[i].to_int(), b[i + 1].to_int()))
        i = i + 2
      }
      Some(out)
    }
    None => None
  }
}

///|
/// The hash prefix: version | type | pubalgo | hashalgo |
/// hashed_len | hashed area (raw wire bytes when available). Version 6
/// uses a 4-octet subpacket-area length (RFC 9580 §5.2.3).
pub fn SignaturePacket::hash_prefix(self : SignaturePacket) -> Bytes {
  let w = Writer::new()
  let v6 = self.version >= 6
  w.u8(self.version)
  w.u8(self.sig_type.number())
  w.u8(self.pub_algo.number())
  w.u8(self.hash_algo)
  let hashed = match self.raw_hashed_area {
    b if b.length() > 0 => b
    _ => serialize_subpackets(self.hashed_subpackets)
  }
  if v6 {
    w.u32(hashed.length())
  } else {
    w.u16(hashed.length())
  }
  w.bytes(hashed.exact_view())
  w.to_bytes()
}

///|
/// The final trailer: 0x04|0x06 0xFF and the 4-octet length of everything
/// hashed before it (RFC 9580 §5.2.4).
pub fn signature_trailer(version : Int32, hashed_body_len : Int32) -> Bytes {
  let w = Writer::new()
  w.u8(version)
  w.u8(255)
  w.u32(hashed_body_len)
  w.to_bytes()
}

///|
/// A one-pass signature packet body: version 3 (RFC 9580 §5.4, what a v4
/// key emits) and version 6 (the same fields plus a hash salt and the
/// 32-octet signing-key fingerprint, and with the 8-octet key id gone).
pub(all) struct OnePassSigPacket {
  version : Int32
  sig_type : SigType
  hash_algo : Int32
  pub_algo : PubKeyAlgo
  /// v3 only: the 8-octet signing key id.
  key_id : Bytes
  /// v6 only: the hash salt (its size comes from the hash algorithm).
  salt : Bytes
  /// v6 only: the 32-octet signing key fingerprint.
  fingerprint : Bytes
  nested : Bool
} derive(Debug)

///|
pub fn OnePassSigPacket::version(self : OnePassSigPacket) -> Int32 {
  self.version
}

///|
pub fn OnePassSigPacket::key_id(self : OnePassSigPacket) -> Bytes {
  self.key_id
}

///|
pub fn OnePassSigPacket::salt(self : OnePassSigPacket) -> Bytes {
  self.salt
}

///|
pub fn OnePassSigPacket::fingerprint(self : OnePassSigPacket) -> Bytes {
  self.fingerprint
}

///|
pub fn OnePassSigPacket::parse(body : BytesView) -> OnePassSigPacket raise {
  let r = Reader::new(body)
  let version = r.read_u8()
  guard version == 3 || version == 6 else {
    @error.fail("packet: unsupported one-pass signature version \{version}")
  }
  let sig_type = sig_type_of_number(r.read_u8())
  let hash_algo = r.read_u8()
  let pub_algo = pub_key_algo_of_number(r.read_u8())
  let mut key_id = Bytes::new(0)
  let mut salt = Bytes::new(0)
  let mut fingerprint = Bytes::new(0)
  if version == 6 {
    let salt_len = r.read_u8()
    // RFC 9580 §5.4: the size MUST match the hash algorithm's Table 23
    // value (a mismatch is malformed, §5.2.5) — same rule the Signature
    // packet enforces when parsing.
    guard salt_len == salt_size_of_hash(hash_algo) else {
      @error.fail(
        "packet: one-pass signature salt size \{salt_len} does not match hash algorithm \{hash_algo}",
      )
    }
    salt = r.read_bytes(salt_len)
    fingerprint = r.read_bytes(32)
  } else {
    key_id = r.read_bytes(8)
  }
  let nested = r.read_u8() != 0
  OnePassSigPacket::{
    version,
    sig_type,
    hash_algo,
    pub_algo,
    key_id,
    salt,
    fingerprint,
    nested,
  }
}

///|
pub fn OnePassSigPacket::to_body(self : OnePassSigPacket) -> Bytes {
  let w = Writer::new()
  w.u8(self.version)
  w.u8(self.sig_type.number())
  w.u8(self.hash_algo)
  w.u8(self.pub_algo.number())
  if self.version == 6 {
    w.u8(self.salt.length())
    w.bytes(self.salt.exact_view())
    w.bytes(self.fingerprint.exact_view())
  } else {
    w.bytes(self.key_id.exact_view())
  }
  w.u8(if self.nested { 1 } else { 0 })
  w.to_bytes()
}

///|
pub extend OnePassSigPacket with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend SigSubpacket with @moonbitlang/core/debug.Debug::{to_repr}

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

///|
pub extend SigType with @moonbitlang/core/debug.Debug::{to_repr}

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

///|
pub extend SignaturePacket with @moonbitlang/core/debug.Debug::{to_repr}