///|
fn snapshot_record_order(
  left : IndexSnapshotRecord,
  right : IndexSnapshotRecord,
) -> Int {
  if left.record.id < right.record.id {
    -1
  } else if left.record.id > right.record.id {
    1
  } else {
    0
  }
}

///|
/// Exports a canonical version-one snapshot sorted by record identifier.
pub fn NameIndex::export_snapshot(self : NameIndex) -> IndexSnapshot {
  let snapshot_records : Array[IndexSnapshotRecord] = []
  for entry in self.records.to_array() {
    let record = entry.1
    let normalized = match self.normalized.get(record.id) {
      Some(value) => value
      None => ""
    }
    let bucket_keys = match self.record_bucket_keys.get(record.id) {
      Some(value) => value.copy()
      None => []
    }
    snapshot_records.push({ record, normalized, bucket_keys })
  }
  snapshot_records.sort_by(snapshot_record_order)
  {
    version: 1,
    config: self.config,
    blocking_algorithms: self.blocking_algorithms.copy(),
    records: snapshot_records,
  }
}

///|
fn snapshot_contains_id(
  records : Array[IndexSnapshotRecord],
  before : Int,
  id : String,
) -> Bool {
  for index = 0; index < before; index = index + 1 {
    if records[index].record.id == id {
      return true
    }
  }
  false
}

///|
/// Rebuilds an index and verifies every supplied derived field by recomputation.
pub fn NameIndex::from_snapshot(
  snapshot : IndexSnapshot,
) -> Result[NameIndex, IndexError] {
  if snapshot.version != 1 {
    return Err(UnsupportedSnapshotVersion(snapshot.version))
  }
  let index = match
    NameIndex::new(snapshot.config, snapshot.blocking_algorithms) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  for record_index = 0
      record_index < snapshot.records.length()
      record_index = record_index + 1 {
    let source = snapshot.records[record_index]
    if snapshot_contains_id(snapshot.records, record_index, source.record.id) {
      return Err(DuplicateSnapshotRecord(source.record.id))
    }
    match index.insert(source.record.id, source.record.name) {
      Err(error) => return Err(error)
      Ok(_) => ()
    }
    let recomputed_normalized = match index.normalized.get(source.record.id) {
      Some(value) => value
      None => return Err(InconsistentSnapshotRecord(source.record.id))
    }
    let recomputed_keys = match index.record_bucket_keys.get(source.record.id) {
      Some(value) => value
      None => return Err(InconsistentSnapshotRecord(source.record.id))
    }
    if recomputed_normalized != source.normalized ||
      recomputed_keys != source.bucket_keys {
      return Err(InconsistentSnapshotRecord(source.record.id))
    }
  }
  Ok(index)
}