use std::{any::Any, sync::Arc};
use hyphae::{Cell, CellImmutable, MapExt};
use serde_json::Value;
use super::{
handler::{ReportContext, ReportHandler},
request::ReportRequest,
traits::{AnyReport, ReportParams},
};
use crate::{common::to_value::ToValue, request::RequestContext, server::CellServerCtx};
pub trait AnyOutput: ToValue + std::fmt::Debug + Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
fn equals(&self, other: &dyn AnyOutput) -> bool;
}
impl<T: ToValue + std::fmt::Debug + PartialEq + Send + Sync + 'static> AnyOutput for T {
fn as_any(&self) -> &dyn Any {
self
}
fn equals(&self, other: &dyn AnyOutput) -> bool {
other
.as_any()
.downcast_ref::<Self>()
.map(|typed| self == typed)
.unwrap_or(false)
}
}
impl PartialEq for dyn AnyOutput {
fn eq(&self, other: &Self) -> bool {
self.equals(other)
}
}
pub type ReportParseFn = fn(Value) -> Result<Arc<dyn AnyReport>, anyhow::Error>;
pub type ReportCellFactory = fn(
Arc<dyn AnyReport>,
Arc<RequestContext>,
Arc<CellServerCtx>,
) -> Result<Cell<Arc<dyn AnyOutput>, CellImmutable>, String>;
inventory::collect!(ReportRegistration);
pub struct ReportRegistration {
pub report_id: &'static str,
pub crate_name: &'static str,
pub output_type: &'static str,
pub output_type_crate: &'static str,
pub parse: ReportParseFn,
pub cell_factory: ReportCellFactory,
}
pub trait ReportFactory: ReportParams {
fn parse(value: Value) -> Result<Arc<dyn AnyReport>, anyhow::Error>;
fn cell_factory(
report: Arc<dyn AnyReport>,
request_ctx: Arc<RequestContext>,
server_ctx: Arc<CellServerCtx>,
) -> Result<Cell<Arc<dyn AnyOutput>, CellImmutable>, String>;
}
impl<R: ReportParams> ReportFactory for R {
fn parse(value: Value) -> Result<Arc<dyn AnyReport>, anyhow::Error> {
let report = serde_json::from_value::<ReportRequest<R>>(value)?;
Ok(Arc::new(report))
}
fn cell_factory(
any_report: Arc<dyn AnyReport>,
request_ctx: Arc<RequestContext>,
server_ctx: Arc<CellServerCtx>,
) -> Result<Cell<Arc<dyn AnyOutput>, CellImmutable>, String> {
let any_ref: &dyn Any = any_report.as_ref();
let request: ReportRequest<R> = any_ref
.downcast_ref::<ReportRequest<R>>()
.cloned()
.ok_or_else(|| {
format!(
"Failed to downcast report to ReportRequest<{}>",
R::report_id_static()
)
})?;
let ctx = ReportContext::new(request_ctx, server_ctx);
let report_id = R::report_id_static();
let cell = <R as ReportHandler>::compute(&request.report, ctx);
let report_name = format!("report:{}", report_id);
Ok(cell
.map(|output| output.clone() as Arc<dyn AnyOutput>)
.with_name(report_name.as_str()))
}
}