saddle-framework 0.3.24

The single business-facing facade for Saddle applications
//! Concrete generated transaction body. Resource authority stays in the
//! framework request; applications supply only typed logic and owned inputs.
use crate::database_capability::{
    DatabaseTarget, GeneratedOwnedQuery, GeneratedOwnedWrite, QueryFailure,
};
use saddle_db::internal::{OwnedQueryProof, OwnedWriteProof, ReservedDatabaseTransactionSession};

pub use saddle_db::internal::ReservedTransactionBody;
pub type Cancel = tokio::sync::futures::OwnedNotified;
#[cfg(feature = "cf-transaction-construction")]
pub fn target(
    process: std::sync::Arc<saddle_db::internal::StartupManagedDatabaseProcessCapability>,
) -> DatabaseTarget {
    DatabaseTarget::for_construction(process)
}
pub type Session<'a, 'b, 'c> =
    ReservedDatabaseTransactionSession<'a, 'b, 'c, tokio::sync::futures::OwnedNotified>;

// CF's real downstream bridge. It is deliberately not selected by App::run.
#[cfg(feature = "cf-transaction-construction")]
pub async fn submit<B: ReservedTransactionBody<Cancel> + Send>(
    process: &saddle_db::internal::StartupManagedDatabaseProcessCapability,
    owner: &mut Option<saddle_runtime::profusegw::ProfuseGwSerialScope<Cancel>>,
    context: &saddle_runtime::request_task::reserved::ReservedTaskContext,
    isolation: crate::database::TransactionIsolation,
    body: B,
) -> Result<
    saddle_db::internal::ReservedDatabaseCompletion<
        Cancel,
        saddle_db::internal::ScopeTransactionResult<B::Value, B::Error>,
    >,
    saddle_runtime::profusegw::ProfuseGwReservedScopeFailure,
> {
    process
        .transaction_scope_reserved(owner, context, isolation, body)
        .await
}

pub fn body_layout<B: ReservedTransactionBody<Cancel> + 'static>() -> std::alloc::Layout {
    fn returned<I, O>(_: impl FnOnce(I) -> O) -> std::alloc::Layout {
        std::alloc::Layout::new::<O>()
    }
    returned(
        |(body, session): (B, &'static mut Session<'static, 'static, 'static>)| {
            body.execute(session)
        },
    )
}

pub async fn optional<O: GeneratedOwnedQuery>(
    session: &mut Session<'_, '_, '_>,
    target: &DatabaseTarget,
    parameters: O::NamedParameters,
) -> Result<Option<O::NamedRow>, QueryFailure<O::NamedParameters>> {
    if let Err(error) = O::validate_parameters(&parameters, target) {
        return Err(QueryFailure::Target { parameters, error });
    }
    let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
    session
        .owned_optional(proof.invocation(O::parameters(parameters)))
        .await
        .map(|row| row.map(From::from))
        .map_err(crate::database_capability::named_failure::<O>)
}

pub async fn write<O: GeneratedOwnedWrite>(
    session: &mut Session<'_, '_, '_>,
    target: &DatabaseTarget,
    parameters: O::NamedParameters,
) -> Result<crate::database::ManagedWriteResult, QueryFailure<O::NamedParameters>> {
    if let Err(error) = O::validate_parameters(&parameters, target) {
        return Err(QueryFailure::Target { parameters, error });
    }
    let proof = OwnedWriteProof::<O>::bind().map_err(QueryFailure::Database)?;
    session
        .owned_write(proof.invocation(O::parameters(parameters)))
        .await
        .map_err(crate::database_capability::named_write_failure::<O>)
}

