use std::hash::{Hash, Hasher};
use std::sync::Arc;
use rustc_hash::FxHasher;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PredicateSymbol(Arc<str>);
impl PredicateSymbol {
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
Self(Arc::from(value.into()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug)]
pub struct VariableSymbol {
name: Arc<str>,
cached_hash: u64,
}
impl VariableSymbol {
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
let name: Arc<str> = Arc::from(value.into());
let cached_hash = {
let mut h = FxHasher::default();
h.write(name.as_bytes());
h.finish()
};
Self { name, cached_hash }
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.name
}
}
impl PartialEq for VariableSymbol {
fn eq(&self, other: &Self) -> bool {
self.cached_hash == other.cached_hash && self.name == other.name
}
}
impl Eq for VariableSymbol {}
impl Hash for VariableSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.cached_hash);
}
}
impl PartialOrd for VariableSymbol {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VariableSymbol {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.name.cmp(&other.name)
}
}
#[derive(Clone, Debug)]
pub struct ConstantSymbol {
value: Arc<str>,
cached_hash: u64,
}
impl ConstantSymbol {
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
let value: Arc<str> = Arc::from(value.into());
let cached_hash = {
let mut h = FxHasher::default();
h.write(value.as_bytes());
h.finish()
};
Self { value, cached_hash }
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.value
}
}
impl PartialEq for ConstantSymbol {
fn eq(&self, other: &Self) -> bool {
self.cached_hash == other.cached_hash && self.value == other.value
}
}
impl Eq for ConstantSymbol {}
impl Hash for ConstantSymbol {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.cached_hash);
}
}
impl PartialOrd for ConstantSymbol {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ConstantSymbol {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}
#[cfg(test)]
mod tests {
use std::collections::{BTreeSet, HashMap};
use super::*;
#[test]
fn equal_constants_hash_and_compare_equal() {
let a = ConstantSymbol::new("http://ex.org/x");
let b = ConstantSymbol::new(String::from("http://ex.org/x"));
assert_eq!(a, b);
let mut map: HashMap<ConstantSymbol, u32> = HashMap::new();
map.insert(a, 7);
assert_eq!(map.get(&b), Some(&7), "equal values must hash to one slot");
}
#[test]
fn distinct_constants_are_unequal() {
let a = ConstantSymbol::new("http://ex.org/x");
let b = ConstantSymbol::new("http://ex.org/y");
assert_ne!(a, b);
}
#[test]
fn ordering_is_content_based() {
let set: BTreeSet<ConstantSymbol> = ["c", "a", "b"]
.into_iter()
.map(ConstantSymbol::new)
.collect();
let ordered: Vec<&str> = set.iter().map(ConstantSymbol::as_str).collect();
assert_eq!(ordered, ["a", "b", "c"]);
}
}