1use candid::utils::ArgumentEncoder;
7use candid::{CandidType, Decode, Principal};
8use ic_agent::Agent;
9use ic_dbms_api::prelude::{
10 DeleteBehavior, Filter, IcDbmsResult, InsertRecord, Query, TableSchema, TransactionId,
11 UpdateRecord,
12};
13
14use crate::client::{Client, RawRecords};
15use crate::errors::{IcAgentError, IcDbmCanisterClientError, IcDbmsCanisterClientResult};
16
17#[derive(Clone, Debug)]
19pub struct IcDbmsAgentClient<'a> {
20 agent: &'a Agent,
21 canister_id: Principal,
22}
23
24impl<'a> IcDbmsAgentClient<'a> {
25 pub fn new(agent: &'a Agent, canister_id: Principal) -> Self {
27 Self { agent, canister_id }
28 }
29}
30
31impl IcDbmsAgentClient<'_> {
32 async fn query<E, R>(&self, method_name: &str, args: E) -> IcDbmsCanisterClientResult<R>
34 where
35 E: ArgumentEncoder,
36 R: CandidType + for<'de> candid::Deserialize<'de>,
37 {
38 let args = candid::encode_args(args).map_err(IcAgentError::from)?;
39 let result = self
40 .agent
41 .query(&self.canister_id, method_name)
42 .with_arg(args)
43 .call()
44 .await
45 .map_err(IcAgentError::from)?;
46
47 self.decode_result(result)
48 }
49
50 async fn update<E, R>(&self, method_name: &str, args: E) -> IcDbmsCanisterClientResult<R>
52 where
53 E: ArgumentEncoder,
54 R: CandidType + for<'de> candid::Deserialize<'de>,
55 {
56 let args = candid::encode_args(args).map_err(IcAgentError::from)?;
57 let result = self
58 .agent
59 .update(&self.canister_id, method_name)
60 .with_arg(args)
61 .call_and_wait()
62 .await
63 .map_err(IcAgentError::from)?;
64
65 self.decode_result(result)
66 }
67
68 fn decode_result<R>(&self, data: Vec<u8>) -> IcDbmsCanisterClientResult<R>
70 where
71 R: CandidType + for<'de> candid::Deserialize<'de>,
72 {
73 Decode!(data.as_slice(), R)
74 .map_err(IcAgentError::from)
75 .map_err(IcDbmCanisterClientError::from)
76 }
77}
78
79impl Client for IcDbmsAgentClient<'_> {
80 fn principal(&self) -> Principal {
81 self.canister_id
82 }
83
84 async fn acl_add_principal(
85 &self,
86 principal: Principal,
87 ) -> IcDbmsCanisterClientResult<IcDbmsResult<()>> {
88 self.update("acl_add_principal", (principal,)).await
89 }
90
91 async fn acl_remove_principal(
92 &self,
93 principal: Principal,
94 ) -> IcDbmsCanisterClientResult<IcDbmsResult<()>> {
95 self.update("acl_remove_principal", (principal,)).await
96 }
97
98 async fn acl_allowed_principals(&self) -> IcDbmsCanisterClientResult<Vec<Principal>> {
99 self.query("acl_allowed_principals", ()).await
100 }
101
102 async fn begin_transaction(&self) -> IcDbmsCanisterClientResult<TransactionId> {
103 self.update("begin_transaction", ()).await
104 }
105
106 async fn commit(
107 &self,
108 transaction_id: TransactionId,
109 ) -> IcDbmsCanisterClientResult<IcDbmsResult<()>> {
110 self.update("commit", (transaction_id,)).await
111 }
112
113 async fn rollback(
114 &self,
115 transaction_id: TransactionId,
116 ) -> IcDbmsCanisterClientResult<IcDbmsResult<()>> {
117 self.update("rollback", (transaction_id,)).await
118 }
119
120 async fn select<T>(
121 &self,
122 table: &str,
123 query: Query,
124 transaction_id: Option<TransactionId>,
125 ) -> IcDbmsCanisterClientResult<IcDbmsResult<Vec<T::Record>>>
126 where
127 T: TableSchema,
128 T::Record: CandidType + for<'de> candid::Deserialize<'de>,
129 {
130 self.query(
131 &crate::utils::table_method(table, "select"),
132 (query, transaction_id),
133 )
134 .await
135 }
136
137 async fn select_raw(
138 &self,
139 table: &str,
140 query: Query,
141 transaction_id: Option<TransactionId>,
142 ) -> IcDbmsCanisterClientResult<IcDbmsResult<RawRecords>> {
143 self.query("select", (table, query, transaction_id)).await
144 }
145
146 async fn insert<T>(
147 &self,
148 table: &str,
149 record: T::Insert,
150 transaction_id: Option<TransactionId>,
151 ) -> IcDbmsCanisterClientResult<IcDbmsResult<()>>
152 where
153 T: TableSchema,
154 T::Insert: InsertRecord<Schema = T> + CandidType,
155 {
156 self.update(
157 &crate::utils::table_method(table, "insert"),
158 (record, transaction_id),
159 )
160 .await
161 }
162
163 async fn update<T>(
164 &self,
165 table: &str,
166 patch: T::Update,
167 transaction_id: Option<TransactionId>,
168 ) -> IcDbmsCanisterClientResult<IcDbmsResult<u64>>
169 where
170 T: TableSchema,
171 T::Update: UpdateRecord<Schema = T> + CandidType,
172 {
173 self.update(
174 &crate::utils::table_method(table, "update"),
175 (patch, transaction_id),
176 )
177 .await
178 }
179
180 async fn delete<T>(
181 &self,
182 table: &str,
183 behaviour: DeleteBehavior,
184 filter: Option<Filter>,
185 transaction_id: Option<TransactionId>,
186 ) -> IcDbmsCanisterClientResult<IcDbmsResult<u64>>
187 where
188 T: TableSchema,
189 {
190 self.update(
191 &crate::utils::table_method(table, "delete"),
192 (behaviour, filter, transaction_id),
193 )
194 .await
195 }
196}