///|
/// Places one key through a selected built-in algorithm.
pub fn place_key(
  nodes : Array[ShardNode],
  key : String,
  replicas : Int,
  algorithm? : PlacementAlgorithm = TopologyAware,
  seed? : UInt = 2166136261U,
  salt? : String = "moonshardkit",
) -> KeyPlacement {
  let owners = match algorithm {
    ConsistentRing =>
      ConsistentHashRing::build(nodes, seed~).owners(key, replicas)
    Rendezvous => rendezvous_owners(nodes, key, replicas, salt~)
    TopologyAware => return place_replicas(nodes, key, replicas, salt~)
  }
  let eligible = nodes.fold(init=0, fn(count, node) {
    if node.is_eligible() {
      count + 1
    } else {
      count
    }
  })
  let expected = if replicas < 0 {
    0
  } else if replicas < eligible {
    replicas
  } else {
    eligible
  }
  let complete = replicas <= eligible && owners.length() == expected
  {
    key,
    owners,
    complete,
    message: if complete {
      "placement complete"
    } else {
      "insufficient eligible nodes"
    },
  }
}

///|
/// Places a deterministic key batch with one algorithm.
pub fn place_keys(
  nodes : Array[ShardNode],
  keys : Array[String],
  replicas : Int,
  algorithm? : PlacementAlgorithm = TopologyAware,
  seed? : UInt = 2166136261U,
  salt? : String = "moonshardkit",
) -> Array[KeyPlacement] {
  keys.map(fn(key) { place_key(nodes, key, replicas, algorithm~, seed~, salt~) })
}