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