1use std::collections::VecDeque;
2use std::future::Future;
3use std::{marker::PhantomData, sync::Arc};
4
5use futures::future::try_join_all;
6use futures::stream::{try_unfold, SelectAll};
7use futures::TryStream;
8use serde::{de::DeserializeOwned, Serialize};
9
10use crate::dto::items::*;
11use crate::{
12 ApiClient, CondBoxedStream, CondSend, EqIdentity, Filter, Identity, IntoParams, IntoPatch,
13 Partition, Patch, Result, Search, SetCursor, UpsertOptions, WithPartition,
14};
15
16use super::utils::{get_duplicates_from_result, get_missing_from_result};
17
18pub struct Resource<T> {
21 pub api_client: Arc<ApiClient>,
23 marker: PhantomData<T>,
24}
25
26impl<T> Resource<T> {
27 pub fn new(api_client: Arc<ApiClient>) -> Self {
33 Resource {
34 api_client,
35 marker: PhantomData,
36 }
37 }
38}
39
40impl<T> Clone for Resource<T> {
41 fn clone(&self) -> Self {
42 Self {
43 api_client: self.api_client.clone(),
44 marker: PhantomData,
45 }
46 }
47}
48
49impl<T> WithApiClient for Resource<T> {
50 fn get_client(&self) -> &ApiClient {
51 &self.api_client
52 }
53}
54
55pub trait WithApiClient {
57 fn get_client(&self) -> &ApiClient;
59}
60
61pub trait WithBasePath {
63 const BASE_PATH: &'static str;
65}
66
67pub trait List<TParams, TResponse>
69where
70 TParams: IntoParams + Send + Sync + 'static,
71 TResponse: Serialize + DeserializeOwned + Send + Sync,
72 Self: WithApiClient + WithBasePath + Sync,
73{
74 fn list(
80 &self,
81 params: Option<TParams>,
82 ) -> impl Future<Output = Result<ItemsVec<TResponse, Cursor>>> + CondSend {
83 async move {
84 self.get_client()
85 .get_with_params(Self::BASE_PATH, params)
86 .await
87 }
88 }
89
90 fn list_all(
96 &self,
97 mut params: TParams,
98 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
99 where
100 TParams: SetCursor + Clone,
101 TResponse: Send,
102 {
103 async move {
104 let mut result = vec![];
105 loop {
106 let lparams = params.clone();
107 let response: ItemsVec<TResponse, Cursor> = self
108 .get_client()
109 .get_with_params(Self::BASE_PATH, Some(lparams))
110 .await?;
111 for it in response.items {
112 result.push(it);
113 }
114 match response.extra_fields.next_cursor {
115 Some(cursor) => params.set_cursor(Some(cursor)),
116 None => return Ok(result),
117 }
118 }
119 }
120 }
121
122 fn list_all_stream(
132 &self,
133 params: TParams,
134 ) -> impl TryStream<Ok = TResponse, Error = crate::Error, Item = Result<TResponse>> + CondSend
135 where
136 TParams: SetCursor + Clone,
137 TResponse: Send + 'static,
138 {
139 let state = CursorStreamState {
140 req: params,
141 responses: VecDeque::new(),
142 next_cursor: CursorState::Initial,
143 };
144
145 try_unfold(state, move |mut state| async move {
146 if let Some(next) = state.responses.pop_front() {
147 Ok(Some((next, state)))
148 } else {
149 let cursor = match std::mem::take(&mut state.next_cursor) {
150 CursorState::Initial => None,
151 CursorState::Some(x) => Some(x),
152 CursorState::End => {
153 return Ok(None);
154 }
155 };
156 state.req.set_cursor(cursor);
157 let response: ItemsVec<TResponse, Cursor> = self
158 .get_client()
159 .get_with_params(Self::BASE_PATH, Some(state.req.clone()))
160 .await?;
161
162 state.responses.extend(response.items);
163 state.next_cursor = match response.extra_fields.next_cursor {
164 Some(x) => CursorState::Some(x),
165 None => CursorState::End,
166 };
167 if let Some(next) = state.responses.pop_front() {
168 Ok(Some((next, state)))
169 } else {
170 Ok(None)
171 }
172 }
173 })
174 }
175}
176
177pub trait Create<TCreate, TResponse>
179where
180 TCreate: Serialize + Sync + Send,
181 TResponse: Serialize + DeserializeOwned + Send,
182 Self: WithApiClient + WithBasePath + Sync,
183{
184 fn create(
190 &self,
191 creates: &[TCreate],
192 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
193 async move {
194 let items = Items::new(creates);
195 let response: ItemsVec<TResponse> =
196 self.get_client().post(Self::BASE_PATH, &items).await?;
197 Ok(response.items)
198 }
199 }
200
201 fn create_from(
207 &self,
208 creates: &[impl Into<TCreate> + Sync + Clone],
209 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
210 async move {
211 let to_add: Vec<TCreate> = creates.iter().map(|i| i.clone().into()).collect();
212 self.create(&to_add).await
213 }
214 }
215
216 fn create_ignore_duplicates(
222 &self,
223 creates: &[TCreate],
224 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
225 where
226 TCreate: EqIdentity,
227 {
228 async move {
229 let resp = self.create(creates).await;
230
231 let duplicates: Option<Vec<Identity>> = get_duplicates_from_result(&resp);
232
233 if let Some(duplicates) = duplicates {
234 let next: Vec<&TCreate> = creates
235 .iter()
236 .filter(|c| !duplicates.iter().any(|i| c.eq(i)))
237 .collect();
238
239 if next.is_empty() {
240 if duplicates.len() == creates.len() {
241 return Ok(vec![]);
242 }
243 return resp;
244 }
245
246 let items = Items::new(next);
247 let response: ItemsVec<TResponse> =
248 self.get_client().post(Self::BASE_PATH, &items).await?;
249 Ok(response.items)
250 } else {
251 resp
252 }
253 }
254 }
255
256 fn create_from_ignore_duplicates<'a, T: 'a>(
263 &self,
264 creates: &'a [impl Into<TCreate> + Sync + Clone],
265 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
266 where
267 TCreate: EqIdentity,
268 {
269 async move {
270 let to_add: Vec<TCreate> = creates.iter().map(|i| i.clone().into()).collect();
271 self.create_ignore_duplicates(&to_add).await
272 }
273 }
274}
275
276pub trait Upsert<'a, TCreate, TUpdate, TResponse>
278where
279 TCreate: Serialize + Sync + Send + EqIdentity + 'a + Clone + IntoPatch<TUpdate>,
280 TUpdate: Serialize + Sync + Send + Default,
281 TResponse: Serialize + DeserializeOwned + Sync + Send,
282 Self: WithApiClient + WithBasePath + Sync,
283{
284 fn upsert(
293 &'a self,
294 upserts: &'a [TCreate],
295 options: &UpsertOptions,
296 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
297 async move {
298 let items = Items::new(upserts);
299 let resp: Result<ItemsVec<TResponse>> =
300 self.get_client().post(Self::BASE_PATH, &items).await;
301
302 let duplicates: Option<Vec<Identity>> = get_duplicates_from_result(&resp);
303
304 if let Some(duplicates) = duplicates {
305 let mut to_create = Vec::with_capacity(upserts.len() - duplicates.len());
306 let mut to_update = Vec::with_capacity(duplicates.len());
307 for it in upserts {
308 let idt = duplicates.iter().find(|i| it.eq(i));
309 if let Some(idt) = idt {
310 to_update.push(Patch::<TUpdate> {
311 id: idt.clone(),
312 update: it.clone().patch(options),
313 });
314 } else {
315 to_create.push(it);
316 }
317 }
318
319 let mut result = Vec::with_capacity(to_create.len() + to_update.len());
320 if !to_create.is_empty() {
321 let mut create_response: ItemsVec<TResponse> = self
322 .get_client()
323 .post(Self::BASE_PATH, &Items::new(to_create))
324 .await?;
325 result.append(&mut create_response.items);
326 }
327 if !to_update.is_empty() {
328 let mut update_response: ItemsVec<TResponse> = self
329 .get_client()
330 .post(
331 &format!("{}/update", Self::BASE_PATH),
332 &Items::new(&to_update),
333 )
334 .await?;
335 result.append(&mut update_response.items);
336 }
337
338 Ok(result)
339 } else {
340 resp.map(|i| i.items)
341 }
342 }
343 }
344}
345
346impl<'a, T, TCreate, TUpdate, TResponse> Upsert<'a, TCreate, TUpdate, TResponse> for T
347where
348 T: Create<TCreate, TResponse> + Update<Patch<TUpdate>, TResponse> + Sync,
349 TCreate: Serialize + Sync + Send + EqIdentity + 'a + Clone + IntoPatch<TUpdate>,
350 TUpdate: Serialize + Sync + Send + Default,
351 TResponse: Serialize + DeserializeOwned + Sync + Send,
352{
353}
354
355pub trait UpsertCollection<TUpsert, TResponse> {
357 fn upsert(
363 &self,
364 collection: &TUpsert,
365 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
366 where
367 TUpsert: Serialize + Sync + Send,
368 TResponse: Serialize + DeserializeOwned + Sync + Send,
369 Self: WithApiClient + WithBasePath + Sync,
370 {
371 async move {
372 let response: ItemsVec<TResponse> =
373 self.get_client().post(Self::BASE_PATH, &collection).await?;
374 Ok(response.items)
375 }
376 }
377}
378
379pub trait Delete<TIdt>
381where
382 TIdt: Serialize + Sync + Send,
383 Self: WithApiClient + WithBasePath + Sync,
384{
385 fn delete(&self, deletes: &[TIdt]) -> impl Future<Output = Result<()>> + CondSend {
391 async move {
392 let items = Items::new(deletes);
393 self.get_client()
394 .post::<::serde_json::Value, Items<&[TIdt]>>(
395 &format!("{}/delete", Self::BASE_PATH),
396 &items,
397 )
398 .await?;
399 Ok(())
400 }
401 }
402}
403
404pub trait DeleteWithRequest<TReq>
406where
407 TReq: Serialize + Sync + Send,
408 Self: WithApiClient + WithBasePath + Sync,
409{
410 fn delete(&self, req: &TReq) -> impl Future<Output = Result<()>> + CondSend {
416 async move {
417 self.get_client()
418 .post::<::serde_json::Value, TReq>(&format!("{}/delete", Self::BASE_PATH), req)
419 .await?;
420 Ok(())
421 }
422 }
423}
424
425pub trait DeleteWithIgnoreUnknownIds<TIdt>
428where
429 TIdt: Serialize + Sync + Send,
430 Self: WithApiClient + WithBasePath + Sync,
431{
432 fn delete(
440 &self,
441 deletes: impl Into<TIdt> + Send,
442 ignore_unknown_ids: bool,
443 ) -> impl Future<Output = Result<()>> + CondSend
444 where
445 Self: Sync,
446 {
447 async move {
448 let req = Items::new_with_extra_fields(
449 deletes.into(),
450 IgnoreUnknownIds { ignore_unknown_ids },
451 );
452 self.get_client()
453 .post::<::serde_json::Value, _>(&format!("{}/delete", Self::BASE_PATH), &req)
454 .await?;
455 Ok(())
456 }
457 }
458}
459
460pub trait DeleteWithResponse<TIdt, TResponse>
463where
464 TIdt: Serialize + Sync + Send,
465 TResponse: Serialize + DeserializeOwned + Sync + Send,
466 Self: WithApiClient + WithBasePath + Sync,
467{
468 fn delete(
474 &self,
475 deletes: &[TIdt],
476 ) -> impl Future<Output = Result<ItemsVec<TResponse>>> + CondSend {
477 async move {
478 let items = Items::new(deletes);
479 let response: ItemsVec<TResponse> = self
480 .get_client()
481 .post(&format!("{}/delete", Self::BASE_PATH), &items)
482 .await?;
483 Ok(response)
484 }
485 }
486}
487
488pub trait Update<TUpdate, TResponse>
490where
491 TUpdate: Serialize + Sync + Send,
492 TResponse: Serialize + DeserializeOwned,
493 Self: WithApiClient + WithBasePath + Sync,
494{
495 fn update(
501 &self,
502 updates: &[TUpdate],
503 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
504 async move {
505 let items = Items::new(updates);
506 let response: ItemsVec<TResponse> = self
507 .get_client()
508 .post(&format!("{}/update", Self::BASE_PATH), &items)
509 .await?;
510 Ok(response.items)
511 }
512 }
513
514 fn update_from<'a, T>(
520 &self,
521 updates: &'a [T],
522 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
523 where
524 T: std::marker::Sync + Clone + 'a,
525 TUpdate: From<T>,
526 {
527 async move {
528 let to_update: Vec<TUpdate> =
529 updates.iter().map(|i| TUpdate::from(i.clone())).collect();
530 self.update(&to_update).await
531 }
532 }
533
534 fn update_ignore_unknown_ids(
540 &self,
541 updates: &[TUpdate],
542 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
543 where
544 TUpdate: EqIdentity,
545 TResponse: Send,
546 {
547 async move {
548 let response = self.update(updates).await;
549 let missing: Option<Vec<Identity>> = get_missing_from_result(&response);
550
551 if let Some(missing) = missing {
552 let next: Vec<&TUpdate> = updates
553 .iter()
554 .filter(|c| !missing.iter().any(|i| c.eq(i)))
555 .collect();
556
557 if next.is_empty() {
558 if missing.len() == updates.len() {
559 return Ok(vec![]);
560 }
561 return response;
562 }
563
564 let items = Items::new(next);
565 let response: ItemsVec<TResponse> = self
566 .get_client()
567 .post(&format!("{}/update", Self::BASE_PATH), &items)
568 .await?;
569 Ok(response.items)
570 } else {
571 response
572 }
573 }
574 }
575
576 fn update_from_ignore_unknown_ids<'a, T>(
583 &self,
584 updates: &'a [T],
585 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
586 where
587 T: Sync + Clone + 'a,
588 TUpdate: From<T> + EqIdentity,
589 TResponse: Send,
590 {
591 async move {
592 let to_update: Vec<TUpdate> =
593 updates.iter().map(|i| TUpdate::from(i.clone())).collect();
594 self.update_ignore_unknown_ids(&to_update).await
595 }
596 }
597}
598
599pub trait Retrieve<TIdt, TResponse>
601where
602 TIdt: Serialize + Sync + Send,
603 TResponse: Serialize + DeserializeOwned,
604 Self: WithApiClient + WithBasePath + Sync,
605{
606 fn retrieve(&self, ids: &[TIdt]) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
612 async move {
613 let items = Items::new(ids);
614 let response: ItemsVec<TResponse> = self
615 .get_client()
616 .post(&format!("{}/byids", Self::BASE_PATH), &items)
617 .await?;
618 Ok(response.items)
619 }
620 }
621}
622
623pub trait RetrieveWithRequest<TRequest, TResponse>
625where
626 TRequest: Serialize + Sync + Send,
627 TResponse: Serialize + DeserializeOwned,
628 Self: WithApiClient + WithBasePath + Sync,
629{
630 fn retrieve(&self, req: &TRequest) -> impl Future<Output = Result<TResponse>> + CondSend {
636 async move {
637 let response: TResponse = self
638 .get_client()
639 .post(&format!("{}/byids", Self::BASE_PATH), req)
640 .await?;
641 Ok(response)
642 }
643 }
644}
645
646pub trait RetrieveWithIgnoreUnknownIds<TIdt, TResponse>
648where
649 TIdt: Serialize + Sync + Send,
650 TResponse: Serialize + DeserializeOwned,
651 Self: WithApiClient + WithBasePath + Sync,
652{
653 fn retrieve(
662 &self,
663 ids: impl Into<TIdt> + Send,
664 ignore_unknown_ids: bool,
665 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
666 async move {
667 let items =
668 Items::new_with_extra_fields(ids.into(), IgnoreUnknownIds { ignore_unknown_ids });
669 let response: ItemsVec<TResponse> = self
670 .get_client()
671 .post(&format!("{}/byids", Self::BASE_PATH), &items)
672 .await?;
673 Ok(response.items)
674 }
675 }
676}
677
678pub trait FilterItems<TFilter, TResponse>
680where
681 TFilter: Serialize + Sync + Send + 'static,
682 TResponse: Serialize + DeserializeOwned,
683 Self: WithApiClient + WithBasePath + Sync,
684{
685 fn filter_items(
694 &self,
695 filter: TFilter,
696 cursor: Option<String>,
697 limit: Option<u32>,
698 ) -> impl Future<Output = Result<ItemsVec<TResponse, Cursor>>> + CondSend {
699 async move {
700 let filter = Filter::<TFilter>::new(filter, cursor, limit);
701 let response: ItemsVec<TResponse, Cursor> = self
702 .get_client()
703 .post(&format!("{}/list", Self::BASE_PATH), &filter)
704 .await?;
705 Ok(response)
706 }
707 }
708}
709
710impl<TFilter, TResponse, T> FilterWithRequest<Filter<TFilter>, TResponse> for T
711where
712 TFilter: Serialize + Sync + Send + 'static,
713 TResponse: Serialize + DeserializeOwned,
714 T: FilterItems<TFilter, TResponse>,
715 Self: WithApiClient + WithBasePath,
716{
717}
718
719#[derive(Debug, Default)]
720pub(crate) enum CursorState {
721 Initial,
722 Some(String),
723 #[default]
724 End,
725}
726
727pub(crate) struct CursorStreamState<TFilter, TResponse> {
728 pub(crate) req: TFilter,
729 pub(crate) responses: VecDeque<TResponse>,
730 pub(crate) next_cursor: CursorState,
731}
732
733pub trait FilterWithRequest<TFilter, TResponse>
735where
736 TFilter: Serialize + Sync + Send + 'static,
737 TResponse: Serialize + DeserializeOwned,
738 Self: WithApiClient + WithBasePath + Sync,
739{
740 fn filter(
746 &self,
747 filter: TFilter,
748 ) -> impl Future<Output = Result<ItemsVec<TResponse, Cursor>>> + CondSend {
749 async move {
750 let response: ItemsVec<TResponse, Cursor> = self
751 .get_client()
752 .post(&format!("{}/list", Self::BASE_PATH), &filter)
753 .await?;
754 Ok(response)
755 }
756 }
757
758 fn filter_all(
764 &self,
765 mut filter: TFilter,
766 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
767 where
768 TFilter: SetCursor,
769 TResponse: Send,
770 {
771 async move {
772 let mut result = vec![];
773 loop {
774 let response: ItemsVec<TResponse, Cursor> = self
775 .get_client()
776 .post(&format!("{}/list", Self::BASE_PATH), &filter)
777 .await?;
778 for it in response.items {
779 result.push(it);
780 }
781 match response.extra_fields.next_cursor {
782 Some(cursor) => filter.set_cursor(Some(cursor)),
783 None => return Ok(result),
784 }
785 }
786 }
787 }
788
789 fn filter_all_stream(
799 &self,
800 filter: TFilter,
801 ) -> impl TryStream<Ok = TResponse, Error = crate::Error, Item = Result<TResponse>> + CondSend
802 where
803 TFilter: SetCursor,
804 TResponse: Send + 'static,
805 {
806 let state = CursorStreamState {
807 req: filter,
808 responses: VecDeque::new(),
809 next_cursor: CursorState::Initial,
810 };
811
812 try_unfold(state, move |mut state| async move {
813 if let Some(next) = state.responses.pop_front() {
814 Ok(Some((next, state)))
815 } else {
816 let cursor = match std::mem::take(&mut state.next_cursor) {
817 CursorState::Initial => None,
818 CursorState::Some(x) => Some(x),
819 CursorState::End => {
820 return Ok(None);
821 }
822 };
823 state.req.set_cursor(cursor);
824 let response: ItemsVec<TResponse, Cursor> = self
825 .get_client()
826 .post(&format!("{}/list", Self::BASE_PATH), &state.req)
827 .await?;
828
829 state.responses.extend(response.items);
830 state.next_cursor = match response.extra_fields.next_cursor {
831 Some(x) => CursorState::Some(x),
832 None => CursorState::End,
833 };
834 if let Some(next) = state.responses.pop_front() {
835 Ok(Some((next, state)))
836 } else {
837 Ok(None)
838 }
839 }
840 })
841 }
842
843 fn filter_all_partitioned(
851 &self,
852 filter: TFilter,
853 num_partitions: u32,
854 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend
855 where
856 TFilter: SetCursor + WithPartition,
857 TResponse: Send,
858 {
859 async move {
860 let mut futures = Vec::with_capacity(num_partitions as usize);
861 for partition in 0..num_partitions {
862 let part_filter =
863 filter.with_partition(Partition::new(partition + 1, num_partitions));
864 futures.push(self.filter_all(part_filter));
865 }
866 let results = try_join_all(futures).await?;
867 let mut response_items = Vec::with_capacity(results.iter().map(|i| i.len()).sum());
868 for chunk in results.into_iter() {
869 response_items.extend(chunk);
870 }
871 Ok(response_items)
872 }
873 }
874
875 fn filter_all_partitioned_stream(
888 &self,
889 filter: TFilter,
890 num_partitions: u32,
891 ) -> impl TryStream<Ok = TResponse, Error = crate::Error, Item = Result<TResponse>> + CondSend
892 where
893 TFilter: SetCursor + WithPartition,
894 TResponse: Send + 'static,
895 {
896 let mut streams = SelectAll::new();
897 for partition in 0..num_partitions {
898 let part_filter = filter.with_partition(Partition::new(partition + 1, num_partitions));
899 streams.push(self.filter_all_stream(part_filter).boxed_cond());
900 }
901
902 streams
903 }
904}
905
906pub trait SearchItems<'a, TFilter, TSearch, TResponse>
908where
909 TFilter: Serialize + Sync + Send + 'a,
910 TSearch: Serialize + Sync + Send + 'a,
911 TResponse: Serialize + DeserializeOwned,
912 Self: WithApiClient + WithBasePath + Sync,
913{
914 fn search(
922 &'a self,
923 filter: TFilter,
924 search: TSearch,
925 limit: Option<u32>,
926 ) -> impl Future<Output = Result<Vec<TResponse>>> + CondSend {
927 async move {
928 let req = Search::<TFilter, TSearch>::new(filter, search, limit);
929 let response: ItemsVec<TResponse> = self
930 .get_client()
931 .post(&format!("{}/search", Self::BASE_PATH), &req)
932 .await?;
933 Ok(response.items)
934 }
935 }
936}