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); pub struct NotesWrap(pub Notes); impl 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 .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}