Closes the last open item in #25. `cargo clippy -W missing_errors_doc -W missing_panics_doc -W must_use_candidate -W doc_markdown` went from 56 warnings to zero. The 13 hand-written sections name the actual variants each function returns rather than gesturing at "an error". Establishing that meant reading the error paths — `Game::ranked` alone returns four distinct variants, and `record_draw` can hit TieWithoutDrawProbability where `record_winner` provably cannot, since a two-team decisive outcome has nothing to tie. Documenting those as interchangeable would have been worse than leaving them undocumented, because a reader would trust it. Two existing doc comments already described panics in prose but not under a `# Panics` heading, so neither rustdoc nor clippy surfaced them: `Outcome::winner` and `EventBuilder::weights`. Both now carry the heading, and `Outcome::winner` gained the note that it ties every loser, so `n >= 3` needs a positive p_draw — the crate's easiest error to hit by accident. The 43 mechanical fixes (31 `#[must_use]` on pure accessors, 11 missing backticks) were applied with `cargo clippy --fix`. `#[must_use]` on Gaussian's arithmetic and on `posteriors()` matters: discarding those results is always a bug, and until now nothing said so. Also documented why `[profile.release] debug = true` exists — cargo-flamegraph needs the symbols, and library profile settings are ignored downstream, so it reads as an oversight without the note. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T5SYDExxL4vZgvunrcNSMc
86 lines
1.9 KiB
Rust
86 lines
1.9 KiB
Rust
use std::{
|
|
borrow::{Borrow, ToOwned},
|
|
collections::HashMap,
|
|
hash::Hash,
|
|
};
|
|
|
|
use crate::Index;
|
|
|
|
/// Maps user keys to internal `Index` handles.
|
|
///
|
|
/// Renamed from the former `IndexMap` to avoid colliding with the `indexmap`
|
|
/// crate. Power users can promote `&K` to `Index` via `get_or_create` and
|
|
/// skip the lookup on subsequent hot-path calls.
|
|
#[derive(Debug)]
|
|
pub struct KeyTable<K> {
|
|
forward: HashMap<K, Index>,
|
|
/// Reverse mapping, indexed by `Index.0`.
|
|
///
|
|
/// Indices are handed out densely and sequentially, so position *is* the
|
|
/// index and `key()` is a lookup rather than a scan over every entry.
|
|
reverse: Vec<K>,
|
|
}
|
|
|
|
impl<K> KeyTable<K>
|
|
where
|
|
K: Eq + Hash + Clone,
|
|
{
|
|
#[must_use]
|
|
pub fn new() -> Self {
|
|
Self {
|
|
forward: HashMap::new(),
|
|
reverse: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn get<Q: ?Sized + Hash + Eq>(&self, k: &Q) -> Option<Index>
|
|
where
|
|
K: Borrow<Q>,
|
|
{
|
|
self.forward.get(k).cloned()
|
|
}
|
|
|
|
pub fn get_or_create<Q: ?Sized + Hash + Eq + ToOwned<Owned = K>>(&mut self, k: &Q) -> Index
|
|
where
|
|
K: Borrow<Q>,
|
|
{
|
|
if let Some(idx) = self.forward.get(k) {
|
|
*idx
|
|
} else {
|
|
let idx = Index::from(self.reverse.len());
|
|
let owned = k.to_owned();
|
|
self.reverse.push(owned.clone());
|
|
self.forward.insert(owned, idx);
|
|
idx
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn key(&self, idx: Index) -> Option<&K> {
|
|
self.reverse.get(idx.0)
|
|
}
|
|
|
|
pub fn keys(&self) -> impl Iterator<Item = &K> {
|
|
self.forward.keys()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn len(&self) -> usize {
|
|
self.reverse.len()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn is_empty(&self) -> bool {
|
|
self.reverse.is_empty()
|
|
}
|
|
}
|
|
|
|
impl<K> Default for KeyTable<K>
|
|
where
|
|
K: Eq + Hash + Clone,
|
|
{
|
|
fn default() -> Self {
|
|
KeyTable::new()
|
|
}
|
|
}
|