1use rust_decimal::Decimal;
2use std::sync::LazyLock;
3use tank::{DataSet, Entity, Executor, FixedDecimal, Passive, cols, expr, stream::TryStreamExt};
4use tokio::sync::Mutex;
5use uuid::Uuid;
6
7static MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
8
9#[derive(Entity, Debug, Clone, PartialEq, Eq, Hash)]
10#[tank(schema = "testing", name = "orders")]
11pub struct Order {
12 #[tank(primary_key)]
13 pub id: Passive<Uuid>,
14 pub customer_id: Uuid,
15 pub country: String,
16 pub total: FixedDecimal<16, 2>,
17 pub status: String,
18 pub created_at: chrono::DateTime<chrono::Utc>,
19}
20
21pub async fn orders<E: Executor>(executor: &mut E) {
22 let _lock = MUTEX.lock().await;
23
24 Order::drop_table(executor, true, false)
26 .await
27 .expect("Failed to drop Order table");
28 Order::create_table(executor, false, true)
29 .await
30 .expect("Failed to create Order table");
31
32 let orders = vec![
34 Order {
35 id: Uuid::new_v4().into(),
36 customer_id: Uuid::parse_str("11111111-1111-1111-1111-111111111111").unwrap(),
37 country: "Germany".into(),
38 total: Decimal::new(12999, 2).into(),
39 status: "paid".into(),
40 created_at: chrono::Utc::now() - chrono::Duration::days(5),
41 },
42 Order {
43 id: Uuid::new_v4().into(),
44 customer_id: Uuid::parse_str("22222222-2222-2222-2222-222222222222").unwrap(),
45 country: "Italy".into(),
46 total: Decimal::new(8990, 2).into(),
47 status: "shipped".into(),
48 created_at: chrono::Utc::now() - chrono::Duration::hours(3),
49 },
50 Order {
51 id: Uuid::new_v4().into(),
52 customer_id: Uuid::parse_str("11111111-1111-1111-1111-111111111111").unwrap(),
53 country: "Germany".into(),
54 total: Decimal::new(45900, 2).into(),
55 status: "shipped".into(),
56 created_at: chrono::Utc::now() - chrono::Duration::days(9),
57 },
58 Order {
59 id: Uuid::new_v4().into(),
60 customer_id: Uuid::parse_str("33333333-3333-3333-3333-333333333333").unwrap(),
61 country: "Spain".into(),
62 total: Decimal::new(22950, 2).into(),
63 status: "paid".into(),
64 created_at: chrono::Utc::now() - chrono::Duration::days(1),
65 },
66 Order {
67 id: Uuid::new_v4().into(),
68 customer_id: Uuid::parse_str("44444444-4444-4444-4444-444444444444").unwrap(),
69 country: "Germany".into(),
70 total: Decimal::new(50, 2).into(),
71 status: "paid".into(),
72 created_at: chrono::Utc::now() - chrono::Duration::days(30),
73 },
74 Order {
75 id: Uuid::new_v4().into(),
76 customer_id: Uuid::parse_str("55555555-5555-5555-5555-555555555555").unwrap(),
77 country: "Germany".into(),
78 total: Decimal::new(111899, 2).into(),
79 status: "shipped".into(),
80 created_at: chrono::Utc::now() - chrono::Duration::days(30),
81 },
82 Order {
83 id: Uuid::new_v4().into(),
84 customer_id: Uuid::parse_str("22222222-2222-2222-2222-222222222222").unwrap(),
85 country: "Italy".into(),
86 total: Decimal::new(4445, 2).into(),
87 status: "paid".into(),
88 created_at: chrono::Utc::now() - chrono::Duration::hours(2),
89 },
90 ];
91 let result = Order::insert_many(executor, orders.iter())
92 .await
93 .expect("Failed to insert orders");
94 if let Some(affected) = result.rows_affected {
95 assert_eq!(affected, 7);
96 }
97
98 let mut query = Order::table()
100 .prepare(
101 executor,
102 cols!(
103 Order::id,
104 Order::customer_id,
105 Order::country,
106 Order::total DESC,
107 Order::status,
108 Order::created_at
109 ),
110 &expr!(
111 Order::status == (?, ?) as IN &&
112 Order::created_at >= ? &&
113 Order::total >= ? &&
114 Order::country == (?, ?) as IN
115 ),
116 None,
117 )
118 .await
119 .expect("Failed to prepare the query");
120 assert!(query.is_prepared(), "Query should be marked as prepared");
121
122 query
124 .bind("paid")
125 .unwrap()
126 .bind("shipped")
127 .unwrap()
128 .bind(chrono::Utc::now() - chrono::Duration::days(10))
129 .unwrap()
130 .bind(99.99)
131 .unwrap()
132 .bind("Germany")
133 .unwrap()
134 .bind("Spain")
135 .unwrap();
136 let orders = executor
137 .fetch(&mut query)
138 .and_then(|v| async { Order::from_row(v) })
139 .map_ok(|v| format!("{}, {}, {:.2}€", v.country, v.status, v.total.0))
140 .try_collect::<Vec<_>>()
141 .await
142 .expect("Could not run query 1");
143 assert_eq!(
144 orders,
145 [
146 "Germany, shipped, 459.00€".to_string(),
147 "Spain, paid, 229.50€".to_string(),
148 "Germany, paid, 129.99€".to_string(),
149 ]
150 );
151 assert!(query.is_prepared());
152
153 query.clear_bindings().expect("Failed to clear bindings");
155 query
156 .bind("paid")
157 .unwrap()
158 .bind("shipped")
159 .unwrap()
160 .bind(chrono::Utc::now() - chrono::Duration::days(365))
161 .unwrap()
162 .bind(1)
163 .unwrap()
164 .bind("Germany")
165 .unwrap()
166 .bind("Italy")
167 .unwrap();
168 let orders: Vec<String> = executor
169 .fetch(&mut query)
170 .and_then(|v| async { Order::from_row(v) })
171 .map_ok(|v| format!("{}, {}, {:.2}€", v.country, v.status, v.total.0))
172 .try_collect()
173 .await
174 .expect("Could not run query 2");
175 assert_eq!(
176 orders,
177 [
178 "Germany, shipped, 1118.99€".to_string(),
179 "Germany, shipped, 459.00€".to_string(),
180 "Germany, paid, 129.99€".to_string(),
181 "Italy, shipped, 89.90€".to_string(),
182 "Italy, paid, 44.45€".to_string(),
183 ]
184 );
185 assert!(query.is_prepared());
186}