distributed 4.0.2

CQRS/ES framework for Rust using Plain Old Rust Structs — append-only events, replay, snapshots, outbox, service bus, and pluggable infrastructure
Documentation
use std::marker::PhantomData;

use super::effects::{EffectExpression, EffectFieldValue, EffectKey, EffectRelationship};
use super::projection_obligations::{CommandInputDefault, InputDefaultGenerator};
use crate::read_model::RelationalReadModel;

/// Wire-shape proofs emitted by derives and erased after compatibility checks.
#[doc(hidden)]
pub struct EffectWireChecked;
#[doc(hidden)]
pub struct EffectWireLiteral;
#[doc(hidden)]
pub struct EffectWireString;
#[doc(hidden)]
pub struct EffectWireBoolean;
#[doc(hidden)]
pub struct EffectWireBigInt;
#[doc(hidden)]
pub struct EffectWireFloat;
#[doc(hidden)]
pub struct EffectWireJson;
#[doc(hidden)]
pub struct EffectWireBytea;
#[doc(hidden)]
pub struct EffectWireTimestamp;
#[doc(hidden)]
pub struct EffectWireList;
#[doc(hidden)]
pub struct EffectWireObject;
#[doc(hidden)]
pub struct EffectWireUnsupported;

/// Closed compile-time compatibility relation between GraphQL input wire
/// shapes and read-model scalar codecs.
#[doc(hidden)]
pub trait EffectWireCompatible<Target> {}

macro_rules! exact_effect_wire_compatibility {
    ($($wire:ty),+ $(,)?) => {
        $(impl EffectWireCompatible<$wire> for $wire {})+
    };
}

exact_effect_wire_compatibility!(
    EffectWireString,
    EffectWireBoolean,
    EffectWireBigInt,
    EffectWireFloat,
    EffectWireJson,
    EffectWireBytea,
    EffectWireTimestamp,
    EffectWireList,
    EffectWireObject,
    EffectWireUnsupported,
);

// JSON model columns deliberately accept a complete input container as their
// leaf value. Other scalar codecs never accept list/object wire shapes.
impl EffectWireCompatible<EffectWireJson> for EffectWireList {}
impl EffectWireCompatible<EffectWireJson> for EffectWireObject {}

// Constants and explicit null retain exact Rust value typing; Surface
// validation checks their serialized scalar/null representation.
impl<Target> EffectWireCompatible<Target> for EffectWireLiteral {}

/// Typed portable expression used only while constructing erased effect IR.
#[doc(hidden)]
pub struct TypedEffectExpression<T, Wire = EffectWireChecked> {
    expression: EffectExpression,
    _value: PhantomData<fn() -> (T, Wire)>,
}

impl<T, Wire> TypedEffectExpression<T, Wire> {
    #[doc(hidden)]
    pub(crate) fn __into_ir(self) -> EffectExpression {
        self.expression
    }

    fn erase_wire(self) -> TypedEffectExpression<T> {
        TypedEffectExpression {
            expression: self.expression,
            _value: PhantomData,
        }
    }
}

/// Marker implemented only by `GraphqlInput` derive output in normal use.
///
/// The trait must be public because derive output lives in downstream crates.
/// All marker metadata is still revalidated against the final command Surface;
/// hand-written implementations cannot bypass runtime structural checks.
#[doc(hidden)]
pub struct EffectRequired;

#[doc(hidden)]
pub struct EffectNullable;

/// Derive-owned classification for a field that is a nested input object and
/// may therefore appear before another segment in an effect input path.
#[doc(hidden)]
pub struct EffectInputObjectKind;

/// Derive-owned classification for scalar and list fields. These fields are
/// valid leaves but cannot be traversed by effect input paths.
#[doc(hidden)]
pub struct EffectInputTerminalKind;

#[doc(hidden)]
pub trait EffectInputPathKind {}

impl EffectInputPathKind for EffectInputObjectKind {}
impl EffectInputPathKind for EffectInputTerminalKind {}

/// Implemented only for the derive-owned nested-object classification.
#[doc(hidden)]
pub trait EffectInputDescendableKind: EffectInputPathKind {}

impl EffectInputDescendableKind for EffectInputObjectKind {}

#[doc(hidden)]
pub trait EffectPathNullability {
    type Applied<T>;
}

impl EffectPathNullability for EffectRequired {
    type Applied<T> = T;
}

impl EffectPathNullability for EffectNullable {
    type Applied<T> = Option<T>;
}

#[doc(hidden)]
pub trait CombineEffectNullability<Other: EffectPathNullability> {
    type Output: EffectPathNullability;
}

impl CombineEffectNullability<EffectRequired> for EffectRequired {
    type Output = EffectRequired;
}

impl CombineEffectNullability<EffectNullable> for EffectRequired {
    type Output = EffectNullable;
}

impl CombineEffectNullability<EffectRequired> for EffectNullable {
    type Output = EffectNullable;
}

impl CombineEffectNullability<EffectNullable> for EffectNullable {
    type Output = EffectNullable;
}

#[doc(hidden)]
pub trait EffectInputFieldMarker {
    type Input: 'static;
    type Value;
    type NonNullValue;
    type Nullability: EffectPathNullability;
    type PathKind: EffectInputPathKind;
    type Wire;
    /// Unwrapped object type used when descending through a nested input.
    type Nested: 'static;
    fn path() -> Vec<&'static str>;
}

/// Type-level composition of two derive-generated input-field markers.
#[doc(hidden)]
pub struct EffectInputPath<Outer, Inner>(PhantomData<fn(Outer) -> Inner>);

