use std::sync::Arc;
use hyphae::{Cell, CellImmutable};
use super::{
cell::FilteredCellMap, registration::QueryFactory, request::QueryRequest, traits::AnyQuery,
};
use crate::{
core::report::{ReportFactory, ReportHandler, ReportOutputType},
request::RequestContext,
server::MykoServerContext,
store::StoreRegistry,
};
#[derive(Clone, Debug)]
pub struct QueryContext {
pub req: Arc<RequestContext>,
}
impl crate::core::capability::sealed::Sealed for QueryContext {}
impl crate::core::capability::RequestScoped for QueryContext {
fn __request(&self) -> &Arc<RequestContext> {
&self.req
}
}
#[derive(Clone)]
pub struct QueryBuildContext {
pub query_context: Arc<QueryContext>,
registry: Arc<StoreRegistry>,
server_ctx: Option<Arc<MykoServerContext>>,
}
impl QueryBuildContext {
#[must_use]
pub const fn new(
query_context: Arc<QueryContext>,
registry: Arc<StoreRegistry>,
server_ctx: Option<Arc<MykoServerContext>>,
) -> Self {
Self {
query_context,
registry,
server_ctx,
}
}
pub fn query<Q>(&self, query: Q) -> Result<FilteredCellMap, String>
where
Q: QueryFactory + Clone,
Q::Item: crate::core::item::Eventable
+ crate::common::with_id::WithId
+ serde::de::DeserializeOwned
+ Clone
+ std::fmt::Debug
+ Send
+ Sync
+ 'static,
{
if let Some(server_ctx) = self.server_ctx.clone() {
return Ok(server_ctx.query_map_untyped(query, self.query_context.req.clone()));
}
let wrapped = QueryRequest::with_tx(query, self.query_context.req.tx.clone());
let any_query: Arc<dyn AnyQuery> = Arc::new(wrapped);
Q::cell_factory(
any_query,
self.registry.clone(),
self.query_context.req.clone(),
self.server_ctx.clone(),
)
}
pub fn report<R>(
&self,
report: R,
) -> Result<Cell<Arc<<R as ReportOutputType>::Output>, CellImmutable>, String>
where
R: ReportFactory + ReportHandler<Output = <R as ReportOutputType>::Output> + Clone,
<R as ReportOutputType>::Output:
crate::common::to_value::ToValue + std::fmt::Debug + Send + Sync + 'static,
{
let Some(server_ctx) = self.server_ctx.clone() else {
return Err("QueryBuildContext.report requires server context".to_string());
};
Ok(server_ctx.report(report, self.query_context.req.clone()))
}
#[must_use]
pub fn registry(&self) -> Arc<StoreRegistry> {
self.registry.clone()
}
#[doc(hidden)]
pub fn graph_watch_at<E>(
&self,
position: crate::graph::EndPosition,
endpoint: &crate::graph::EndpointValue,
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_at(E::ENTITY_NAME_STATIC, position, endpoint)
.map_err(|error| error.to_string())
}
#[doc(hidden)]
pub fn graph_watch_many_at<E>(
&self,
position: crate::graph::EndPosition,
endpoints: &[crate::graph::EndpointValue],
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_many_at(E::ENTITY_NAME_STATIC, position, endpoints)
.map_err(|error| error.to_string())
}
#[doc(hidden)]
pub fn graph_related_at<E, T>(
&self,
edge_position: crate::graph::EndPosition,
endpoint: &crate::graph::EndpointValue,
related_position: crate::graph::EndPosition,
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
T: crate::graph::EntityEndpointSpec,
{
let edges = self.graph_watch_at::<E>(edge_position, endpoint)?;
let edges = crate::item::typed_map_arc_from_any_item::<E>(
edges,
"QueryBuildContext::graph_related_at",
);
Ok(crate::graph::graph_related_entity_watch::<E, T>(
&edges,
self.registry.as_ref(),
related_position,
))
}
#[doc(hidden)]
pub fn graph_related_many_at<E, T>(
&self,
edge_position: crate::graph::EndPosition,
endpoints: &[crate::graph::EndpointValue],
related_position: crate::graph::EndPosition,
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
T: crate::graph::EntityEndpointSpec,
{
let edges = self.graph_watch_many_at::<E>(edge_position, endpoints)?;
let edges = crate::item::typed_map_arc_from_any_item::<E>(
edges,
"QueryBuildContext::graph_related_many_at",
);
Ok(crate::graph::graph_related_entity_watch::<E, T>(
&edges,
self.registry.as_ref(),
related_position,
))
}
#[doc(hidden)]
pub fn graph_neighbors_at<E, T>(
&self,
endpoint: &crate::graph::EndpointValue,
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
T: crate::graph::EntityEndpointSpec,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
let edges = graph
.watch_incident(E::ENTITY_NAME_STATIC, endpoint)
.map_err(|error| error.to_string())?;
let edges = crate::item::typed_map_arc_from_any_item::<E>(
edges,
"QueryBuildContext::graph_neighbors_at",
);
Ok(crate::graph::graph_neighbor_entity_watch::<E, T>(
&edges,
self.registry.as_ref(),
endpoint,
))
}
#[doc(hidden)]
pub fn graph_watch_between<E>(
&self,
a: &crate::graph::EndpointValue,
b: &crate::graph::EndpointValue,
) -> Result<FilteredCellMap, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_between(E::ENTITY_NAME_STATIC, a, b)
.map_err(|error| error.to_string())
}
#[doc(hidden)]
pub fn graph_window_at<E>(
&self,
position: crate::graph::EndPosition,
endpoint: &crate::graph::EndpointValue,
window: crate::wire::QueryWindow,
) -> Result<Option<super::WindowedQuerySource>, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_window_at(E::ENTITY_NAME_STATIC, position, endpoint, window)
.map_err(|error| error.to_string())
}
#[doc(hidden)]
pub fn graph_window_many_at<E>(
&self,
position: crate::graph::EndPosition,
endpoints: &[crate::graph::EndpointValue],
window: crate::wire::QueryWindow,
) -> Result<Option<super::WindowedQuerySource>, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_window_many_at(E::ENTITY_NAME_STATIC, position, endpoints, window)
.map_err(|error| error.to_string())
}
#[doc(hidden)]
pub fn graph_window_between<E>(
&self,
a: &crate::graph::EndpointValue,
b: &crate::graph::EndpointValue,
window: crate::wire::QueryWindow,
) -> Result<Option<super::WindowedQuerySource>, String>
where
E: crate::graph::GraphEdge,
E::Ends: crate::graph::TypedEdgeEnds,
{
let server = self
.server_ctx
.as_ref()
.ok_or_else(|| "graph query requires server context".to_string())?;
let graph = server
.graph_index()
.ok_or_else(|| "application has no graph registrations".to_string())?;
graph
.watch_window_between(E::ENTITY_NAME_STATIC, a, b, window)
.map_err(|error| error.to_string())
}
}
impl crate::core::capability::sealed::Sealed for QueryBuildContext {}
impl crate::core::capability::RequestScoped for QueryBuildContext {
fn __request(&self) -> &Arc<RequestContext> {
&self.query_context.req
}
}
impl crate::core::capability::HistoryReading for QueryBuildContext {
fn __history_replay(&self) -> Option<&Arc<dyn crate::server::HistoryReplayProvider>> {
self.server_ctx
.as_ref()
.and_then(|server| server.history_replay())
}
}