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::config::GroupRollupMode;
26use crate::proto::request::ClientReq;
27use crate::proto::response::ClientResp;
28use crate::proto::{
29 ColumnType, GetFeaturesReq, GetFeaturesResp, GetHostedTablesReq, GetHostedTablesResp,
30 HostedTable, JoinType, MakeJoinTableReq, MakeTableReq, RemoveHostedTablesUpdateReq, Request,
31 Response, ServerError, ServerSystemInfoReq,
32};
33use crate::table::{JoinOptions, Table, TableIndex, TableInitOptions, TableOptions, ViewBinding};
34use crate::table_data::{TableData, UpdateData};
35use crate::table_ref::TableRef;
36use crate::utils::*;
37use crate::view::{OnRemoveData, OnUpdateData, ViewWindow};
38use crate::{OnUpdateMode, OnUpdateOptions, asyncfn, clone};
39
40#[derive(Clone, Debug, Serialize, Deserialize, TS)]
42pub struct SystemInfo<T = u64> {
43 pub heap_size: T,
45
46 pub used_size: T,
48
49 pub cpu_time: u32,
54
55 pub cpu_time_epoch: u32,
57
58 pub timestamp: Option<T>,
62
63 pub client_heap: Option<T>,
66
67 pub client_used: Option<T>,
70}
71
72impl<U: Copy + 'static> SystemInfo<U> {
73 pub fn cast<T: Copy + 'static>(&self) -> SystemInfo<T>
76 where
77 U: num_traits::AsPrimitive<T>,
78 {
79 SystemInfo {
80 heap_size: self.heap_size.as_(),
81 used_size: self.used_size.as_(),
82 cpu_time: self.cpu_time,
83 cpu_time_epoch: self.cpu_time_epoch,
84 timestamp: self.timestamp.map(|x| x.as_()),
85 client_heap: self.client_heap.map(|x| x.as_()),
86 client_used: self.client_used.map(|x| x.as_()),
87 }
88 }
89}
90
91#[derive(Clone, Debug, Default, PartialEq)]
94pub struct Features(Arc<GetFeaturesResp>);
95
96impl Features {
97 pub fn get_group_rollup_modes(&self) -> Vec<crate::config::GroupRollupMode> {
98 self.group_rollup_mode
99 .iter()
100 .map(|x| {
101 crate::config::GroupRollupMode::from(
102 crate::proto::GroupRollupMode::try_from(*x).unwrap(),
103 )
104 })
105 .collect::<Vec<_>>()
106 }
107
108 pub fn get_split_rollup_modes(&self) -> Vec<crate::config::SplitRollupMode> {
113 if self.split_rollup_mode.is_empty() {
114 return vec![crate::config::SplitRollupMode::Flat];
115 }
116
117 self.split_rollup_mode
118 .iter()
119 .map(|x| {
120 crate::config::SplitRollupMode::from(
121 crate::proto::SplitRollupMode::try_from(*x).unwrap(),
122 )
123 })
124 .collect::<Vec<_>>()
125 }
126}
127
128impl Deref for Features {
129 type Target = GetFeaturesResp;
130
131 fn deref(&self) -> &Self::Target {
132 &self.0
133 }
134}
135
136impl GetFeaturesResp {
137 pub fn default_op(&self, col_type: ColumnType) -> Option<&str> {
138 self.filter_ops
139 .get(&(col_type as u32))?
140 .options
141 .first()
142 .map(|x| x.as_str())
143 }
144
145 pub fn get_window_aggregates(
148 &self,
149 col_type: ColumnType,
150 ) -> Vec<crate::proto::WindowAggregateArgs> {
151 self.window_aggregates
152 .get(&(col_type as u32))
153 .map(|x| x.options.clone())
154 .unwrap_or_default()
155 }
156
157 pub fn has_window_aggregates(&self) -> bool {
160 self.window_aggregates
161 .values()
162 .any(|x| !x.options.is_empty())
163 }
164}
165
166type Box2Fn<I, J, O> = Box<dyn Fn(I, J) -> O + Send + Sync + 'static>;
167
168type Subscriptions<C> = Arc<RwLock<HashMap<u32, C>>>;
169type UpdateCallback =
170 Arc<dyn Fn(Response) -> BoxFuture<'static, Result<(), ClientError>> + Send + Sync + 'static>;
171type OnErrorCallback =
172 Box2Fn<ClientError, Option<ReconnectCallback>, BoxFuture<'static, Result<(), ClientError>>>;
173
174type OnceCallback = Box<dyn FnOnce(Response) -> ClientResult<()> + Send + Sync + 'static>;
175type SendCallback = Arc<
176 dyn for<'a> Fn(&'a Request) -> BoxFuture<'a, Result<(), Box<dyn Error + Send + Sync>>>
177 + Send
178 + Sync
179 + 'static,
180>;
181
182pub trait ClientHandler: Clone + Send + Sync + 'static {
184 fn send_request(
185 &self,
186 msg: Vec<u8>,
187 ) -> impl Future<Output = Result<(), Box<dyn Error + Send + Sync>>> + Send;
188}
189
190mod name_registry {
191 use std::collections::HashSet;
192 use std::sync::{Arc, LazyLock, Mutex};
193
194 use crate::ClientError;
195 use crate::view::ClientResult;
196
197 static CLIENT_ID_GEN: LazyLock<Arc<Mutex<u32>>> = LazyLock::new(Arc::default);
198 static REGISTERED_CLIENTS: LazyLock<Arc<Mutex<HashSet<String>>>> = LazyLock::new(Arc::default);
199
200 pub(crate) fn generate_name(name: Option<&str>) -> ClientResult<String> {
201 if let Some(name) = name {
202 if let Some(name) = REGISTERED_CLIENTS
203 .lock()
204 .map_err(ClientError::from)?
205 .get(name)
206 {
207 Err(ClientError::DuplicateNameError(name.to_owned()))
208 } else {
209 Ok(name.to_owned())
210 }
211 } else {
212 let mut guard = CLIENT_ID_GEN.lock()?;
213 *guard += 1;
214 Ok(format!("client-{guard}"))
215 }
216 }
217}
218
219#[derive(Clone)]
226#[allow(clippy::type_complexity)]
227pub struct ReconnectCallback(
228 Arc<dyn Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync>,
229);
230
231impl Deref for ReconnectCallback {
232 type Target = dyn Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync;
233
234 fn deref(&self) -> &Self::Target {
235 &*self.0
236 }
237}
238
239impl ReconnectCallback {
240 pub fn new(
241 f: impl Fn() -> LocalBoxFuture<'static, Result<(), Box<dyn Error>>> + Send + Sync + 'static,
242 ) -> Self {
243 ReconnectCallback(Arc::new(f))
244 }
245}
246
247#[derive(Clone)]
251pub struct Client {
252 name: Arc<String>,
253 features: Arc<Mutex<Option<Features>>>,
254 send: SendCallback,
255 id_gen: IDGen,
256 subscriptions_errors: Subscriptions<OnErrorCallback>,
257 subscriptions_once: Subscriptions<OnceCallback>,
258 subscriptions: Subscriptions<UpdateCallback>,
259}
260
261impl PartialEq for Client {
262 fn eq(&self, other: &Self) -> bool {
263 self.name == other.name
264 }
265}
266
267impl std::fmt::Debug for Client {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 f.debug_struct("Client").finish()
270 }
271}
272
273impl Client {
274 pub fn new_with_callback<T, U>(name: Option<&str>, send_request: T) -> ClientResult<Self>
277 where
278 T: Fn(Vec<u8>) -> U + 'static + Sync + Send,
279 U: Future<Output = Result<(), Box<dyn Error + Send + Sync>>> + Send + 'static,
280 {
281 let name = name_registry::generate_name(name)?;
282 let send_request = Arc::new(send_request);
283 let send: SendCallback = Arc::new(move |req| {
284 let mut bytes: Vec<u8> = Vec::new();
285 req.encode(&mut bytes).unwrap();
286 let send_request = send_request.clone();
287 Box::pin(async move { send_request(bytes).await })
288 });
289
290 Ok(Client {
291 name: Arc::new(name),
292 features: Arc::default(),
293 id_gen: IDGen::default(),
294 send,
295 subscriptions: Subscriptions::default(),
296 subscriptions_errors: Arc::default(),
297 subscriptions_once: Arc::default(),
298 })
299 }
300
301 pub fn new<T>(name: Option<&str>, client_handler: T) -> ClientResult<Self>
303 where
304 T: ClientHandler + 'static + Sync + Send,
305 {
306 Self::new_with_callback(
307 name,
308 asyncfn!(client_handler, async move |req| {
309 client_handler.send_request(req).await
310 }),
311 )
312 }
313
314 pub fn get_name(&self) -> &'_ str {
315 self.name.as_str()
316 }
317
318 pub async fn handle_response<'a>(&'a self, msg: &'a [u8]) -> ClientResult<bool> {
325 let msg = Response::decode(msg)?;
326 tracing::debug!("RECV {}", msg);
327 let mut wr = self.subscriptions_once.write().await;
328 if let Some(handler) = (*wr).remove(&msg.msg_id) {
329 drop(wr);
330 handler(msg)?;
331 return Ok(true);
332 }
333
334 let handler = self.subscriptions.read().await.get(&msg.msg_id).cloned();
335 drop(wr);
336 if let Some(handler) = handler {
337 handler(msg).await?;
338 return Ok(true);
339 }
340
341 if let Response {
342 client_resp: Some(ClientResp::ServerError(ServerError { message, .. })),
343 ..
344 } = &msg
345 {
346 tracing::error!("{}", message);
347 } else {
348 tracing::debug!("Received unsolicited server response: {}", msg);
349 }
350
351 Ok(false)
352 }
353
354 pub async fn handle_error<T, U>(
356 &self,
357 message: ClientError,
358 reconnect: Option<T>,
359 ) -> ClientResult<()>
360 where
361 T: Fn() -> U + Clone + Send + Sync + 'static,
362 U: Future<Output = ClientResult<()>>,
363 {
364 let subs = self.subscriptions_errors.read().await;
365 let tasks = join_all(subs.values().map(|callback| {
366 callback(
367 message.clone(),
368 reconnect.clone().map(move |f| {
369 ReconnectCallback(Arc::new(move || {
370 clone!(f);
371 Box::pin(async move { Ok(f().await?) }) as LocalBoxFuture<'static, _>
372 }))
373 }),
374 )
375 }));
376
377 tasks.await.into_iter().collect::<Result<(), _>>()?;
378 self.close_and_error_subscriptions(&message).await
379 }
380
381 async fn close_and_error_subscriptions(&self, message: &ClientError) -> ClientResult<()> {
386 let synthetic_error = |msg_id| Response {
387 msg_id,
388 entity_id: "".to_string(),
389 client_resp: Some(ClientResp::ServerError(ServerError {
390 message: format!("{message}"),
391 status_code: 2,
392 })),
393 };
394
395 self.subscriptions.write().await.clear();
396 let callbacks_once = self
397 .subscriptions_once
398 .write()
399 .await
400 .drain()
401 .collect::<Vec<_>>();
402
403 callbacks_once
404 .into_iter()
405 .try_for_each(|(msg_id, f)| f(synthetic_error(msg_id)))
406 }
407
408 pub async fn on_error<T, U, V>(&self, on_error: T) -> ClientResult<u32>
409 where
410 T: Fn(ClientError, Option<ReconnectCallback>) -> U + Clone + Send + Sync + 'static,
411 U: Future<Output = V> + Send + 'static,
412 V: Into<Result<(), ClientError>> + Sync + 'static,
413 {
414 let id = self.gen_id();
415 let callback = asyncfn!(on_error, async move |x, y| on_error(x, y).await.into());
416 self.subscriptions_errors
417 .write()
418 .await
419 .insert(id, Box::new(move |x, y| Box::pin(callback(x, y))));
420
421 Ok(id)
422 }
423
424 pub(crate) fn gen_id(&self) -> u32 {
426 self.id_gen.next()
427 }
428
429 pub(crate) async fn unsubscribe(&self, update_id: u32) -> ClientResult<()> {
430 let callback = self
431 .subscriptions
432 .write()
433 .await
434 .remove(&update_id)
435 .ok_or(ClientError::Unknown("remove_update".to_string()))?;
436
437 drop(callback);
438 Ok(())
439 }
440
441 pub(crate) async fn subscribe_once(
443 &self,
444 msg: &Request,
445 on_update: Box<dyn FnOnce(Response) -> ClientResult<()> + Send + Sync + 'static>,
446 ) -> ClientResult<()> {
447 self.subscriptions_once
448 .write()
449 .await
450 .insert(msg.msg_id, on_update);
451
452 tracing::debug!("SEND {}", msg);
453 if let Err(e) = (self.send)(msg).await {
454 self.subscriptions_once.write().await.remove(&msg.msg_id);
455 Err(ClientError::Unknown(e.to_string()))
456 } else {
457 Ok(())
458 }
459 }
460
461 pub(crate) async fn subscribe<T, U>(&self, msg: &Request, on_update: T) -> ClientResult<()>
462 where
463 T: Fn(Response) -> U + Send + Sync + 'static,
464 U: Future<Output = Result<(), ClientError>> + Send + 'static,
465 {
466 self.subscriptions
467 .write()
468 .await
469 .insert(msg.msg_id, Arc::new(move |x| Box::pin(on_update(x))));
470
471 tracing::debug!("SEND {}", msg);
472 if let Err(e) = (self.send)(msg).await {
473 self.subscriptions.write().await.remove(&msg.msg_id);
474 Err(ClientError::Unknown(e.to_string()))
475 } else {
476 Ok(())
477 }
478 }
479
480 pub(crate) async fn oneshot(&self, req: &Request) -> ClientResult<ClientResp> {
483 let (sender, receiver) = futures::channel::oneshot::channel::<ClientResp>();
484 let on_update = Box::new(move |res: Response| {
485 sender.send(res.client_resp.unwrap()).map_err(|x| x.into())
486 });
487
488 self.subscribe_once(req, on_update).await?;
489 receiver
490 .await
491 .map_err(|_| ClientError::Unknown(format!("Internal error for req {req}")))
492 }
493
494 pub(crate) async fn get_features(&self) -> ClientResult<Features> {
495 let mut guard = self.features.lock().await;
496 let features = if let Some(features) = &*guard {
497 features.clone()
498 } else {
499 let msg = Request {
500 msg_id: self.gen_id(),
501 entity_id: "".to_owned(),
502 client_req: Some(ClientReq::GetFeaturesReq(GetFeaturesReq {})),
503 };
504
505 let features = Features(Arc::new(match self.oneshot(&msg).await? {
506 ClientResp::GetFeaturesResp(features) => Ok(features),
507 resp => Err(resp),
508 }?));
509
510 *guard = Some(features.clone());
511 features
512 };
513
514 Ok(features)
515 }
516
517 pub async fn table(&self, input: TableData, options: TableInitOptions) -> ClientResult<Table> {
580 let mut options = options;
581 if let Some(index) = options.index.take() {
582 options.index = Some(index.normalize()?);
583 }
584
585 let entity_id = match options.name.clone() {
586 Some(x) => x.to_owned(),
587 None => randid(),
588 };
589
590 if let TableData::View(view) = &input {
591 let request_options: TableOptions = options.clone().into();
592 let mut options = options;
593 let source_index = view.source.as_ref().and_then(|x| x.options.index.clone());
594 let config = view.get_config().await?;
595 let is_pivoted = !config.group_by.is_empty();
596 let keyed_by_group = options.index.is_none()
597 && is_pivoted
598 && config.split_by.is_empty()
599 && config.group_rollup_mode == GroupRollupMode::Flat;
600 if let (None, Some(index)) = (&options.index, &source_index) {
601 let is_flat = config.group_by.is_empty() && config.split_by.is_empty();
602 let has_index = index
603 .columns()
604 .iter()
605 .all(|column| config.columns.iter().flatten().any(|x| x == column));
606
607 if is_flat && has_index {
608 options.index = Some(index.clone());
609 }
610 }
611
612 if options.index.is_none() && options.limit.is_none() {
613 options.limit = view.source.as_ref().and_then(|x| x.options.limit);
614 }
615
616 if view.client() == self {
617 let data = TableData::View(view.clone());
618 self.crate_table_inner(data, request_options, entity_id.clone())
619 .await?;
620
621 let has_keys = options
622 .schema
623 .as_ref()
624 .is_none_or(|schema| config.group_by.iter().all(|x| schema.contains_key(x)));
625
626 if keyed_by_group && has_keys {
627 options.index = TableIndex::from_columns(config.group_by.clone());
628 options.limit = None;
629 }
630
631 return Ok(Table::new(entity_id, self.clone(), options.into()));
632 }
633
634 if options.schema.is_some() {
635 return Err(ClientError::BadTableOptions);
636 }
637
638 if keyed_by_group {
639 options.index = TableIndex::from_columns(config.group_by.clone());
640 options.limit = None;
641 }
642
643 let window = ViewWindow::default();
644 let arrow = view.to_arrow(window).await?;
645 let mut table = self
646 .crate_table_inner(UpdateData::Arrow(arrow).into(), options.into(), entity_id)
647 .await?;
648
649 let table_ = table.clone();
650 let callback = asyncfn!(table_, update, async move |update: OnUpdateData| {
651 let data = UpdateData::Arrow(update.delta.expect("Malformed message").into());
652 if update.reset {
653 table_.replace(data).await.unwrap_or_log();
654 } else {
655 let options = crate::UpdateOptions::default();
656 table_.update(data, options).await.unwrap_or_log();
657 }
658 });
659
660 let options = OnUpdateOptions {
661 mode: Some(OnUpdateMode::Row),
662 };
663
664 let update_token = view.on_update(callback, options).await?;
665 let replica_index = table.get_index();
666 let mirrors_removes =
667 replica_index.is_some() && (is_pivoted || source_index == replica_index);
668
669 let remove_token = if mirrors_removes {
670 let table_ = table.clone();
671 let callback = asyncfn!(table_, async move |removed: OnRemoveData| {
672 if let Some(indices) = removed.indices.as_ref().filter(|x| !x.is_empty()) {
673 let indices = UpdateData::Arrow(indices.clone().into());
674 table_.remove(indices).await.unwrap_or_log();
675 }
676 });
677
678 Some(view.on_remove(callback).await?)
679 } else {
680 None
681 };
682
683 table.view_binding = Some(ViewBinding {
684 view: view.clone(),
685 update_token,
686 remove_token,
687 });
688
689 Ok(table)
690 } else {
691 self.crate_table_inner(input, options.into(), entity_id)
692 .await
693 }
694 }
695
696 async fn crate_table_inner(
697 &self,
698 input: TableData,
699 options: TableOptions,
700 entity_id: String,
701 ) -> ClientResult<Table> {
702 let msg = Request {
703 msg_id: self.gen_id(),
704 entity_id: entity_id.clone(),
705 client_req: Some(ClientReq::MakeTableReq(MakeTableReq {
706 data: Some(input.into()),
707 options: Some(options.clone().try_into()?),
708 })),
709 };
710
711 let client = self.clone();
712 match self.oneshot(&msg).await? {
713 ClientResp::MakeTableResp(_) => Ok(Table::new(entity_id, client, options)),
714 resp => Err(resp.into()),
715 }
716 }
717
718 pub async fn join(
730 &self,
731 left: TableRef,
732 right: TableRef,
733 on: &str,
734 options: JoinOptions,
735 ) -> ClientResult<Table> {
736 let entity_id = options.name.unwrap_or_else(randid);
737 let join_type: JoinType = options.join_type.unwrap_or_default();
738 let right_on_column = options.right_on.unwrap_or_default();
739 let msg = Request {
740 msg_id: self.gen_id(),
741 entity_id: entity_id.clone(),
742 client_req: Some(ClientReq::MakeJoinTableReq(MakeJoinTableReq {
743 left_table_id: left.table_name().to_owned(),
744 right_table_id: right.table_name().to_owned(),
745 on_column: on.to_owned(),
746 join_type: join_type.into(),
747 right_on_column,
748 })),
749 };
750
751 let client = self.clone();
752 match self.oneshot(&msg).await? {
753 ClientResp::MakeJoinTableResp(_) => Ok(Table::new(entity_id, client, TableOptions {
754 index: Some(TableIndex::Column(on.to_owned())),
755 limit: None,
756 page_to_disk: None,
757 list_flatten: None,
758 struct_flatten: None,
759 map_flatten: None,
760 schema: None,
761 })),
762 resp => Err(resp.into()),
763 }
764 }
765
766 async fn get_table_infos(&self) -> ClientResult<Vec<HostedTable>> {
767 let msg = Request {
768 msg_id: self.gen_id(),
769 entity_id: "".to_owned(),
770 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
771 subscribe: false,
772 })),
773 };
774
775 match self.oneshot(&msg).await? {
776 ClientResp::GetHostedTablesResp(GetHostedTablesResp { table_infos }) => Ok(table_infos),
777 resp => Err(resp.into()),
778 }
779 }
780
781 pub async fn open_table(&self, entity_id: String) -> ClientResult<Table> {
798 let infos = self.get_table_infos().await?;
799
800 if let Some(info) = infos.into_iter().find(|i| i.entity_id == entity_id) {
802 let options = TableOptions {
803 index: TableIndex::from_columns(info.index),
804 limit: info.limit,
805 page_to_disk: None,
806 list_flatten: None,
807 struct_flatten: None,
808 map_flatten: None,
809 schema: None,
810 };
811
812 let client = self.clone();
813 Ok(Table::new(entity_id, client, options))
814 } else {
815 Err(ClientError::Unknown(format!(
816 "Unknown table \"{}\"",
817 entity_id
818 )))
819 }
820 }
821
822 pub async fn get_hosted_table_names(&self) -> ClientResult<Vec<String>> {
839 let msg = Request {
840 msg_id: self.gen_id(),
841 entity_id: "".to_owned(),
842 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
843 subscribe: false,
844 })),
845 };
846
847 match self.oneshot(&msg).await? {
848 ClientResp::GetHostedTablesResp(GetHostedTablesResp { table_infos }) => {
849 Ok(table_infos.into_iter().map(|i| i.entity_id).collect())
850 },
851 resp => Err(resp.into()),
852 }
853 }
854
855 pub async fn on_hosted_tables_update<T, U>(&self, on_update: T) -> ClientResult<u32>
859 where
860 T: Fn() -> U + Send + Sync + 'static,
861 U: Future<Output = ()> + Send + 'static,
862 {
863 let on_update = Arc::new(on_update);
864 let callback = asyncfn!(on_update, async move |resp: Response| {
865 match resp.client_resp {
866 Some(ClientResp::GetHostedTablesResp(_)) | None => {
867 on_update().await;
868 Ok(())
869 },
870 resp => Err(resp.into()),
871 }
872 });
873
874 let msg = Request {
875 msg_id: self.gen_id(),
876 entity_id: "".to_owned(),
877 client_req: Some(ClientReq::GetHostedTablesReq(GetHostedTablesReq {
878 subscribe: true,
879 })),
880 };
881
882 self.subscribe(&msg, callback).await?;
883 Ok(msg.msg_id)
884 }
885
886 pub async fn remove_hosted_tables_update(&self, update_id: u32) -> ClientResult<()> {
889 let msg = Request {
890 msg_id: self.gen_id(),
891 entity_id: "".to_owned(),
892 client_req: Some(ClientReq::RemoveHostedTablesUpdateReq(
893 RemoveHostedTablesUpdateReq { id: update_id },
894 )),
895 };
896
897 self.unsubscribe(update_id).await?;
898 match self.oneshot(&msg).await? {
899 ClientResp::RemoveHostedTablesUpdateResp(_) => Ok(()),
900 resp => Err(resp.into()),
901 }
902 }
903
904 pub async fn system_info(&self) -> ClientResult<SystemInfo> {
908 let msg = Request {
909 msg_id: self.gen_id(),
910 entity_id: "".to_string(),
911 client_req: Some(ClientReq::ServerSystemInfoReq(ServerSystemInfoReq {})),
912 };
913
914 match self.oneshot(&msg).await? {
915 ClientResp::ServerSystemInfoResp(resp) => {
916 #[cfg(not(target_family = "wasm"))]
917 let timestamp = Some(
918 std::time::SystemTime::now()
919 .duration_since(std::time::UNIX_EPOCH)?
920 .as_millis() as u64,
921 );
922
923 #[cfg(target_family = "wasm")]
924 let timestamp = None;
925
926 #[cfg(feature = "talc-allocator")]
927 let (client_used, client_heap) = {
928 let (client_used, client_heap) = crate::utils::get_used();
929 (Some(client_used as u64), Some(client_heap as u64))
930 };
931
932 #[cfg(not(feature = "talc-allocator"))]
933 let (client_used, client_heap) = (None, None);
934
935 let info = SystemInfo {
936 heap_size: resp.heap_size,
937 used_size: resp.used_size,
938 cpu_time: resp.cpu_time,
939 cpu_time_epoch: resp.cpu_time_epoch,
940 timestamp,
941 client_heap,
942 client_used,
943 };
944
945 Ok(info)
946 },
947 resp => Err(resp.into()),
948 }
949 }
950}