impl<Outer, Inner> EffectInputFieldMarker for EffectInputPath<Outer, Inner>
where
    Outer: EffectInputFieldMarker,
    Inner: EffectInputFieldMarker<Input = Outer::Nested>,
    Outer::PathKind: EffectInputDescendableKind,
    Outer::Nullability: CombineEffectNullability<Inner::Nullability>,
{
    type Input = Outer::Input;
    type Value = <<Outer::Nullability as CombineEffectNullability<
        Inner::Nullability,
    >>::Output as EffectPathNullability>::Applied<Inner::NonNullValue>;
    type NonNullValue = Inner::NonNullValue;
    type Nullability = <Outer::Nullability as CombineEffectNullability<Inner::Nullability>>::Output;
    type PathKind = Inner::PathKind;
    type Wire = Inner::Wire;
    type Nested = Inner::Nested;

    fn path() -> Vec<&'static str> {
        let mut path = Outer::path();
        path.extend(Inner::path());
        path
    }
}
/// Marker implemented by a `ReadModel` derive for one concrete model field.
#[doc(hidden)]
pub trait EffectModelFieldMarker {
    type Model: RelationalReadModel;
    type Value;
    type Wire;
    const FIELD: &'static str;
}

/// Marker implemented by a `ReadModel` derive for one relationship.
#[doc(hidden)]
pub trait EffectRelationshipMarker {
    type Source: RelationalReadModel;
    type Target: RelationalReadModel;
    const FIELD: &'static str;
}

/// Typed model key generated as a named struct by `#[derive(ReadModel)]`.
///
/// Retained for derive output stability; application authors no longer build
/// effect IR from these keys (mutation IR owns that path).
#[doc(hidden)]
#[allow(dead_code)]
pub struct TypedEffectKey<M> {
    key: EffectKey,
    _model: PhantomData<fn() -> M>,
}

/// Opaque key field emitted by a `ReadModel` derive. Application code cannot
/// assemble raw effect IR through the public typed-command API.
#[doc(hidden)]
pub struct CompiledEffectKeyField<M>(EffectFieldValue, PhantomData<fn() -> M>);

#[doc(hidden)]
pub fn __effect_key_field<F>(
    value: TypedEffectExpression<F::Value>,
) -> CompiledEffectKeyField<F::Model>
where
    F: EffectModelFieldMarker,
{
    CompiledEffectKeyField(
        EffectFieldValue {
            field: F::FIELD.to_string(),
            value: value.__into_ir(),
        },
        PhantomData,
    )
}

/// Prove one generated key expression's wire compatibility, then erase the
/// proof so derive-generated composite key structs need no wire generics.
#[doc(hidden)]
pub fn __effect_key_assignment<F, Wire>(
    value: TypedEffectExpression<F::Value, Wire>,
) -> TypedEffectExpression<F::Value>
where
    F: EffectModelFieldMarker,
    Wire: EffectWireCompatible<F::Wire>,
{
    value.erase_wire()
}

/// Assemble a typed model key from derive-generated field markers. The final
/// Surface validator additionally requires the exact ordered primary key.
#[doc(hidden)]
pub fn __effect_key<M: RelationalReadModel>(
    fields: Vec<CompiledEffectKeyField<M>>,
) -> TypedEffectKey<M> {
    TypedEffectKey {
        key: EffectKey {
            fields: fields.into_iter().map(|field| field.0).collect(),
        },
        _model: PhantomData,
    }
}

/// Typed relationship marker generated by `#[derive(ReadModel)]`.
#[doc(hidden)]
#[allow(dead_code)]
pub struct TypedEffectRelationship<S, T> {
    relationship: EffectRelationship,
    _models: PhantomData<fn(S) -> T>,
}

impl<S, T> TypedEffectRelationship<S, T> {}

/// Convert a derive-generated relationship marker into opaque typed IR.
#[doc(hidden)]
pub fn __effect_relationship<R>() -> TypedEffectRelationship<R::Source, R::Target>
where
    R: EffectRelationshipMarker,
{
    TypedEffectRelationship {
        relationship: EffectRelationship {
            source_model: R::Source::schema().model_name.clone(),
            field: R::FIELD.to_string(),
            target_model: R::Target::schema().model_name.clone(),
        },
        _models: PhantomData,
    }
}

/// One compile-checked generated canonical-input default.
#[doc(hidden)]
pub struct CompiledInputDefault<I>(CommandInputDefault, PhantomData<fn(I)>);

/// Declaration-owned generated defaults for one exact command input type.
pub struct CompiledInputDefaults<I>(pub(crate) Vec<CommandInputDefault>, PhantomData<fn(I)>);

#[doc(hidden)]
pub fn __input_default_uuid_v7<I, F>() -> CompiledInputDefault<I>
where
    I: 'static,
    F: EffectInputFieldMarker<Input = I, Value = String>,
{
    CompiledInputDefault(
        CommandInputDefault {
            path: F::path().into_iter().map(str::to_string).collect(),
            generator: InputDefaultGenerator::UuidV7,
        },
        PhantomData,
    )
}

#[doc(hidden)]
pub fn __input_default_ulid<I, F>() -> CompiledInputDefault<I>
where
    I: 'static,
    F: EffectInputFieldMarker<Input = I, Value = String>,
{
    CompiledInputDefault(
        CommandInputDefault {
            path: F::path().into_iter().map(str::to_string).collect(),
            generator: InputDefaultGenerator::Ulid,
        },
        PhantomData,
    )
}

#[doc(hidden)]
pub fn __command_input_defaults<I>(
    defaults: impl IntoIterator<Item = CompiledInputDefault<I>>,
) -> CompiledInputDefaults<I> {
    CompiledInputDefaults(
        defaults.into_iter().map(|default| default.0).collect(),
        PhantomData,
    )
}