///|
/// Result of validating every manifest reference and Segment checksum.
pub(all) struct IndexCheckReport {
  generation : Int
  segment_count : Int
  live_document_count : Int
  orphan_files : ReadOnlyArray[String]
  temporary_files : ReadOnlyArray[String]
} derive(Eq, @debug.Debug)

///|
/// Validates the current manifest, every referenced Segment, tombstones,
/// checksums, and cross-Segment Schema compatibility. Orphans and temporary
/// files are reported without making the otherwise valid snapshot unreadable.
pub fn[D : DirectoryV2] check_index(
  directory : D,
) -> IndexCheckReport raise PersistenceError {
  guard Directory::exists(directory, manifest_file_name) else {
    raise PersistenceError::NotFound(manifest_file_name)
  }
  let manifest = decode_manifest(Directory::read(directory, manifest_file_name))
  let mut live_document_count = 0
  let mut schema : Schema? = None
  for index in 0.. FaultInjectingDirectory[D] {
  guard fail_at_operation > 0 else { abort("fault operation must be positive") }
  { inner, fail_at_operation, operation_count: 0 }
}

///|
fn[D] FaultInjectingDirectory::before_mutation(
  self : FaultInjectingDirectory[D],
) -> Unit raise PersistenceError {
  self.operation_count += 1
  if self.operation_count == self.fail_at_operation {
    raise PersistenceError::Io(
      "injected directory failure at operation \{self.operation_count}",
    )
  }
}

///|
pub fn[D] FaultInjectingDirectory::operation_count(
  self : FaultInjectingDirectory[D],
) -> Int {
  self.operation_count
}

///|
pub impl[D : Directory] Directory for FaultInjectingDirectory[D] with fn write(
  self,
  name,
  bytes,
) {
  self.before_mutation()
  Directory::write(self.inner, name, bytes)
}

///|
pub impl[D : Directory] Directory for FaultInjectingDirectory[D] with fn read(
  self,
  name,
) {
  Directory::read(self.inner, name)
}

///|
pub impl[D : Directory] Directory for FaultInjectingDirectory[D] with fn exists(
  self,
  name,
) {
  Directory::exists(self.inner, name)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn write_atomic(
  self,
  name,
  bytes,
) {
  self.before_mutation()
  DirectoryV2::write_atomic(self.inner, name, bytes)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn sync(
  self,
  name,
) {
  self.before_mutation()
  DirectoryV2::sync(self.inner, name)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn remove(
  self,
  name,
) {
  self.before_mutation()
  DirectoryV2::remove(self.inner, name)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn list(
  self,
) {
  DirectoryV2::list(self.inner)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn try_acquire_lock(
  self,
  name,
) {
  self.before_mutation()
  DirectoryV2::try_acquire_lock(self.inner, name)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn release_lock(
  self,
  name,
  token,
) {
  self.before_mutation()
  DirectoryV2::release_lock(self.inner, name, token)
}

///|
pub impl[D : DirectoryV2] DirectoryV2 for FaultInjectingDirectory[D] with fn capabilities(
  self,
) {
  DirectoryV2::capabilities(self.inner)
}