use std::hash::{
Hash,
Hasher,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectionKey {
type_path: String,
trait_path: Option<String>,
assoc_name: String,
signature_hash: Option<u64>,
}
impl ProjectionKey {
pub fn new(
type_path: impl Into<String>,
assoc_name: impl Into<String>,
) -> Self {
Self {
type_path: type_path.into(),
trait_path: None,
assoc_name: assoc_name.into(),
signature_hash: None,
}
}
pub fn scoped(
type_path: impl Into<String>,
trait_path: impl Into<String>,
assoc_name: impl Into<String>,
) -> Self {
Self {
type_path: type_path.into(),
trait_path: Some(trait_path.into()),
assoc_name: assoc_name.into(),
signature_hash: None,
}
}
pub fn with_trait(
mut self,
trait_path: impl Into<String>,
) -> Self {
self.trait_path = Some(trait_path.into());
self
}
pub fn with_signature_hash(
mut self,
hash: u64,
) -> Self {
self.signature_hash = Some(hash);
self
}
pub fn module_level(mut self) -> Self {
self.trait_path = None;
self
}
pub fn type_path(&self) -> &str {
&self.type_path
}
pub fn trait_path(&self) -> Option<&str> {
self.trait_path.as_deref()
}
pub fn assoc_name(&self) -> &str {
&self.assoc_name
}
pub fn is_module_level(&self) -> bool {
self.trait_path.is_none()
}
pub fn is_scoped(&self) -> bool {
self.trait_path.is_some()
}
pub fn to_tuple(&self) -> (String, Option<String>, String) {
(self.type_path.clone(), self.trait_path.clone(), self.assoc_name.clone())
}
pub fn from_tuple(tuple: (String, Option<String>, String)) -> Self {
Self {
type_path: tuple.0,
trait_path: tuple.1,
assoc_name: tuple.2,
signature_hash: None,
}
}
}
impl Hash for ProjectionKey {
fn hash<H: Hasher>(
&self,
state: &mut H,
) {
self.type_path.hash(state);
self.trait_path.hash(state);
self.assoc_name.hash(state);
self.signature_hash.hash(state);
}
}
impl From<(String, Option<String>, String)> for ProjectionKey {
fn from(tuple: (String, Option<String>, String)) -> Self {
Self {
type_path: tuple.0,
trait_path: tuple.1,
assoc_name: tuple.2,
signature_hash: None,
}
}
}
impl From<ProjectionKey> for (String, Option<String>, String) {
fn from(key: ProjectionKey) -> Self {
(key.type_path, key.trait_path, key.assoc_name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_module_level() {
let key = ProjectionKey::new("MyBrand", "Of");
assert_eq!(key.type_path(), "MyBrand");
assert_eq!(key.trait_path(), None);
assert_eq!(key.assoc_name(), "Of");
assert!(key.is_module_level());
assert!(!key.is_scoped());
}
#[test]
fn test_scoped() {
let key = ProjectionKey::scoped("MyBrand", "Functor", "Map");
assert_eq!(key.type_path(), "MyBrand");
assert_eq!(key.trait_path(), Some("Functor"));
assert_eq!(key.assoc_name(), "Map");
assert!(!key.is_module_level());
assert!(key.is_scoped());
}
#[test]
fn test_with_trait() {
let key = ProjectionKey::new("MyBrand", "Of").with_trait("Functor");
assert_eq!(key.type_path(), "MyBrand");
assert_eq!(key.trait_path(), Some("Functor"));
assert_eq!(key.assoc_name(), "Of");
}
#[test]
fn test_module_level_conversion() {
let key = ProjectionKey::scoped("MyBrand", "Functor", "Map").module_level();
assert_eq!(key.trait_path(), None);
assert!(key.is_module_level());
}
#[test]
fn test_tuple_conversion() {
let original = ProjectionKey::scoped("MyBrand", "Functor", "Map");
let tuple = original.to_tuple();
let restored = ProjectionKey::from_tuple(tuple);
assert_eq!(original, restored);
}
#[test]
fn test_from_into_tuple() {
let tuple = ("MyBrand".to_string(), Some("Functor".to_string()), "Map".to_string());
let key: ProjectionKey = tuple.clone().into();
let restored: (String, Option<String>, String) = key.into();
assert_eq!(tuple, restored);
}
#[test]
fn test_equality() {
let key1 = ProjectionKey::scoped("MyBrand", "Functor", "Map");
let key2 = ProjectionKey::scoped("MyBrand", "Functor", "Map");
let key3 = ProjectionKey::new("MyBrand", "Map");
assert_eq!(key1, key2);
assert_ne!(key1, key3);
}
#[test]
fn test_hash_consistency() {
use std::collections::HashSet;
let key1 = ProjectionKey::scoped("MyBrand", "Functor", "Map");
let key2 = ProjectionKey::scoped("MyBrand", "Functor", "Map");
let mut set = HashSet::new();
set.insert(key1.clone());
assert!(set.contains(&key2));
}
}