1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![feature(type_alias_impl_trait)]
3
4use std::{ops::Deref, sync::Arc};
5
6use anyhow::Result;
7use kvid::KvId;
8pub use site_log_derive::linkme;
9use tosql::ToSqlTrait;
10mod ctx;
11pub use ctx::Ctx;
12pub use tokio::spawn;
13
14pub static SITE_LOG_ID: KvId = KvId::const_new("siteLog");
15
16pub struct Site {
17 pub ctx: Arc<ctx::Ctx>,
18}
19
20#[derive(Clone)]
21pub struct Arg<T: Sized> {
22 pub ctx: Arc<ctx::Ctx>,
23 pub inner: Arc<T>,
24}
25
26impl<T> Deref for Arg<T> {
27 type Target = T;
28 fn deref(&self) -> &Self::Target {
29 self.inner.as_ref()
30 }
31}
32
33pub type ArcArg<T> = Arc<Arg<T>>;
34pub type JoinHandle = tokio::task::JoinHandle<Result<()>>;
35pub type Hook<T> = fn(ArcArg<T>) -> JoinHandle;
36pub type HookLi<T> = [Hook<T>];
37pub type HookSlice<T> = &'static linkme::DistributedSlice<HookLi<T>>;
38
39pub trait TypeHook
40where
41 Self: 'static + Sized,
42{
43 const ID: u64;
44 const HOOK: HookSlice<Self>;
45}
46
47impl Site {
48 pub async fn log<T: Send + Sync + TypeHook + ToSqlTrait>(
49 &self,
50 row: impl Into<Arc<T>>,
51 ) -> Result<()> {
52 let row = row.into();
53 let ctx = self.ctx.clone();
54 let (log_result, task_result) = tokio::join!(
55 tokio::spawn({
56 let dump = row.dump();
57 let ctx = ctx.clone();
58 async move {
59 use fred::types::streams::XID;
60 use xkv::fred::interfaces::StreamsInterface;
61 ctx
62 .xadd::<(), _, _, _, _>(
63 &ctx.site_key[..],
64 false,
65 None,
66 XID::Auto,
67 (&ctx.ipbin[..], xbin::concat!(ctx.bin, dump)),
68 )
69 .await
70 }
71 }),
72 tokio::spawn({
73 let arg = Arc::new(Arg { ctx, inner: row });
74 async move {
75 for i in T::HOOK.iter().map(|f| f(arg.clone())) {
76 let _ = i.await?;
77 }
78 Ok::<_, anyhow::Error>(())
79 }
80 })
81 );
82 let _ = task_result?;
83 let _ = log_result?;
84 Ok(())
85 }
86}