///|
priv struct CarrierMember {
  name : String
  data : Bytes
}

///|
fn carrier_budget(
  length : Int32,
  maximum : Int32,
) -> Unit raise @ledger.InputError {
  if maximum <= 0 || maximum > 268435456 || length > maximum {
    raise bad(
      "TransportLimit", "$", "Transport budget must be 1..268435456 bytes and contain the whole archive",
    )
  }
}

///|
fn carrier_text(entry : CarrierMember) -> String raise @ledger.InputError {
  @utf8.decode(entry.data) catch {
    _ =>
      raise bad(
        "InvalidUtf8",
        entry.name,
        "Archive entry must contain exact valid UTF-8",
      )
  }
}

///|
fn carrier_pack(
  evidence : Evidence,
  limits : @ledger.Limits,
) -> Array[CarrierMember] raise @ledger.InputError {
  let (index, files) = evidence.detach(limits~)
  [
    { name: "index.json", data: @utf8.encode(index.encode(limits~)), },
    ..files.map(file => { name: file.name, data: @utf8.encode(file.xml), }),
  ]
}

///|
fn carrier_restore(
  entries : Array[CarrierMember],
  limits : @ledger.Limits,
) -> Evidence raise @ledger.InputError {
  guard entries.iter().find_first(entry => entry.name == "index.json")
    is Some(index_entry) else {
    raise bad(
      "MissingPackFile", "index.json", "Transport requires a file-pack index",
    )
  }
  let index = decode_pack_index(carrier_text(index_entry), limits~)
  let files = entries
    .filter(entry => entry.name != "index.json")
    .map(entry => { name: entry.name, xml: carrier_text(entry), })
  index.restore(files[:], limits~)
}

///|
fn carrier_sizes(
  size : Int32,
  total : Int64,
  limits : @ledger.Limits,
) -> Unit raise @ledger.InputError {
  if size.to_int64() > 4L * limits.max_source_units.to_int64() ||
    total >
    4L *
    (
      limits.max_total_source_units.to_int64() +
      limits.max_source_units.to_int64()
    ) {
    raise bad(
      "SourceLimit", "$", "Expanded archive entries exceed the source byte budget",
    )
  }
}