use std::{hash::Hash, pin::Pin};
use topcoat_core::{context::Cx, error::Result};
use topcoat_router::{
Body, Method, Methods, Path, PathBuf, Route, RouteFuture, RouteId, RouterBuilder,
response::IntoResponse,
};
use topcoat_view::ViewHandle;
pub(crate) const SHARD_ROUTE_PREFIX: &str = "/_topcoat/shards";
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct ShardId(&'static str);
impl ShardId {
#[must_use]
pub const fn new(inner: &'static str) -> Self {
Self(inner)
}
#[must_use]
pub fn as_str(&self) -> &str {
self.0
}
}
pub type ShardFuture<'cx> = Pin<Box<dyn Future<Output = Result<ViewHandle>> + Send + 'cx>>;
pub trait Shard: Send + Sync + 'static {
fn id(&self) -> ShardId;
fn render<'cx>(&'cx self, cx: &'cx Cx, body: Body) -> ShardFuture<'cx>;
}
impl<S: Shard + ?Sized> Shard for &'static S {
fn id(&self) -> ShardId {
(**self).id()
}
fn render<'cx>(&'cx self, cx: &'cx Cx, body: Body) -> ShardFuture<'cx> {
(**self).render(cx, body)
}
}
#[cfg(feature = "discover")]
inventory::collect!(&'static dyn Shard);
pub struct ShardRoute {
id: RouteId,
path: PathBuf,
shard: Box<dyn Shard>,
}
impl ShardRoute {
pub fn new(shard: impl Shard) -> Self {
Self {
id: RouteId::new(),
path: Path::new(&format!("{SHARD_ROUTE_PREFIX}/{}", shard.id().as_str())).to_owned(),
shard: Box::new(shard),
}
}
}
impl Route for ShardRoute {
fn id(&self) -> RouteId {
self.id
}
fn methods(&self) -> Methods<'_> {
Methods::Only(&[Method::POST])
}
fn path(&self) -> &Path {
&self.path
}
fn handle<'cx>(&'cx self, cx: &'cx Cx, body: Body) -> RouteFuture<'cx> {
Box::pin(async move {
let view = self.shard.render(cx, body).await?;
view.into_response(cx)
})
}
}
pub trait RouterBuilderShardExt {
#[must_use]
fn shard(self, shard: impl Shard) -> Self;
#[cfg(feature = "discover")]
#[must_use]
fn discover_shards(self) -> Self;
}
impl RouterBuilderShardExt for RouterBuilder {
fn shard(self, shard: impl Shard) -> Self {
self.route(ShardRoute::new(shard))
}
#[cfg(feature = "discover")]
fn discover_shards(mut self) -> Self {
for &shard in inventory::iter::<&'static dyn Shard>() {
self = self.shard(shard);
}
self
}
}