use crate::{
db::executor::group::{StableHash, stable_hash_value},
error::InternalError,
value::{MapValueError, Value},
};
use std::{
collections::HashSet,
fmt,
hash::{Hash, Hasher},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db::executor) enum KeyCanonicalError {
InvalidMapValue(MapValueError),
HashingFailed { reason: String },
}
impl KeyCanonicalError {
fn invalid_map_value(err: &MapValueError) -> InternalError {
InternalError::executor_invariant(format!(
"group key canonicalization rejected map value: {err}"
))
}
pub(in crate::db::executor) fn into_internal_error(self) -> InternalError {
match self {
Self::InvalidMapValue(err) => Self::invalid_map_value(&err),
Self::HashingFailed { reason } => {
InternalError::executor_internal(format!("group key hashing failed: {reason}"))
}
}
}
}
impl fmt::Display for KeyCanonicalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidMapValue(err) => write!(f, "{err}"),
Self::HashingFailed { reason } => write!(f, "{reason}"),
}
}
}
impl std::error::Error for KeyCanonicalError {}
#[derive(Clone, Debug, Eq, PartialEq)]
struct CanonicalValue(Value);
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db::executor) struct GroupKey {
raw: CanonicalValue,
hash: StableHash,
}
impl Hash for GroupKey {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash);
}
}
#[cfg(test)]
#[must_use]
fn canonical_group_key_equals(left: &GroupKey, right: &GroupKey) -> bool {
left == right
}
impl GroupKey {
fn from_raw(raw: Value) -> Result<Self, KeyCanonicalError> {
let hash = stable_hash_value(&raw).map_err(|err| KeyCanonicalError::HashingFailed {
reason: err.display_with_class(),
})?;
Ok(Self::from_raw_with_hash(raw, hash))
}
const fn from_raw_with_hash(raw: Value, hash: StableHash) -> Self {
Self {
raw: CanonicalValue(raw),
hash,
}
}
#[must_use]
pub(in crate::db::executor) const fn hash(&self) -> StableHash {
self.hash
}
#[must_use]
pub(in crate::db::executor) const fn canonical_value(&self) -> &Value {
&self.raw.0
}
pub(in crate::db::executor) fn into_canonical_value(self) -> Value {
self.raw.0
}
pub(in crate::db::executor) fn from_group_values(
group_values: Vec<Value>,
) -> Result<Self, KeyCanonicalError> {
let canonical = canonicalize_owned_value(Value::List(group_values))?;
Self::from_raw(canonical)
}
pub(in crate::db::executor) fn from_group_values_with_hash(
group_values: Vec<Value>,
hash: StableHash,
) -> Result<Self, KeyCanonicalError> {
let canonical = canonicalize_owned_value(Value::List(group_values))?;
Ok(Self::from_raw_with_hash(canonical, hash))
}
pub(in crate::db::executor) fn from_single_group_value(
group_value: Value,
) -> Result<Self, KeyCanonicalError> {
let canonical_group_value = canonicalize_owned_value(group_value)?;
Self::from_raw(Value::List(vec![canonical_group_value]))
}
pub(in crate::db::executor) fn from_single_group_value_with_hash(
group_value: Value,
hash: StableHash,
) -> Result<Self, KeyCanonicalError> {
let canonical_group_value = canonicalize_owned_value(group_value)?;
Ok(Self::from_raw_with_hash(
Value::List(vec![canonical_group_value]),
hash,
))
}
pub(in crate::db::executor) fn from_single_canonical_group_value(
group_value: Value,
) -> Result<Self, KeyCanonicalError> {
Self::from_raw(Value::List(vec![group_value]))
}
pub(in crate::db::executor) fn from_single_canonical_group_value_with_hash(
group_value: Value,
hash: StableHash,
) -> Self {
Self::from_raw_with_hash(Value::List(vec![group_value]), hash)
}
#[cfg(test)]
#[must_use]
const fn raw(&self) -> &Value {
&self.raw.0
}
}
pub(in crate::db::executor) trait CanonicalKey {
fn canonical_key(&self) -> Result<GroupKey, KeyCanonicalError>;
}
impl CanonicalKey for Value {
fn canonical_key(&self) -> Result<GroupKey, KeyCanonicalError> {
let canonical = canonicalize_value(self)?;
GroupKey::from_raw(canonical)
}
}
impl CanonicalKey for &Value {
fn canonical_key(&self) -> Result<GroupKey, KeyCanonicalError> {
(*self).canonical_key()
}
}
#[derive(Debug)]
pub(in crate::db::executor) struct GroupKeySet {
keys: HashSet<GroupKey>,
}
impl GroupKeySet {
#[must_use]
pub(in crate::db::executor) fn new() -> Self {
Self {
keys: HashSet::new(),
}
}
#[must_use]
pub(in crate::db::executor) fn contains_key(&self, key: &GroupKey) -> bool {
self.keys.contains(key)
}
#[must_use]
pub(in crate::db::executor) fn len(&self) -> usize {
self.keys.len()
}
pub(in crate::db::executor) fn insert_key(&mut self, key: GroupKey) -> bool {
self.keys.insert(key)
}
pub(in crate::db::executor) fn insert_value(
&mut self,
value: &Value,
) -> Result<bool, KeyCanonicalError> {
let key = value.canonical_key()?;
Ok(self.insert_key(key))
}
}
impl Default for GroupKeySet {
fn default() -> Self {
Self::new()
}
}
fn canonicalize_value(value: &Value) -> Result<Value, KeyCanonicalError> {
match value {
Value::Decimal(decimal) => Ok(Value::Decimal(decimal.normalize())),
Value::List(items) => items
.iter()
.map(canonicalize_value)
.collect::<Result<Vec<_>, _>>()
.map(Value::List),
Value::Map(entries) => canonicalize_map_entries(entries),
_ => Ok(value.clone()),
}
}
fn canonicalize_map_entries(entries: &[(Value, Value)]) -> Result<Value, KeyCanonicalError> {
normalize_canonical_map_entries(
entries
.iter()
.map(|(key, value)| Ok((canonicalize_value(key)?, canonicalize_value(value)?))),
)
}
fn canonicalize_owned_value(value: Value) -> Result<Value, KeyCanonicalError> {
match value {
Value::Decimal(decimal) => Ok(Value::Decimal(decimal.normalize())),
Value::List(items) => items
.into_iter()
.map(canonicalize_owned_value)
.collect::<Result<Vec<_>, _>>()
.map(Value::List),
Value::Map(entries) => canonicalize_owned_map_entries(entries),
value => Ok(value),
}
}
fn canonicalize_owned_map_entries(
entries: Vec<(Value, Value)>,
) -> Result<Value, KeyCanonicalError> {
normalize_canonical_map_entries(entries.into_iter().map(|(key, value)| {
Ok((
canonicalize_owned_value(key)?,
canonicalize_owned_value(value)?,
))
}))
}
fn normalize_canonical_map_entries(
entries: impl IntoIterator<Item = Result<(Value, Value), KeyCanonicalError>>,
) -> Result<Value, KeyCanonicalError> {
let canonical_entries = entries.into_iter().collect::<Result<Vec<_>, _>>()?;
let normalized = Value::normalize_map_entries(canonical_entries)
.map_err(KeyCanonicalError::InvalidMapValue)?;
Ok(Value::Map(normalized))
}
#[cfg(test)]
mod tests;