Skip to main content

tank_tests/
cheat_sheet.rs

1use anyhow::anyhow;
2use std::{collections::HashMap, str::FromStr, sync::LazyLock};
3use tank::{AsValue, Entity, Result, Value, expr};
4use tokio::sync::Mutex;
5use uuid::Uuid;
6
7static MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
8
9#[derive(Default, PartialEq, Clone, Debug)]
10pub struct Notes(pub String); // Third party type
11pub struct NotesWrap(pub Notes); // Local wrapper
12
13impl AsValue for NotesWrap {
14    fn as_empty_value() -> Value {
15        Value::Varchar(None)
16    }
17    fn as_value(self) -> Value {
18        Value::Varchar(Some(self.0.0.into()))
19    }
20    fn try_from_value(value: Value) -> Result<Self> {
21        match value.try_as(&Value::Varchar(None)) {
22            Ok(Value::Varchar(Some(s))) => Ok(NotesWrap(Notes(s.to_string()))),
23            _ => Err(anyhow!("Expected Varchar for Notes")),
24        }
25    }
26}
27impl From<Notes> for NotesWrap {
28    fn from(v: Notes) -> Self {
29        NotesWrap(v)
30    }
31}
32impl From<NotesWrap> for Notes {
33    fn from(v: NotesWrap) -> Self {
34        v.0
35    }
36}
37
38#[derive(Default, Entity, PartialEq, Debug)]
39#[tank(
40    schema = "army",
41    name = "deployments",
42    primary_key = (Self::unit_id, Self::region),
43)]
44struct EntityExample {
45    unit_id: Uuid,
46    #[tank(clustering_key)]
47    region: String,
48    #[tank(name = "callsign")]
49    callsign: String,
50    casualties: i32,
51    #[tank(conversion_type = NotesWrap)]
52    metadata: Notes,
53    #[tank(ignore)]
54    transient_cache: HashMap<String, String>,
55}
56
57pub async fn cheat_sheet(mut connection: &mut impl tank::Connection) -> Result<()> {
58    let _lock = MUTEX.lock().await;
59
60    {
61        EntityExample::drop_table(&mut connection, true, false).await?;
62        EntityExample::create_table(&mut connection, true, true).await?;
63    }
64
65    let entity = EntityExample {
66        unit_id: Uuid::new_v4(),
67        region: "North".into(),
68        callsign: "Alpha".into(),
69        casualties: 0,
70        metadata: Notes("mission".into()),
71        transient_cache: HashMap::new(),
72    };
73
74    {
75        use tank::{Entity, Transaction};
76
77        let mut tx = connection.begin().await?;
78        EntityExample::insert_one(&mut tx, &entity).await?;
79        entity.delete(&mut tx).await?;
80        tx.commit().await?;
81    }
82
83    let entity2 = EntityExample {
84        unit_id: Uuid::new_v4(),
85        region: "South".into(),
86        callsign: "Bravo-2".into(),
87        casualties: 3,
88        metadata: Notes("mission-2".into()),
89        transient_cache: HashMap::new(),
90    };
91    let entity3 = EntityExample {
92        unit_id: Uuid::new_v4(),
93        region: "East".into(),
94        callsign: "Charlie-3".into(),
95        casualties: 5,
96        metadata: Notes("mission-3".into()),
97        transient_cache: HashMap::new(),
98    };
99
100    {
101        EntityExample::insert_one(&mut connection, &entity).await?;
102        EntityExample::insert_many(&mut connection, [&entity2]).await?;
103        connection.append([&entity3]).await?;
104    }
105
106    {
107        entity.save(&mut connection).await?;
108        entity.delete(&mut connection).await?;
109    }
110
111    {
112        use std::pin::pin;
113        use tank::{Entity, expr, stream::TryStreamExt};
114
115        let entity = EntityExample::find_one(&mut connection, entity.primary_key_expr()).await?;
116        {
117            let uid = entity2.unit_id;
118            let mut stream = pin!(EntityExample::find_many(
119                &mut connection,
120                expr!(EntityExample::unit_id == #uid),
121                Some(100),
122            ));
123            while let Some(entity) = stream.try_next().await? {
124                println!("{}", entity.callsign);
125            }
126        }
127        let uid = Uuid::from_str("94f0cbcc-1fce-454e-a6e4-4e3587741808")?;
128        let entities: Vec<EntityExample> =
129            EntityExample::find_many(&mut connection, expr!(EntityExample::unit_id == #uid), None)
130                .try_collect()
131                .await?;
132    }
133
134    {
135        use tank::{Entity, expr};
136
137        let uid = entity2.unit_id;
138        EntityExample::delete_many(&mut connection, expr!(EntityExample::unit_id == #uid)).await?;
139
140        let uid = entity3.unit_id;
141        EntityExample::delete_many(&mut connection, expr!(EntityExample::unit_id == #uid)).await?;
142    }
143
144    {
145        expr!(EntityExample::casualties == 0);
146        expr!(EntityExample::casualties >= 10);
147        expr!(EntityExample::region == "North" || EntityExample::region == "South");
148        expr!(EntityExample::callsign == "Alpha%" as LIKE);
149        expr!(EntityExample::callsign != "Alpha%" as LIKE);
150        expr!(EntityExample::casualties > ?);
151        let uid = Uuid::new_v4();
152        expr!(EntityExample::unit_id == #uid);
153    }
154
155    {
156        use tank::{Entity, expr, stream::TryStreamExt};
157
158        let mut query = EntityExample::prepare_find(
159            &mut connection,
160            expr!(EntityExample::unit_id == ?),
161            Some(50),
162        )
163        .await?;
164        query.bind(Uuid::from_str("2f4f97da-0278-4c99-bc22-2b3986aeee85")?)?;
165        let entities = connection
166            .fetch(&mut query)
167            .map_ok(|row| EntityExample::from_row(row).unwrap())
168            .try_collect::<Vec<EntityExample>>()
169            .await?;
170        query.clear_bindings()?;
171        query.bind(Uuid::from_str("962f2c1c-7caa-468d-a387-53ed9860c4bf")?)?;
172    }
173
174    {
175        use tank::{QueryBuilder, cols, expr, stream::TryStreamExt};
176
177        let uid = entity.unit_id;
178        let results = connection
179            .fetch(
180                QueryBuilder::new()
181                    // Selecting fewer columns requires the entity to have the Default trait
182                    .select(cols!(EntityExample::callsign, EntityExample::casualties))
183                    .from(EntityExample::table())
184                    .where_expr(expr!(EntityExample::unit_id == #uid))
185                    .order_by(cols!(EntityExample::region ASC))
186                    .limit(Some(50))
187                    .build(&connection.driver()),
188            )
189            .map_ok(|row| EntityExample::from_row(row).unwrap())
190            .try_collect::<Vec<_>>()
191            .await?;
192    }
193
194    #[cfg(not(feature = "disable-joins"))]
195    {
196        use crate::{Author, AuthorColumnTrait, Book, BookColumnTrait};
197        use tank::{
198            Entity, QueryBuilder, cols, expr, join, stream::StreamExt, stream::TryStreamExt,
199        };
200
201        #[derive(Entity, Debug)]
202        struct BookWithAuthor {
203            title: String,
204            author: String,
205        }
206
207        let rows: Vec<BookWithAuthor> = connection
208            .fetch(
209                QueryBuilder::new()
210                    .select(cols!(Book::title, Author::name as author))
211                    .from(join!(Book JOIN Author ON Book::author == Author::id))
212                    .where_expr(expr!(Book::year > 2000))
213                    .order_by(cols!(Book::title ASC))
214                    .build(&connection.driver()),
215            )
216            .map_ok(BookWithAuthor::from_row)
217            .map(Result::flatten)
218            .try_collect()
219            .await?;
220
221        let rows: Vec<BookWithAuthor> = connection
222            .fetch(
223                QueryBuilder::new()
224                    .select(cols!(B.title, A.name as author))
225                    .from(join!(Book B LEFT JOIN Author A ON B.author == A.author_id))
226                    .where_expr(true)
227                    .build(&connection.driver()),
228            )
229            .map_ok(BookWithAuthor::from_row)
230            .map(Result::flatten)
231            .try_collect()
232            .await?;
233
234        let dataset = join!(
235            Book B
236                LEFT JOIN Author A1 ON B.author == A1.author_id
237                LEFT JOIN Author A2 ON B.co_author == A2.author_id
238        );
239        let rows = connection
240            .fetch(
241                QueryBuilder::new()
242                    .select(cols!(B.title, A1.name as author, A2.name as co_author))
243                    .from(dataset)
244                    .where_expr(true)
245                    .build(&connection.driver()),
246            )
247            .try_collect::<Vec<_>>()
248            .await?;
249    }
250
251    Ok(())
252}