use std::sync::Arc;
use uuid::Uuid;
use crate::{request::RequestContext, store::StoreRegistry};
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait RequestScoped: sealed::Sealed {
#[doc(hidden)]
fn __request(&self) -> &Arc<RequestContext>;
fn tx(&self) -> &str {
&self.__request().tx
}
fn client_id(&self) -> Option<&str> {
self.__request().client_id.as_deref()
}
fn host_id(&self) -> Uuid {
self.__request().host_id
}
fn lineage(&self) -> &[Arc<str>] {
&self.__request().lineage
}
}
pub trait RegistryScoped: sealed::Sealed {
#[doc(hidden)]
fn __registry(&self) -> &Arc<StoreRegistry>;
fn registry(&self) -> Arc<StoreRegistry> {
self.__registry().clone()
}
}
pub trait ServerScoped: RequestScoped {
#[doc(hidden)]
fn __server_ctx(&self) -> &Arc<crate::server::MykoServerContext>;
}
use hyphae::{Cell, CellImmutable, CellMap, CellValue};
use serde::{Serialize, de::DeserializeOwned};
use crate::{
cache::CacheKey,
command::{CommandContext, CommandError, CommandHandler},
common::with_id::{WithId, WithTypedId},
core::item::Eventable,
query::{LiveFilterQuery, QueryParams},
report::{ReportHandler, ReportId},
wire::MEvent,
};
type QueryDiffCell<T> = Cell<Option<hyphae::MapDiff<Arc<str>, T>>, CellImmutable>;
pub trait Querying: ServerScoped {
fn query_map<Q>(
&self,
query: Q,
) -> CellMap<<Q::Item as WithTypedId>::Id, Arc<Q::Item>, CellImmutable>
where
Q: QueryParams + 'static,
Q::Item: Eventable
+ WithId
+ WithTypedId
+ DeserializeOwned
+ Clone
+ std::fmt::Debug
+ Send
+ Sync
+ CellValue
+ 'static,
{
self.__server_ctx()
.query_map(query, self.__request().clone())
}
fn query_map_by_str<Q>(&self, query: Q) -> CellMap<Arc<str>, Arc<Q::Item>, CellImmutable>
where
Q: QueryParams + 'static,
Q::Item: Eventable
+ WithId
+ WithTypedId
+ DeserializeOwned
+ Clone
+ std::fmt::Debug
+ Send
+ Sync
+ CellValue
+ 'static,
{
self.__server_ctx()
.query_map_by_str(query, self.__request().clone())
}
fn query_map_untyped<Q>(&self, query: Q) -> crate::query::FilteredCellMap
where
Q: crate::query::QueryFactory
+ crate::query::QueryHandler
+ QueryParams
+ Clone
+ Send
+ Sync
+ 'static,
Q::Item: DeserializeOwned + Clone + std::fmt::Debug + Send + Sync + 'static,
{
self.__server_ctx()
.query_map_untyped(query, self.__request().clone())
}
fn query_diff<Q>(&self, query: Q) -> QueryDiffCell<Q::Item>
where
Q: crate::query::QueryFactory
+ crate::query::QueryHandler
+ QueryParams
+ Clone
+ Send
+ Sync
+ 'static,
Q::Item: DeserializeOwned + Clone + std::fmt::Debug + Send + Sync + 'static,
{
use hyphae::{MapExt, Materialize};
self.query_map_untyped(query)
.diffs()
.map(|diff| crate::item::downcast_any_item_map_diff::<Q::Item>(diff, "query_diff"))
.materialize()
}
fn query_live<F>(
&self,
filter_cell: impl hyphae::Watchable<F>,
) -> CellMap<<F::Item as WithTypedId>::Id, Arc<F::Item>, CellImmutable>
where
F: LiveFilterQuery,
F::Item: WithTypedId,
{
self.__server_ctx().query_live(filter_cell)
}
}
pub trait GraphQuerying: ServerScoped {
fn edges<E>(&self) -> crate::graph::EdgeQuery<'_, E>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
self.__server_ctx().edges::<E>()
}
fn traverse<E>(&self) -> crate::graph::TraversalBuilder<'_, E>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
self.__server_ctx().traverse::<E>()
}
}
pub trait Searching: ServerScoped {
fn search(&self, entity_type: &str, query: &str, limit: usize) -> Vec<Arc<str>> {
self.__server_ctx()
.search_index()
.search(entity_type, query, limit)
}
}
pub trait Reporting: ServerScoped {
fn report<R>(&self, report: R) -> Cell<Arc<R::Output>, CellImmutable>
where
R: ReportHandler + ReportId + CacheKey + Clone + Serialize + 'static,
{
self.__server_ctx().report(report, self.__request().clone())
}
}
pub trait EventPublishing: ServerScoped {
#[doc(hidden)]
fn __command_id(&self) -> &Arc<str>;
#[doc(hidden)]
fn __emit_err(&self, message: impl std::fmt::Display) -> CommandError {
CommandError::new(
self.__request().tx.to_string(),
self.__command_id().to_string(),
message.to_string(),
)
}
fn emit_set<T>(&self, item: impl std::ops::Deref<Target = T>) -> Result<(), CommandError>
where
T: Eventable + Serialize + Clone + 'static,
{
self.__server_ctx()
.set(&*item)
.map_err(|e| self.__emit_err(e))
}
fn emit_set_transient<T>(
&self,
item: impl std::ops::Deref<Target = T>,
) -> Result<(), CommandError>
where
T: Eventable + Serialize + Clone + 'static,
{
self.__server_ctx()
.set_transient(&*item)
.map_err(|e| self.__emit_err(e))
}
fn emit_set_batch<T: Eventable + Serialize + Clone + 'static>(
&self,
items: &[T],
) -> Result<(), CommandError> {
let anys = items
.iter()
.map(|item| -> Arc<dyn crate::item::AnyItem> { Arc::new(item.clone()) });
self.__server_ctx()
.set_batch_any(anys)
.map_err(|e| self.__emit_err(e))
}
fn emit_set_any_batch<I>(&self, items: I) -> Result<(), CommandError>
where
I: IntoIterator<Item = Arc<dyn crate::item::AnyItem>>,
{
self.__server_ctx()
.set_batch_any(items)
.map_err(|e| self.__emit_err(e))
}
fn emit_del<T>(&self, item: impl std::ops::Deref<Target = T>) -> Result<(), CommandError>
where
T: Eventable + Serialize + Clone + 'static,
{
self.__server_ctx()
.del(&*item)
.map_err(|e| self.__emit_err(e))
}
fn emit_del_batch<'a, T, I>(&self, items: I) -> Result<(), CommandError>
where
T: Eventable + Serialize + Clone + 'static,
I: IntoIterator<Item = &'a T>,
T: 'a,
{
let anys = items
.into_iter()
.map(|item| -> Arc<dyn crate::item::AnyItem> { Arc::new(item.clone()) });
self.__server_ctx()
.del_batch_any(anys)
.map_err(|e| self.__emit_err(e))
}
fn emit_replace_batch<T>(&self, upserts: &[T], deletes: &[Arc<T>]) -> Result<(), CommandError>
where
T: Eventable + Serialize + Clone + 'static,
{
let upserts = upserts
.iter()
.map(|item| -> Arc<dyn crate::item::AnyItem> { Arc::new(item.clone()) });
let deletes = deletes
.iter()
.cloned()
.map(|item| -> Arc<dyn crate::item::AnyItem> { item });
self.__server_ctx()
.replace_batch_any(upserts, deletes)
.map_err(|e| self.__emit_err(e))
}
fn emit_event_batch(&self, events: Vec<MEvent>) -> Result<usize, CommandError> {
self.__server_ctx()
.apply_events_immediate(events)
.map_err(|e| self.__emit_err(e))
}
}
pub trait CommandSending: ServerScoped {
#[doc(hidden)]
fn __command_ctx(&self) -> CommandContext;
fn execute_command<C: CommandHandler>(&self, cmd: C) -> Result<C::Result, CommandError> {
let _span = tracing::trace_span!("myko.command", cmd = C::command_id_static()).entered();
crate::server::dispatch_metrics::record_command(C::command_id_static(), "internal");
cmd.execute(self.__command_ctx())
}
}
pub trait Viewing: ServerScoped {
fn view<V>(&self, view: V) -> crate::core::view::TypedViewCellMap<V::Item>
where
V: crate::core::view::ViewFactory + Clone,
V::Item: DeserializeOwned + Clone + std::fmt::Debug,
{
self.__server_ctx().view(view, self.__request().clone())
}
fn view_map_untyped<V>(&self, view: V) -> crate::core::view::FilteredViewCellMap
where
V: crate::core::view::ViewFactory + Clone + Send + Sync + 'static,
V::Item: DeserializeOwned + Clone + std::fmt::Debug + Send + Sync + 'static,
{
self.__server_ctx()
.view_map_untyped(view, self.__request().clone())
}
}
pub trait PeerAccess: ServerScoped {
fn peer_client(&self, peer_id: &str) -> Option<Arc<crate::client::MykoClient>> {
self.__server_ctx().peer_client(peer_id)
}
fn peer_clients_tick(&self) -> Cell<u64, CellImmutable> {
self.__server_ctx().peer_clients_tick()
}
}
pub trait HistoryReading: sealed::Sealed {
#[doc(hidden)]
fn __history_replay(&self) -> Option<&Arc<dyn crate::server::HistoryReplayProvider>>;
fn entity_history_page(
&self,
key: &crate::server::HistoryEntityKey,
window: &crate::wire::QueryWindow,
) -> Result<crate::server::HistoryPage, String> {
self.__history_replay()
.ok_or_else(|| "No history replay provider configured".to_string())?
.entity_history_page(key, window)
}
fn committed_history_event(
&self,
) -> Cell<Option<Arc<crate::server::CommittedHistoryEvent>>, CellImmutable> {
self.__history_replay().map_or_else(
|| Cell::new(None).lock(),
|provider| provider.committed_history_event(),
)
}
}
pub trait Replaying: ServerScoped + HistoryReading {
fn persist_health(&self) -> Arc<crate::server::PersistHealth> {
self.__server_ctx().persist_health()
}
fn replay_store(&self, until: &str) -> Result<Arc<StoreRegistry>, String> {
let ctx = self.__server_ctx();
let provider = ctx
.history_replay()
.ok_or_else(|| "No history replay provider configured".to_string())?;
provider.replay_to_store(until, &ctx.handler_registry)
}
}
const fn capability_matrix() {
const fn querying<T: Querying>() {}
const fn searching<T: Searching>() {}
const fn reporting<T: Reporting>() {}
const fn event_publishing<T: EventPublishing>() {}
const fn command_sending<T: CommandSending>() {}
const fn viewing<T: Viewing>() {}
const fn peer_access<T: PeerAccess>() {}
const fn replaying<T: Replaying>() {}
const fn history_reading<T: HistoryReading>() {}
use crate::core::{
query::QueryBuildContext,
report::ReportContext,
view::{ViewBuildContext, ViewContext},
};
querying::<ReportContext>();
searching::<ReportContext>();
reporting::<ReportContext>();
querying::<ViewContext>();
searching::<ViewContext>();
reporting::<ViewContext>();
querying::<ViewBuildContext>();
searching::<ViewBuildContext>();
reporting::<ViewBuildContext>();
event_publishing::<CommandContext>();
command_sending::<CommandContext>();
viewing::<ReportContext>();
peer_access::<ReportContext>();
replaying::<ReportContext>();
history_reading::<ReportContext>();
history_reading::<QueryBuildContext>();
viewing::<ViewContext>();
viewing::<ViewBuildContext>();
}
const _: () = capability_matrix();