edgedb_composable_query/
refs.rs

1use edgedb_protocol::{model::Uuid, value::Value};
2use itertools::Itertools;
3
4use crate::{prim::EdgedbPrim, value::EdgedbValue, EdgedbObject};
5
6/// Basically a `Uuid` and an `Option<T>`. Use this instead of adding `id: Uuid` field to your structs.
7#[derive(Debug, PartialEq, Eq)]
8pub struct Ref<T: EdgedbObject> {
9    pub id: Uuid,
10    pub known_value: Option<T>,
11}
12
13impl<T: EdgedbObject> EdgedbValue for Ref<T> {
14    type NativeArgType = Value;
15
16    fn from_edgedb_value(value: edgedb_protocol::value::Value) -> anyhow::Result<Self> {
17        match value {
18            edgedb_protocol::value::Value::Object { shape, mut fields } => {
19                let uuid_i = shape
20                    .elements
21                    .iter()
22                    .find_position(|e| e.name == "id")
23                    .map(|(i, _)| i);
24
25                let id = uuid_i.and_then(|i| fields[i].take());
26                let id = Uuid::from_edgedb_val(id.ok_or_else(|| anyhow::anyhow!("Expected an object with an 'id' field when deserializing a Ref, got something else"))?)?;
27
28                if shape.elements.len() == 1 {
29                    return Ok(Self {
30                        id,
31                        known_value: None,
32                    });
33                }
34
35                // if shape.elements.len() == 2 {
36                //     let known_value_i = shape
37                //         .elements
38                //         .iter()
39                //         .find_position(|e: &&edgedb_protocol::codec::ShapeElement| {
40                //             e.name == "_known_value"
41                //         })
42                //         .map(|(i, _)| i);
43
44                //     if let Some(known_value_i) = known_value_i {
45                //         let known_value = fields[known_value_i].take();
46                //         let known_value = known_value
47                //             .map(EdgedbValue::from_edgedb_value)
48                //             .transpose()?;
49
50                //         return Ok(Self { id, known_value });
51                //     }
52                // }
53
54                let known_value = Some(T::from_edgedb_object(shape, fields)?);
55
56                Ok(Self { id, known_value })
57            }
58            edgedb_protocol::value::Value::Uuid(_) => {
59                anyhow::bail!("Expected an object when deserializing a Ref, got uuid (do we want to handle this?)")
60            }
61            edgedb_protocol::value::Value::SparseObject(_) => {
62                anyhow::bail!("Expected an object when deserializing a Ref, got sparse object (?)")
63            }
64            _ => {
65                anyhow::bail!("Expected an object when deserializing a Ref, got something else")
66            }
67        }
68    }
69
70    // fn to_edgedb_value(self) -> anyhow::Result<edgedb_protocol::value::Value> {
71    //     todo!()
72    // }
73}
74
75#[cfg(test)]
76mod test {
77    use crate::{value::EdgedbSetValue, EdgedbObject};
78
79    #[derive(Debug, PartialEq)]
80    struct Inner {
81        req: String,
82        opt: Option<String>,
83    }
84
85    impl EdgedbObject for Inner {
86        fn from_edgedb_object(
87            shape: edgedb_protocol::codec::ObjectShape,
88            mut fields: Vec<Option<edgedb_protocol::value::Value>>,
89        ) -> anyhow::Result<Self> {
90            let mut req = None;
91            let mut opt = None;
92
93            for (i, s) in shape.elements.iter().enumerate() {
94                match s.name.as_str() {
95                    "req" => {
96                        req = fields[i]
97                            .take()
98                            .map(EdgedbSetValue::from_edgedb_set_value)
99                            .transpose()?;
100                    }
101                    "opt" => {
102                        opt = fields[i]
103                            .take()
104                            .map(EdgedbSetValue::from_edgedb_set_value)
105                            .transpose()?;
106                    }
107                    _ => {}
108                }
109            }
110
111            Ok(Self {
112                req: EdgedbSetValue::interpret_possibly_missing_required_value(req)?,
113                opt: EdgedbSetValue::interpret_possibly_missing_required_value(opt)?,
114            })
115        }
116
117        // fn to_edgedb_object(
118        //     &self,
119        // ) -> anyhow::Result<(
120        //     edgedb_protocol::codec::ObjectShape,
121        //     Vec<Option<edgedb_protocol::value::Value>>,
122        // )> {
123        //     todo!()
124        // }
125    }
126
127    #[tokio::test]
128    async fn some_queries() -> anyhow::Result<()> {
129        let _conn = edgedb_tokio::create_client().await?;
130
131        // dbg!(query::<Vec<Ref<Inner>>>(&conn, "select Inner;").await?);
132        // dbg!(query::<Vec<Ref<Inner>>>(&conn, "select Inner {id, opt, req};").await?);
133
134        Ok(())
135    }
136}