pub async fn rows<O: GeneratedOwnedQuery>(
    session: &mut Session<'_, '_, '_>,
    target: &DatabaseTarget,
    parameters: O::NamedParameters,
) -> Result<crate::database::Rows<O::Row, O::NamedRow>, QueryFailure<O::NamedParameters>> {
    if let Err(error) = O::validate_parameters(&parameters, target) {
        return Err(QueryFailure::Target { parameters, error });
    }
    let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
    session
        .owned_rows(proof.invocation(O::parameters(parameters)))
        .await
        .map(crate::database_rows::Rows::from_database)
        .map_err(crate::database_capability::named_failure::<O>)
}
pub async fn probe<O: GeneratedOwnedQuery>(
    session: &mut Session<'_, '_, '_>,
    target: &DatabaseTarget,
    parameters: O::NamedParameters,
) -> Result<crate::database::MatchCardinality<O::NamedRow>, QueryFailure<O::NamedParameters>> {
    if let Err(error) = O::validate_parameters(&parameters, target) {
        return Err(QueryFailure::Target { parameters, error });
    }
    let proof = OwnedQueryProof::<O>::bind().map_err(QueryFailure::Database)?;
    session
        .owned_probe(proof.invocation(O::parameters(parameters)))
        .await
        .map(|row| match row {
            saddle_db::internal::OwnedDbProbe::Zero => crate::database::MatchCardinality::Zero,
            saddle_db::internal::OwnedDbProbe::One(row) => {
                crate::database::MatchCardinality::One(row.into())
            }
            saddle_db::internal::OwnedDbProbe::Many => crate::database::MatchCardinality::Many,
        })
        .map_err(crate::database_capability::named_failure::<O>)
}

