use async_trait::async_trait;
use std::fmt;
use std::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::Arc;
pub trait FactKey: Eq + Hash + Clone + Send + Sync + 'static {
type Value: Clone + Send + Sync + 'static;
const NAME: &'static str;
}
#[derive(Debug)]
struct MessageError(String);
impl fmt::Display for MessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for MessageError {}
#[derive(Debug, Clone)]
pub enum FactLoadError {
SourceNotRegistered {
fact_name: &'static str,
},
SourceContractViolation {
fact_name: &'static str,
expected: usize,
actual: usize,
},
LoaderCancelled {
fact_name: &'static str,
},
Backend(Arc<dyn std::error::Error + Send + Sync>),
}
impl FactLoadError {
pub fn backend(error: impl std::error::Error + Send + Sync + 'static) -> Self {
Self::Backend(Arc::new(error))
}
pub fn backend_message(message: impl Into<String>) -> Self {
Self::backend(MessageError(message.into()))
}
}
impl fmt::Display for FactLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SourceNotRegistered { fact_name } => {
write!(f, "No fact source registered for '{fact_name}'")
}
Self::SourceContractViolation {
fact_name,
expected,
actual,
} => write!(
f,
"Fact source '{fact_name}' returned {actual} results for {expected} keys"
),
Self::LoaderCancelled { fact_name } => {
write!(f, "Fact load for '{fact_name}' was cancelled")
}
Self::Backend(error) => write!(f, "{error}"),
}
}
}
impl std::error::Error for FactLoadError {}
#[derive(Debug, Clone)]
pub enum FactLoadResult<V> {
Found(V),
Missing,
Error(FactLoadError),
}
#[async_trait]
pub trait FactSource<K>: Send + Sync
where
K: FactKey,
{
async fn load_many(&self, keys: &[K]) -> Vec<FactLoadResult<K::Value>>;
fn max_batch_size(&self) -> Option<NonZeroUsize> {
None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FactSourceRegistrationError {
SharedEmptySession {
fact_name: &'static str,
},
AlreadyRegistered {
fact_name: &'static str,
},
InFlight {
fact_name: &'static str,
},
}
impl fmt::Display for FactSourceRegistrationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SharedEmptySession { .. } => write!(
f,
"EvaluationSession::shared_empty() cannot register fact sources; use EvaluationSession::new() or EvaluationSession::builder()",
),
Self::AlreadyRegistered { fact_name } => write!(
f,
"fact source for '{fact_name}' is already registered; use replace or replace_arc to overwrite it",
),
Self::InFlight { .. } => write!(
f,
"fact sources should not be registered or replaced while loads for the same key type are in flight",
),
}
}
}
impl std::error::Error for FactSourceRegistrationError {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RelationshipQuery<SubjectId, ResourceId, Relation> {
pub subject_id: SubjectId,
pub resource_id: ResourceId,
pub relation: Relation,
}
impl<SubjectId, ResourceId, Relation> FactKey for RelationshipQuery<SubjectId, ResourceId, Relation>
where
SubjectId: Eq + Hash + Clone + Send + Sync + 'static,
ResourceId: Eq + Hash + Clone + Send + Sync + 'static,
Relation: Eq + Hash + Clone + Send + Sync + 'static,
{
type Value = bool;
const NAME: &'static str = "relationship";
}