1use std::{marker::PhantomData, mem::take, pin::Pin};
2
3use futures_util::Future;
4use queries_for_sqlx::{
5 execute_no_cache::ExecuteNoCache, insert_one_st::InsertStOne, prelude::{col, stmt}, quick_query::QuickQuery, select_st::{
6 joins::{join_type, Join},
7 SelectSt,
8 }, update_st::UpdateSt, InitStatement, SupportNamedBind
9};
10use serde_json::{from_value, json, Value};
11use sqlx::{
12 ColumnIndex, Database, Decode, Encode, Executor, Pool, Row,
13 Type,
14};
15use tracing::warn;
16
17use crate::{entities::DynEntity, entities::EntityPhantom};
18
19use super::super::operations::Id;
20
21use super::{
22 DynRelation, InsertInput, SubmitableRelation, UpdateInput,
23};
24
25pub struct OneToMany<Base, Related> {
26 pub relation_key: &'static str,
27 pub foreign_entity_snake_case: &'static str,
28 pub foreign_table: &'static str,
29 pub _entities: PhantomData<(Base, Related)>,
30}
31
32pub struct OptionalToMany<O, M>(
33 pub &'static str,
34 pub PhantomData<(O, M)>,
35);
36
37impl<O, M> OptionalToMany<O, M> {
38 pub fn colomn_link(str: &'static str) -> Self {
39 Self(str, PhantomData)
40 }
41}
42
43impl<S, Base, Related> SubmitableRelation<S>
44 for OneToMany<Base, Related>
45where
46 S: Database,
47 EntityPhantom<Base>: DynEntity<S>,
48 Base: Send + 'static,
49 EntityPhantom<Related>: DynEntity<S>,
50 Related: Send + 'static,
51 Option<i64>:
52 Type<S> + for<'d> Decode<'d, S> + for<'e> Encode<'e, S>,
53 i64: Type<S> + for<'d> Encode<'d, S> + for<'d> Decode<'d, S>,
54 S: Database,
55 for<'s> &'s str: ColumnIndex<S::Row>,
56{
57 fn schema_key(&self) -> &str {
58 self.foreign_entity_snake_case
59 }
60 fn convert(
61 &self,
62 origin: &'static str,
63 input: serde_json::Value,
64 ) -> Box<dyn DynRelation<S>> {
65 match origin {
66 "from_update_one" => {
67 match from_value::<InsertInput<UpdateInput<i64>>>(input) {
68 Ok(InsertInput{
69 id: true,
70 attributes: true,
71 data: UpdateInput::set { id }
72 }) => {
73 Box::new(OneToManyUpdateSet {
74 input: id,
75 schema_relation_key: self
76 .foreign_entity_snake_case,
77 relation_key: self.relation_key,
78 related_entity: Box::new(
79 EntityPhantom::<Related>(
80 PhantomData,
81 ),
82 ),
83 output: Default::default(),
84 })
85 },
86 Ok(_) => todo!("update_one's one-to-many relation support the following format: id: true, attributes: true, data: {{ set }}"),
87 Err(err) => panic!("update_one's one-to-many format {:?}", err),
88 }
89 }
90 "from_get_all" | "from_get_one" => {
91 Box::new(OneToManyS {
92 input,
93 relation_key: self.relation_key,
94 related_entity: Box::new(EntityPhantom::<
95 Related,
96 >(
97 PhantomData
98 )),
99 value: Default::default(),
100 schema_relation_key: self
101 .foreign_entity_snake_case,
102 })
103 }
104 "from_insert_one" => {
105 match from_value::<InsertInput<Id>>(input) {
106 Ok(InsertInput {
107 id: true,
108 attributes: true,
109 data,
110 }) => {
111 return Box::new(OneToManyInsert {
112 input: data,
113 relation_key: self.relation_key,
114 related_entity: Box::new(
115 EntityPhantom::<Related>(
116 PhantomData,
117 ),
118 ),
119 output: Default::default(),
120 schema_relation_key: self
121 .foreign_entity_snake_case,
122 });
123 }
124 _ => {
125 panic!("insert_one input only supports {{'id': true, 'attributes': true }}")
126 }
127 };
128 }
132 _ => {
133 todo!("query {} is not supported", origin)
134 }
135 }
136 }
137}
138
139pub struct OneToManyUpdateSet<S> {
140 pub input: i64,
141 pub schema_relation_key: &'static str,
142 pub relation_key: &'static str,
143 pub related_entity: Box<dyn DynEntity<S>>,
144 pub output: Option<Value>,
145}
146
147impl<S> DynRelation<S> for OneToManyUpdateSet<S>
148where
149 Option<i64>: Type<S> + for<'d> Decode<'d, S>,
150 S: Database,
151 i64: Type<S> + for<'d> Encode<'d, S>,
152 for<'s> &'s str: ColumnIndex<S::Row>,
153{
154 fn take2(&mut self, _local_id: i64) -> Option<Value> {
155 self.output.take()
156 }
157 fn on_update(&self, st: &mut UpdateSt<S, QuickQuery<'_>>)
158 where
159 S: Database + SupportNamedBind,
160 {
161 let input = self.input;
162 st.set(self.relation_key, move || input);
163 }
164 fn sub_op2<'this>(
165 &'this mut self,
166 pool: Pool<S>,
167 _: i64,
168 ) -> Pin<Box<dyn Future<Output = ()> + 'this + Send>>
169 where
170 for<'s> &'s mut <S>::Connection:
171 Executor<'s, Database = S>,
172 S: Database + SupportNamedBind,
173 {
174 Box::pin(async move {
175 let mut st =
176 stmt::SelectSt::init(self.related_entity.table_name());
177
178 let id = self.input;
179 st.where_(col("id").eq(move || id));
180
181 self.related_entity.on_select(&mut st);
182
183 use queries_for_sqlx::execute_no_cache::ExecuteNoCache;
184
185 let res = st
186 .fetch_one(&pool, |r| {
187 let value =
188 self.related_entity.from_row2(&r)?;
189 Ok(value)
190 })
191 .await
192 .unwrap();
193
194 self.output = Some(json!({
195 "id": id,
196 "attributes": res
197 }));
198 })
199 }
200 fn on_insert(&self, st: &mut InsertStOne<'_, S>)
201 where
202 S: Database,
203 {
204 todo!()
205 }
206 fn schema_key(&self) -> &str {
207 self.schema_relation_key
208 }
209 fn take(&mut self) -> Option<Value> {
210 todo!()
211 }
212
213 fn from_row(&mut self, row: &<S>::Row)
214 where
215 S: Database,
216 {
217 todo!()
218 }
219 fn on_select(&self, st: &mut SelectSt<S, QuickQuery<'_>>)
220 where
221 S: SupportNamedBind + Database,
222 {
223 todo!()
224 }
225}
226
227pub struct OneToManyInsert<S> {
228 pub input: Id,
229 pub schema_relation_key: &'static str,
230 pub relation_key: &'static str,
231 pub related_entity: Box<dyn DynEntity<S>>,
232 pub output: Option<Value>,
233}
234
235impl<S> DynRelation<S> for OneToManyInsert<S>
236where
237 Option<i64>: Type<S> + for<'d> Decode<'d, S>,
238 S: Database,
239 i64: Type<S> + for<'d> Encode<'d, S>,
240 for<'s> &'s str: ColumnIndex<S::Row>,
241{
242 fn take2(&mut self, _local_id: i64) -> Option<Value> {
243 take(&mut self.output)
244 }
245 fn sub_op2<'this>(
246 &'this mut self,
247 pool: Pool<S>,
248 _: i64,
249 ) -> Pin<Box<dyn Future<Output = ()> + 'this + Send>>
250 where
251 for<'s> &'s mut <S>::Connection:
252 Executor<'s, Database = S>,
253 S: Database + SupportNamedBind,
254 {
255 Box::pin(async move {
256 let mut st =
257 stmt::SelectSt::init(self.related_entity.table_name());
258
259 warn!("refactor: depricate limit on insert");
264
265 self.related_entity.on_select(&mut st);
266
267 let id = self.input.id;
268 st.where_(col("id").eq(move || id));
269
270 let res = st
271 .fetch_one(&pool, |row| {
272 let val = self
273 .related_entity
274 .from_row2(&row)
275 .unwrap();
276
277 Ok(val)
278 })
279 .await
280 .unwrap();
281
282 self.output = Some(json!({
283 "id": self.input.id,
284 "attributes": res
285 }));
286 })
287 }
288 fn on_insert(&self, st: &mut InsertStOne<'_, S>)
289 where
290 S: Database,
291 {
292 let input = self.input.id;
293
294 st.insert(self.relation_key, input);
295 }
296 fn schema_key(&self) -> &str {
297 self.schema_relation_key
298 }
299 fn take(&mut self) -> Option<Value> {
300 None }
302
303 fn from_row(&mut self, row: &<S>::Row)
304 where
305 S: Database,
306 {
307 }
309 fn on_select(&self, st: &mut SelectSt<S, QuickQuery<'_>>)
310 where
311 S: SupportNamedBind + Database,
312 {
313 }
315}
316
317pub struct OneToManyS<S> {
318 #[deprecated]
319 pub input: Value,
320 pub schema_relation_key: &'static str,
321 pub relation_key: &'static str,
322 pub related_entity: Box<dyn DynEntity<S>>,
323 pub value: Option<Value>,
324}
325
326impl<S> DynRelation<S> for OneToManyS<S>
327where
328 Option<i64>: Type<S> + for<'d> Decode<'d, S>,
329 S: Database,
330 i64: Type<S> + for<'d> Encode<'d, S>,
331 for<'s> &'s str: ColumnIndex<S::Row>,
332{
333 fn on_insert(&self, st: &mut InsertStOne<'_, S>)
334 where
335 S: Database,
336 {
337 let input = self
338 .input
339 .get("data")
340 .unwrap()
341 .get("id")
342 .unwrap()
343 .as_i64()
344 .unwrap();
345 st.insert(self.relation_key, input);
346 }
349 fn schema_key(&self) -> &str {
350 self.schema_relation_key
351 }
352 fn take(&mut self) -> Option<Value> {
353 Some(take(&mut self.value).into())
354 }
355 fn from_row(&mut self, row: &<S>::Row)
356 where
357 S: Database,
358 {
359 let id: Option<i64> = row.get(self.relation_key);
360 if let Some(id) = id {
361 let value = self
362 .related_entity
363 .from_row_scoped(row)
364 .unwrap();
365 self.value =
366 Some(json!({"id": id, "attributes": value}));
367 }
368 }
369 fn on_select(&self, st: &mut SelectSt<S, QuickQuery<'_>>)
370 where
371 S: SupportNamedBind + Database,
372 {
373 st.join(Join {
374 ty: join_type::Left,
375 on_table: self.related_entity.table_name(),
376 on_column: "id",
377 local_column: self.relation_key,
378 });
379
380 st.select(col(self.relation_key));
381
382 self.related_entity.on_select_scoped(st);
383 }
384}