Skip to main content

cms_for_rust/relations/
optional_to_many.rs

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                // match from_value::<InsertInput<()>>(
129                //     input,
130                // ) {
131            }
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            // limit here used to work for many-to-many
260            // relations, where I add : where base_id = 1
261            // this is unnecessary because I have the id
262            // in input
263            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 // no-op
301    }
302
303    fn from_row(&mut self, row: &<S>::Row)
304    where
305        S: Database,
306    {
307        // no-op
308    }
309    fn on_select(&self, st: &mut SelectSt<S, QuickQuery<'_>>)
310    where
311        S: SupportNamedBind + Database,
312    {
313        // no-op
314    }
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        // panic!("{input}");
347        // self.related_entity.into_insert(input, st);
348    }
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}