1use std::collections::HashMap;
14use std::error::Error;
15use std::ops::Deref;
16use std::sync::Arc;
17
18use async_lock::{Mutex, RwLock};
19use futures::Future;
20use futures::future::{BoxFuture, LocalBoxFuture, join_all};
21use prost::Message;
22use serde::{Deserialize, Serialize};
23use ts_rs::TS;
24
25use crate::proto::request::ClientReq;
26use crate::proto::response::ClientResp;
27use crate::proto::{
28 ColumnType, GetFeaturesReq, GetFeaturesResp, GetHostedTablesReq, GetHostedTablesResp,
29 HostedTable, JoinType, MakeJoinTableReq, MakeTableReq, RemoveHostedTablesUpdateReq, Request,
30 Response, ServerError, ServerSystemInfoReq,
31};
32use crate::table::{JoinOptions, Table, TableInitOptions, TableOptions};
33use crate::table_data::{TableData, UpdateData};
34use crate::table_ref::TableRef;
35use crate::utils::*;
36use crate::view::{OnUpdateData, ViewWindow};
37use crate::{OnUpdateMode, OnUpdateOptions, asyncfn, clone};
38
39#[derive(Clone, Debug, Serialize, Deserialize, TS)]
41pub struct SystemInfo<T = u64> {
42 pub heap_size: T,
44
45 pub used_size: T,
47
48 pub cpu_time: u32,
53
54 pub cpu_time_epoch: u32,
56
57 pub timestamp: Option<T>,
61
62 pub client_heap: Option<T>,
65
66 pub client_used: Option<T>,
69}
70
71impl<U: Copy + 'static> SystemInfo<U> {
72 pub fn cast<T: Copy + 'static>(&self) -> SystemInfo<T>
75 where
76 U: num_traits::AsPrimitive<T>,
77 {
78 SystemInfo {
79 heap_size: self.heap_size.as_(),
80 used_size: self.used_size.as_(),
81 cpu_time: self.cpu_time,
82 cpu_time_epoch: self.cpu_time_epoch,
83 timestamp: self.timestamp.map(|x| x.as_()),
84 client_heap: self.client_heap.map(|x| x.as_()),
85 client_used: self.client_used.map(|x| x.as_()),
86 }
87 }
88}
89
90#[derive(Clone, Debug, Default, PartialEq)]
93pub struct Features(Arc<GetFeaturesResp>);
94
95impl Features {
96 pub fn get_group_rollup_modes(&self) -> Vec<crate::config::GroupRollupMode> {
97 self.group_rollup_mode
98 .iter()
99 .map(|x| {
100 crate::config::GroupRollupMode::from(
101 crate::proto::GroupRollupMode::try_from(*x).unwrap(),
102 )
103 })
104 .collect::<Vec<_>>()
105 }
106
107 pub fn get_split_rollup_modes(&self) -> Vec<crate::config::SplitRollupMode> {
112 if self.split_rollup_mode.is_empty() {
113 return vec![crate::config::SplitRollupMode::Flat];
114 }
115
116 self.split_rollup_mode
117 .iter()
118 .map(|x| {
119 crate::config::SplitRollupMode::from(
120 crate::proto::SplitRollupMode::try_from(*x).unwrap(),
121 )
122 })
123 .collect::<Vec<_>>()
124 }
125}
126
127impl Deref for Features {
128 type Target = GetFeaturesResp;
129
130 fn deref(&self) -> &Self::Target {
131 &self.0
132 }
133}
134
135impl GetFeaturesResp {
136 pub fn default_op(&self, col_type: ColumnType) -> Option<&str> {
137 self.filter_ops
138 .get(&(col_type as u32))?
139 .options
140 .first()
141 .map(|x| x.as_str())
142 }
143
144 pub fn get_window_aggregates(
147 &self,
148 col_type: ColumnType,
149 ) -> Vec<crate::proto::WindowAggregateArgs> {
150 self.window_aggregates
151 .get(&(col_type as u32))
152 .map(|x| x.options.clone())
153 .unwrap_or_default()
154 }
155
156 pub fn has_window_aggregates(&self) -> bool {
159 self.window_aggregates
160 .values()
161 .any(|x| !x.options.is_empty())
162 }
163}
164
165type BoxFn<I, O> = Box<dyn Fn(I) -> O + Send + Sync + 'static>;
166type Box2Fn<I, J, O> = Box<dyn Fn(I, J) -> O + Send + Sync + 'static>;
167
168type Subscriptions<C> = Arc<RwLock<HashMap<u32, C>>>;
169type OnErrorCallback =
170 Box2Fn<ClientError, Option<ReconnectCallback>, BoxFuture<'static, Result<(), ClientError>>>;
171
172type OnceCallback = Box<dyn FnOnce(Response) -> ClientResult<()> + Send + Sync + 'static>;
173type SendCallback = Arc<
174 dyn for<'a> Fn(&'a Request) -> BoxFuture<'a, Result<(), Box<dyn Error + Send + Sync>>>
175 + Send
176 + Sync
177 + 'static,
178>;
179
180pub trait ClientHandler: Clone + Send + Sync + 'static {
182 fn send_request(
183 &self,
184 msg: Vec<u8>,
185 ) -> impl Future<Output = Result<(), Box<dyn Error + Send + Sync>>> + Send;
186}
187
188mod name_registry {
189 use std::collections::HashSet;
190 use std::sync::{Arc, LazyLock, Mutex};
191
192 use crate::ClientError;
193 use crate::view::ClientResult;
194
195 static CLIENT_ID_GEN: LazyLock<Arc<Mutex<u32>>> = LazyLock::new(Arc::default);
196 static REGISTERED_CLIENTS: LazyLock<Arc<Mutex<HashSet<String>>>> = LazyLock::new(Arc::default);
197
198 pub(crate) fn generate_name(name: Option<&str>) -> ClientResult<String> {
199 if let Some(name) = name {
200 if let Some(name) = REGISTERED_CLIENTS
201 .lock()
202 .map_err(ClientError::from)?
203 .get(name)
204 {
205 Err(ClientError::DuplicateNameError(name.to_owned()))
206 } else {
207 Ok(name.to_owned())
208 }
209 } else {
210 let mut guard = CLIENT_ID_GEN.lock()?;
211 *guard += 1;
212 Ok(format!("client-{guard}"))
213 }
214 }
215}
216
217#[derive(Clone)]
224#[allow(clippy::type_complexity)]
225pub struct ReconnectCallback(
226 Arc<dyn Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync>,
227);
228
229impl Deref for ReconnectCallback {
230 type Target = dyn Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync;
231
232 fn deref(&self) -> &Self::Target {
233 &*self.0
234 }
235}
236
237impl ReconnectCallback {
238 pub fn new(
239 f: impl Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync + 'static,
240 ) -> Self {
241 ReconnectCallback(Arc::new(f))
242 }
243}
244
245#[derive(Clone)]
249pub struct Client {
250 name: Arc<String>,
251 features: Arc<Mutex<Option<Features>>>,
252 send: SendCallback,
253 id_gen: IDGen,
254 subscriptions_errors: Subscriptions<OnErrorCallback>,
255 subscriptions_once: Subscriptions<OnceCallback>,
256 subscriptions: Subscriptions<BoxFn<Response, BoxFuture<'static, Result<(), ClientError>>>>,
257}
258
259impl PartialEq for Client {
260 fn eq(&self, other: &Self) -> bool {
261 self.name == other.name
262 }
263}
264
265impl std::fmt::Debug for Client {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 f.debug_struct("Client").finish()
268 }
269}
270
271impl Client {
272 pub fn new_with_callback<T, U>(name: Option<&str>, send_request: T) -> ClientResult<Self>
275 where
276 T: Fn(Vec<u8>) -> U + 'static + Sync + Send,
277 U: Future<Output = Result<(), Box<dyn Error + Send + Sync>>> + Send + 'static,
278 {
279 let name = name_registry::generate_name(name)?;
280 let send_request = Arc::new(send_request);
281 let send: SendCallback = Arc::new(move |req| {
282 let mut bytes: Vec<u8> = Vec::new();
283 req.encode(&mut bytes).unwrap();
284 let send_request = send_request.clone();
285 Box::pin(async move { send_request(bytes).await })
286 });
287
288 Ok(Client {
289 name: Arc::new(name),
290 features: Arc::default(),
291 id_gen: IDGen::default(),
292 send,
293 subscriptions: Subscriptions::default(),
294 subscriptions_errors: Arc::default(),
295 subscriptions_once: Arc::default(),
296 })
297 }
298
299 pub fn new<T>(name: Option<&str>, client_handler: T) -> ClientResult<Self>
301 where
302 T: ClientHandler + 'static + Sync + Send,
303 {
304 Self::new_with_callback(
305 name,
306 asyncfn!(client_handler, async move |req| {
307 client_handler.send_request(req).await
308 }),
309 )
310 }
311
312 pub fn get_name(&self) -> &'_ str {
313 self.name.as_str()
314 }
315
316 pub async fn handle_response<'a>(&'a self, msg: &'a [u8]) -> ClientResult<bool> {
323 let msg = Response::decode(msg)?;
324 tracing::debug!("RECV {}", msg);
325 let mut wr = self.subscriptions_once.write().await;
326 if let Some(handler) = (*wr).remove(&msg.msg_id) {
327 drop(wr);
328 handler(msg)?;
329 return Ok(true);
330 } else if let Some(handler) = self.subscriptions.try_read().unwrap().get(&msg.msg_id) {
331 drop(wr);
332 handler(msg).await?;
333 return Ok(true);
334 }
335
336 if let Response {
337 client_resp: Some(ClientResp::ServerError(ServerError { message, .. })),
338 ..
339 } = &msg
340 {
341 tracing::error!("{}", message);
342 } else {
343 tracing::debug!("Received unsolicited server response: {}", msg);
344 }
345
346 Ok(false)
347 }
348
349 pub async fn handle_error<T, U>(
351 &self,
352 message: ClientError,
353 reconnect: Option<T>,
354 ) -> ClientResult<()>
355 where
356 T: Fn() -> U + Clone + Send + Sync + 'static,
357 U: Future<Output = ClientResult<()>>,
358 {
359 let subs = self.subscriptions_errors.read().await;
360 let tasks = join_all(subs.values().map(|callback| {
361 callback(
362 message.clone(),
363 reconnect.clone().map(move |f| {
364 ReconnectCallback(Arc::new(move || {
365 clone!(f);
366 Box::pin(async move { Ok(f().await?) }) as LocalBoxFuture<'static, _>
367 }))
368 }),
369 )
370 }));
371
372 tasks.await.into_iter().collect::<Result<(), _>>()?;
373 self.close_and_error_subscriptions(&message).await
374 }
375
376 async fn close_and_error_subscriptions(&self, message: &ClientError) -> ClientResult<()> {
381 let synthetic_error = |msg_id| Response {
382 msg_id,
383 entity_id: "".to_string(),
384 client_resp: Some(ClientResp::ServerError(ServerError {
385 message: format!("{message}"),
386 status_code: 2,
387 })),
388 };
389
390 self.subscriptions.write().await.clear();
391 let callbacks_once = self
392 .subscriptions_once
393 .write()
394 .await
395 .drain()
396 .collect::<Vec<_>>();
397
398 callbacks_once
399 .into_iter()
400 .try_for_each(|(msg_id, f)| f(synthetic_error(msg_id)))
401 }
402
403 pub async fn on_error<T, U, V>(&self, on_error: T) -> ClientResult<u32>
404 where
405 T: Fn(ClientError, Option<ReconnectCallback>) -> U + Clone + Send + Sync + 'static,
406 U: Future<Output = V> + Send + 'static,
407 V: Into<Result<(), ClientError>> + Sync + 'static,
408 {
409 let id = self.gen_id();
410 let callback = asyncfn!(on_error, async move |x, y| on_error(x, y).await.into());
411 self.subscriptions_errors
412 .write()
413 .await
414 .insert(id, Box::new(move |x, y| Box::pin(callback(x, y))));
415
416 Ok(id)
417 }
418
419 pub(crate) fn gen_id(&self) -> u32 {
421 self.id_gen.next()
422 }
423
424 pub(crate) async fn unsubscribe(&self, update_id: u32) -> ClientResult<()> {
425 let callback = self
426 .subscriptions
427 .write()
428 .await
429 .remove(&update_id)
430 .ok_or(ClientError::Unknown("remove_update".to_string()))?;
431
432 drop(callback);
433 Ok(())
434 }
435
436 pub(crate) async fn subscribe_once(
438 &self,
439 msg: &Request,
440 on_update: Box<dyn FnOnce(Response) -> ClientResult<()> + Send + Sync + 'static>,
441 ) -> ClientResult<()> {
442 self.subscriptions_once
443 .write()
444 .await
445 .insert(msg.msg_id, on_update);
446
447 tracing::debug!("SEND {}", msg);
448 if let Err(e) = (self.send)(msg).await {
449 self.subscriptions_once.write().await.remove(&msg.msg_id);
450 Err(ClientError::Unknown(e.to_string()))
451 } else {
452 Ok(())
453 }
454 }
455
456 pub(crate) async fn subscribe<T, U>(&self, msg: &Request, on_update: T) -> ClientResult<()>
457 where
458 T: Fn(Response) -> U + Send + Sync + 'static,
459 U: Future<Output = Result<(), ClientError>> + Send + 'static,
460 {
461 self.subscriptions
462 .write()
463 .await
464 .insert(msg.msg_id, Box::new(move |x| Box::pin(on_update(x))));
465
466 tracing::debug!("SEND {}", msg);
467 if let Err(e) = (self.send)(msg).await {
468 self.subscriptions.write().await.remove(&msg.msg_id);
469 Err(ClientError::Unknown(e.to_string()))
470 } else {
471 Ok(())
472 }
473 }
474
475 pub(crate) async fn oneshot(&self, req: &Request) -> ClientResult<ClientResp> {
478 let (sender, receiver) = futures::channel::oneshot::channel::<ClientResp>();
479 let on_update = Box::new(move |res: Response| {
480 sender.send(res.client_resp.unwrap()).map_err(|x| x.into())
481 });
482
483 self.subscribe_once(req, on_update).await?;
484 receiver
485 .await
486 .map_err(|_| ClientError::Unknown(format!("Internal error for req {req}")))
487 }
488
489 pub(crate) async fn get_features(&self) -> ClientResult<Features> {
490 let mut guard = self.features.lock().await;
491 let features = if let Some(features) = &*guard {
492 features.clone()
493 } else {
494 let msg = Request {
495 msg_id: self.gen_id(),
496 entity_id: "".to_owned(),
497 client_req: Some(ClientReq::GetFeaturesReq(GetFeaturesReq {})),
498 };
499
500 let features = Features(Arc::new(match self.oneshot(&msg).await? {
501 ClientResp::GetFeaturesResp(features) => Ok(features),
502 resp => Err(resp),
503 }?));
504
505 *guard = Some(features.clone());
506 features
507 };
508
509 Ok(features)
510 }
511
512 pub async fn table(&self, input: TableData, options: TableInitOptions) -> ClientResult<Table> {
568 let entity_id = match options.name.clone() {
569 Some(x) => x.to_owned(),
570 None => randid(),
571 };
572
573 if let TableData::View(view) = &input {
574 let window = ViewWindow::default();
575 let arrow = view.to_arrow(window).await?;
576 let mut table = self
577 .crate_table_inner(UpdateData::Arrow(arrow).into(), options.into(), entity_id)
578 .await?;
579
580 let table_ = table.clone();
581 let callback = asyncfn!(table_, update, async move |update: OnUpdateData| {
582 let update = UpdateData::Arrow(update.delta.expect("Malformed message").into());
583 let options = crate::UpdateOptions::default();
584 table_.update(update, options).await.unwrap_or_log();
585 });
586
587 let options = OnUpdateOptions {
588 mode: Some(OnUpdateMode::Row),
589 };
590
591 let on_update_token = view.on_update(callback, options).await?;
592 table.view_update_token = Some(on_update_token);
593 Ok(table)
594 } else {
595 self.crate_table_inner(input, options.into(), entity_id)
596 .await
597 }
598 }
599
600 async fn crate_table_inner(
601 &self,
602 input: TableData,
603 options: TableOptions,
604 entity_id: String,
605 ) -> ClientResult<Table> {
606 let msg = Request {
607 msg_id: self.gen_id(),
608 entity_id: entity_id.clone(),
609 client_req: Some(ClientReq::MakeTableReq(MakeTableReq {
610 data: Some(input.into()),
611 options: Some(options.clone().try_into()?),
612 })),
613 };
614
615 let client = self.clone();
616 match self.oneshot(&msg).await? {
617 ClientResp::MakeTableResp(_) => Ok(Table::new(entity_id, client, options)),
618 resp => Err(resp.into()),
619 }
620 }
621
622 pub async fn join(
634 &self,
635 left: TableRef,
636 right: TableRef,
637 on: &str,
638 options: JoinOptions,
639 ) -> ClientResult<Table> {
640 let entity_id = options.name.unwrap_or_else(randid);
641 let join_type: JoinType = options.join_type.unwrap_or_default();
642 let right_on_column = options.right_on.unwrap_or_default();
643 let msg = Request {
644 msg_id: self.gen_id(),
645 entity_id: entity_id.clone(),
646 client_req: Some(ClientReq::MakeJoinTableReq(MakeJoinTableReq {
647 left_table_id: left.table_name().to_owned(),
648 right_table_id: right.table_name().to_owned(),
649 on_column: on.to_owned(),
650 join_type: join_type.into(),
651 right_on_column,
652 })),
653 };
654
655 let client = self.clone();
656 match self.oneshot(&msg).await? {
657 ClientResp::MakeJoinTableResp(_) => Ok(Table::new(entity_id, client, TableOptions {
658 index: Some(on.to_owned()),
659 limit: None,
660 page_to_disk: None,
661 list_flatten: None,
662 })),
663 resp => Err(resp.into()),
664 }
665 }
666
667 async fn get_table_infos(&self) -> ClientResult<Vec<HostedTable>> {
668 let msg = Request {
669 msg_id: self.gen_id(),
670 entity_id: "".to_owned(),
671 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
672 subscribe: false,
673 })),
674 };
675
676 match self.oneshot(&msg).await? {
677 ClientResp::GetHostedTablesResp(GetHostedTablesResp { table_infos }) => Ok(table_infos),
678 resp => Err(resp.into()),
679 }
680 }
681
682 pub async fn open_table(&self, entity_id: String) -> ClientResult<Table> {
699 let infos = self.get_table_infos().await?;
700
701 if let Some(info) = infos.into_iter().find(|i| i.entity_id == entity_id) {
703 let options = TableOptions {
704 index: info.index,
705 limit: info.limit,
706 page_to_disk: None,
707 list_flatten: None,
708 };
709
710 let client = self.clone();
711 Ok(Table::new(entity_id, client, options))
712 } else {
713 Err(ClientError::Unknown(format!(
714 "Unknown table \"{}\"",
715 entity_id
716 )))
717 }
718 }
719
720 pub async fn get_hosted_table_names(&self) -> ClientResult<Vec<String>> {
737 let msg = Request {
738 msg_id: self.gen_id(),
739 entity_id: "".to_owned(),
740 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
741 subscribe: false,
742 })),
743 };
744
745 match self.oneshot(&msg).await? {
746 ClientResp::GetHostedTablesResp(GetHostedTablesResp { table_infos }) => {
747 Ok(table_infos.into_iter().map(|i| i.entity_id).collect())
748 },
749 resp => Err(resp.into()),
750 }
751 }
752
753 pub async fn on_hosted_tables_update<T, U>(&self, on_update: T) -> ClientResult<u32>
757 where
758 T: Fn() -> U + Send + Sync + 'static,
759 U: Future<Output = ()> + Send + 'static,
760 {
761 let on_update = Arc::new(on_update);
762 let callback = asyncfn!(on_update, async move |resp: Response| {
763 match resp.client_resp {
764 Some(ClientResp::GetHostedTablesResp(_)) | None => {
765 on_update().await;
766 Ok(())
767 },
768 resp => Err(resp.into()),
769 }
770 });
771
772 let msg = Request {
773 msg_id: self.gen_id(),
774 entity_id: "".to_owned(),
775 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
776 subscribe: true,
777 })),
778 };
779
780 self.subscribe(&msg, callback).await?;
781 Ok(msg.msg_id)
782 }
783
784 pub async fn remove_hosted_tables_update(&self, update_id: u32) -> ClientResult<()> {
787 let msg = Request {
788 msg_id: self.gen_id(),
789 entity_id: "".to_owned(),
790 client_req: Some(ClientReq::RemoveHostedTablesUpdateReq(
791 RemoveHostedTablesUpdateReq { id: update_id },
792 )),
793 };
794
795 self.unsubscribe(update_id).await?;
796 match self.oneshot(&msg).await? {
797 ClientResp::RemoveHostedTablesUpdateResp(_) => Ok(()),
798 resp => Err(resp.into()),
799 }
800 }
801
802 pub async fn system_info(&self) -> ClientResult<SystemInfo> {
806 let msg = Request {
807 msg_id: self.gen_id(),
808 entity_id: "".to_string(),
809 client_req: Some(ClientReq::ServerSystemInfoReq(ServerSystemInfoReq {})),
810 };
811
812 match self.oneshot(&msg).await? {
813 ClientResp::ServerSystemInfoResp(resp) => {
814 #[cfg(not(target_family = "wasm"))]
815 let timestamp = Some(
816 std::time::SystemTime::now()
817 .duration_since(std::time::UNIX_EPOCH)?
818 .as_millis() as u64,
819 );
820
821 #[cfg(target_family = "wasm")]
822 let timestamp = None;
823
824 #[cfg(feature = "talc-allocator")]
825 let (client_used, client_heap) = {
826 let (client_used, client_heap) = crate::utils::get_used();
827 (Some(client_used as u64), Some(client_heap as u64))
828 };
829
830 #[cfg(not(feature = "talc-allocator"))]
831 let (client_used, client_heap) = (None, None);
832
833 let info = SystemInfo {
834 heap_size: resp.heap_size,
835 used_size: resp.used_size,
836 cpu_time: resp.cpu_time,
837 cpu_time_epoch: resp.cpu_time_epoch,
838 timestamp,
839 client_heap,
840 client_used,
841 };
842
843 Ok(info)
844 },
845 resp => Err(resp.into()),
846 }
847 }
848}