///|
pub(all) struct DirectoryEntry {
  fileid : UInt64
  name : Bytes
  cookie : UInt64
  attributes : Attributes?
  handle : Handle?
}

///|
pub(all) struct DirectoryPage {
  entries : Array[DirectoryEntry]
  verifier : Bytes
  eof : Bool
}

///|
pub async fn Client::read_directory(
  self : Client,
  directory : Handle,
  cookie? : UInt64 = 0UL,
  verifier? : Bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00",
  plus? : Bool = true,
) -> DirectoryPage {
  if verifier.length() != 8 {
    raise InvalidArgument("directory verifier must have eight bytes")
  }
  let w = @xdr.Writer::new()
  put_handle(w, directory)
  w.write_uhyper(cookie)
  w.write_fixed(verifier.to_array())
  let budget = (self.options.max_record - 4096).reinterpret_as_uint()
  w.write_uint(if plus { budget / 2U } else { budget })
  if plus {
    w.write_uint(budget)
  }
  let r = self.invoke(if plus { 17U } else { 16U }, w)
  ignore(post_attributes(r))
  let verifier = Bytes::from_array(r.read_fixed(8))
  let entries : Array[DirectoryEntry] = []
  while r.read_bool() {
    if entries.length() >= self.options.max_entries {
      raise ProtocolError("directory page exceeds entry budget")
    }
    let fileid = r.read_uhyper()
    let name = Bytes::from_array(r.read_opaque(bound=255U))
    if name.length() == 0 {
      raise ProtocolError("empty directory entry name")
    }
    let cookie = r.read_uhyper()
    let attributes = if plus { post_attributes(r) } else { None }
    let handle = if plus && r.read_bool() { Some(get_handle(r)) } else { None }
    entries.push({ fileid, name, cookie, attributes, handle, })
  }
  let eof = r.read_bool()
  r.finish()
  { entries, verifier, eof, }
}

///|
/// A bounded collection for a static directory, not a concurrent directory snapshot.
/// BAD_COOKIE is surfaced to the caller rather than silently restarting traversal.
pub async fn Client::list_dir(
  self : Client,
  path : String,
  plus? : Bool = true,
) -> Array[DirectoryEntry] {
  let cursor = self.open_directory(path, plus~)
  defer cursor.close()
  let result : Array[DirectoryEntry] = []
  while cursor.next() is Some(entry) {
    result.push(entry)
  }
  result
}