/// Business declares only owned inputs and transaction logic. The generated
/// body stays concrete, including its awaits; no Box is constructed here.
#[macro_export]
macro_rules! transaction_logic {
    ($vis:vis fn $name:ident ($tx:ident : $route:ty; $($arg:ident : $ty:ty),* $(,)?) -> ($value:ty, $error:ty) $body:block) => {
        $vis fn $name($($arg: $ty),*) -> impl __saddle_cf::Logic<$route, Value=$value, Error=$error> {
            struct Body { $($arg: $ty),* }
            impl __saddle_cf::Logic<$route> for Body {
                type Value = $value;
                type Error = $error;
                async fn execute(self, $tx: &mut __saddle_cf::Transaction<'_, '_, '_, '_, $route>)
                -> Result<$value, $crate::database::TransactionAbort<$error>> {
                    let Self { $($arg),* } = self;
                    $body
                }
            }
            Body { $($arg),* }
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_steps {
    ($route:ty, page, $name:ident, $op:ty) => {};
    ($route:ty, locked_rows, $name:ident, $op:ty) => {
        $crate::__saddle_cf_steps!($route, rows, $name, $op);
    };
    ($route:ty, locked_probe, $name:ident, $op:ty) => {
        $crate::__saddle_cf_steps!($route, probe, $name, $op);
    };
    ($route:ty, rows, $name:ident, $op:ty) => {
        impl Transaction<'_, '_, '_, '_, $route> {
            pub async fn $name(
                &mut self,
                p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
            ) -> Result<
                $crate::database::Rows<
                    <$op as $crate::__private::StaticOwnedQuery>::Row,
                    <$op as $crate::__private::GeneratedOwnedQuery>::NamedRow,
                >,
                $crate::database::QueryFailure<
                    <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
                >,
            > {
                $crate::transaction_construction::rows::<$op>(self.session, self.target, p).await
            }
        }
    };
    ($route:ty, probe, $name:ident, $op:ty) => {
        impl Transaction<'_, '_, '_, '_, $route> {
            pub async fn $name(
                &mut self,
                p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
            ) -> Result<
                $crate::database::MatchCardinality<
                    <$op as $crate::__private::GeneratedOwnedQuery>::NamedRow,
                >,
                $crate::database::QueryFailure<
                    <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
                >,
            > {
                $crate::transaction_construction::probe::<$op>(self.session, self.target, p).await
            }
        }
    };
    ($route:ty, query_optional, $name:ident, $op:ty) => {
        impl Transaction<'_, '_, '_, '_, $route> {
            pub async fn $name(
                &mut self,
                p: <$op as $crate::__private::GeneratedNamedQuery>::NamedParameters,
            ) -> Result<
                Option<<$op as $crate::__private::GeneratedNamedQuery>::NamedRow>,
                $crate::database::DatabaseFailure,
            > {
                let proof = $crate::__private::QueryOptionalOperationProof::<$op>::bind()
                    .map_err(|_| $crate::database::DatabaseFailure::Mapping)?;
                let row = self
                    .session
                    .query(proof.invocation(
                        <$op as $crate::__private::GeneratedNamedQuery>::parameters(p),
                    ))
                    .await?;
                Ok(<$op as $crate::__private::GeneratedNamedQuery>::row(row))
            }
        }
    };
    ($route:ty, optional, $name:ident, $op:ty) => {
        impl Transaction<'_, '_, '_, '_, $route> {
            pub async fn $name(
                &mut self,
                p: <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
            ) -> Result<
                Option<<$op as $crate::__private::GeneratedOwnedQuery>::NamedRow>,
                $crate::database::QueryFailure<
                    <$op as $crate::__private::GeneratedOwnedQuery>::NamedParameters,
                >,
            > {
                $crate::transaction_construction::optional::<$op>(self.session, self.target, p)
                    .await
            }
        }
    };
    ($route:ty, write, $name:ident, $op:ty) => {
        impl Transaction<'_, '_, '_, '_, $route> {
            pub async fn $name(
                &mut self,
                p: <$op as $crate::__private::GeneratedOwnedWrite>::NamedParameters,
            ) -> Result<
                $crate::database::ManagedWriteResult,
                $crate::database::QueryFailure<
                    <$op as $crate::__private::GeneratedOwnedWrite>::NamedParameters,
                >,
            > {
                $crate::transaction_construction::write::<$op>(self.session, self.target, p).await
            }
        }
    };
    ($route:ty, $kind:ident, $name:ident, $op:ty) => {
        compile_error!("unsupported transaction step");
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_route_steps {
    ($route:ty, transaction, $name:ident, {$($kind:ident $method:ident ($op:ty);)+}) => {
        $($crate::__saddle_cf_steps!($route, $kind, $method, $op);)+
    };
    ($route:ty, operations, $name:ident, {$($kind:ident $method:ident ($op:ty);)+}) => {
        $($crate::__saddle_cf_steps!($route, $kind, $method, $op);)+
    };
    ($route:ty, $($other:tt)*) => {};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_route_prepare {
    ($cap:ident, $route:ty, transaction, $method:ident, $spec:tt) => {
        impl $cap<$route> {
            pub fn prepare_transaction<B: __saddle_cf::Logic<$route>>(
                &self,
                body: B,
            ) -> __saddle_cf::Prepared<$route, B> {
                __saddle_cf::Prepared {
                    body,
                    target: self
                        .__database
                        .as_ref()
                        .expect("declared transaction")
                        .target(),
                    route: ::core::marker::PhantomData,
                }
            }
        }
    };
    ($cap:ident, $route:ty, $($other:tt)*) => {};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __saddle_cf_application {
    (capability $cap:ident; modules {$($module:ident {$($mk:ident $mm:ident ($mo:ty);)+})*} routes {$($route:ident {$($kind:ident $method:ident $spec:tt)?})+}) => {
        #[doc(hidden)]
        pub mod __saddle_cf {
            use super::*;
            pub struct Transaction<'t,'s,'r,'l,R> {
                session: &'t mut $crate::transaction_construction::Session<'s,'r,'l>,
                target: &'t $crate::__private::DatabaseTarget,
                route: ::core::marker::PhantomData<fn()->R>,
            }
            pub trait Logic<R>: Send + 'static {
                type Value: Send + 'static;
                type Error: Send + 'static;
                fn execute(self, tx: &mut Transaction<'_,'_,'_,'_,R>) -> impl ::core::future::Future<Output=Result<Self::Value,$crate::database::TransactionAbort<Self::Error>>> + Send;
            }
            pub struct Prepared<R,B> {
                pub(super) body: B,
                pub(super) target: $crate::__private::DatabaseTarget,
                pub(super) route: ::core::marker::PhantomData<fn()->R>,
            }
            pub fn body_layout<R:'static,B:Logic<R>>(_: &B)->::std::alloc::Layout {
                $crate::transaction_construction::body_layout::<Prepared<R,B>>()
            }
            impl<R,B:Logic<R>> $crate::transaction_construction::ReservedTransactionBody<$crate::transaction_construction::Cancel> for Prepared<R,B> {
                type Value=B::Value;
                type Error=B::Error;
                async fn execute(self, session:&mut $crate::transaction_construction::Session<'_,'_,'_>) -> Result<Self::Value,$crate::database::TransactionAbort<Self::Error>> {
                    let mut tx=Transaction { session, target:&self.target, route: ::core::marker::PhantomData };
                    self.body.execute(&mut tx).await
                }
            }
            $($($crate::__saddle_cf_steps!($module,$mk,$mm,$mo);)+)*
            $($crate::__saddle_cf_route_steps!($route,$($kind,$method,$spec)?);)+
        }
    };
}