#[cfg(any(feature = "postgres", feature = "sqlite"))]
mod action;
#[cfg(feature = "live")]
pub mod channel;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
mod cx;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
mod http;
#[cfg(feature = "db")]
mod identity;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub mod inbox;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub mod jobs;
#[cfg(feature = "live")]
pub(crate) mod live;
#[cfg(feature = "db")]
pub mod receipts;
#[cfg(feature = "relay")]
pub mod relay;
mod roles;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
mod run;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub mod stream;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use action::ActionCx;
#[cfg(feature = "live")]
pub use channel::ChannelRegistration;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use cx::MutationCx;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use cx::QueryCx;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use cx::ReadSink;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use http::IDEMPOTENCY_KEY;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use http::Server;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub(crate) use http::installed;
#[cfg(feature = "db")]
pub use identity::Anonymous;
#[cfg(feature = "db")]
#[doc(hidden)]
pub use identity::HasContext;
#[cfg(feature = "db")]
pub use identity::Identify;
#[cfg(feature = "db")]
pub use identity::Identity;
#[cfg(feature = "db")]
pub use identity::IdentityLease;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::JobCx;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::JobRegistration;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::JobRun;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::Jobs;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::JobsHandle;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use jobs::cancel;
#[cfg(feature = "live")]
pub use live::Live;
#[cfg(feature = "live")]
pub use live::LiveStats;
#[cfg(feature = "relay")]
pub use relay::PublisherStats;
#[cfg(feature = "relay")]
pub use relay::Relay;
#[cfg(feature = "relay")]
pub use relay::RelayError;
#[cfg(feature = "relay")]
pub use relay::RelayPublisher;
#[cfg(feature = "relay")]
pub use relay::RelaySettings;
pub use roles::ROLE_ENV;
pub use roles::Role;
pub use roles::Roles;
pub use roles::RolesError;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use run::Invocation;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use run::Target;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use run::call;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use run::call_as;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
#[doc(hidden)]
pub use run::handle;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use stream::StreamRegistration;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub use stream::StreamSink;
pub trait OperationContext: Send + Sync + Sized + 'static + sealed::Sealed {
#[cfg(any(feature = "postgres", feature = "sqlite"))]
#[doc(hidden)]
fn dispatch<O: crate::Operation<Context = Self>>(
db: &leptoskit_db::Db,
identity: Identity,
request_id: Option<&str>,
op: O,
) -> impl Future<Output = crate::Result<O::Output>> + Send;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
#[doc(hidden)]
fn dispatch_recording<O: crate::Operation<Context = Self>>(
db: &leptoskit_db::Db,
identity: Identity,
request_id: Option<&str>,
reads: Option<ReadSink>,
op: O,
) -> impl Future<Output = crate::Result<O::Output>> + Send {
let _ = reads;
Self::dispatch(db, identity, request_id, op)
}
}
pub(crate) mod sealed {
pub trait Sealed {}
}
#[cfg(any(feature = "postgres", feature = "sqlite"))]
#[derive(Debug)]
pub struct Registration {
pub name: &'static str,
pub kind: crate::Kind,
pub module_path: &'static str,
#[doc(hidden)]
pub handler: run::Handler,
}
#[cfg(any(feature = "postgres", feature = "sqlite"))]
inventory::collect!(Registration);
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub fn registrations() -> Result<Vec<&'static Registration>, RegistryError> {
registrations_from(inventory::iter::<Registration>().collect())
}
#[cfg(any(feature = "postgres", feature = "sqlite"))]
fn registrations_from(
mut registrations: Vec<&'static Registration>,
) -> Result<Vec<&'static Registration>, RegistryError> {
registrations.sort_by(|a, b| {
a.name
.cmp(b.name)
.then_with(|| a.module_path.cmp(b.module_path))
});
for pair in registrations.windows(2) {
if pair[0].name == pair[1].name {
return Err(RegistryError {
name: pair[0].name,
first: pair[0].module_path,
second: pair[1].module_path,
});
}
}
Ok(registrations)
}
#[cfg(any(feature = "postgres", feature = "sqlite"))]
pub fn registration(name: &str) -> Option<&'static Registration> {
inventory::iter::<Registration>().find(|registration| registration.name == name)
}
#[cfg(any(feature = "postgres", feature = "sqlite"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error(
"two operations are named `{name}` (in `{first}` and `{second}`); rename one, or give one its \
own `name = \"...\"`"
)]
pub struct RegistryError {
pub name: &'static str,
pub first: &'static str,
pub second: &'static str,
}
#[cfg(all(test, any(feature = "postgres", feature = "sqlite")))]
mod tests {
use super::*;
fn handler(
_: Invocation,
) -> leptoskit_query::BoxFuture<'static, crate::Result<serde_json::Value>> {
Box::pin(std::future::ready(Ok(serde_json::Value::Null)))
}
static A: Registration = Registration {
name: "a",
kind: crate::Kind::Query,
module_path: "app::one",
handler,
};
static B: Registration = Registration {
name: "b",
kind: crate::Kind::Mutation,
module_path: "app::one",
handler,
};
static A_AGAIN: Registration = Registration {
name: "a",
kind: crate::Kind::Mutation,
module_path: "app::two",
handler,
};
#[test]
fn a_name_belongs_to_one_operation() {
let sorted = registrations_from(vec![&B, &A]).expect("distinct names");
assert_eq!(
sorted.iter().map(|r| r.name).collect::<Vec<_>>(),
["a", "b"]
);
let error = registrations_from(vec![&A_AGAIN, &B, &A]).expect_err("a clash");
assert_eq!(error.name, "a");
assert_eq!((error.first, error.second), ("app::one", "app::two"));
assert!(error.to_string().contains("two operations are named `a`"));
}
}