1use apithing::{ApiExecutor, ApiOperation};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone)]
17struct CreateUserProps {
18 name: String,
19 email: String,
20}
21
22#[derive(Debug, Clone)]
23struct FindUserProps {
24 user_id: u64,
25}
26
27#[derive(Debug, Clone)]
28struct User {
29 id: u64,
30 name: String,
31 email: String,
32}
33
34#[derive(Debug)]
35enum UserError {
36 InvalidEmail,
37 NotFound,
38}
39
40#[derive(Debug)]
42struct AppContext {
43 connection_pool: String,
44 transaction_count: u32,
45 cache: HashMap<String, String>,
46}
47
48impl AppContext {
49 fn new(connection: String) -> Self {
50 Self {
51 connection_pool: connection,
52 transaction_count: 0,
53 cache: HashMap::new(),
54 }
55 }
56
57 fn increment_transaction(&mut self) {
58 self.transaction_count += 1;
59 }
60
61 fn transaction_count(&self) -> u32 {
62 self.transaction_count
63 }
64
65 fn connection_pool(&self) -> &str {
66 &self.connection_pool
67 }
68
69 fn cache(&self) -> &HashMap<String, String> {
70 &self.cache
71 }
72
73 fn cache_mut(&mut self) -> &mut HashMap<String, String> {
74 &mut self.cache
75 }
76}
77
78struct CreateUser;
79struct FindUser;
80
81impl ApiOperation<AppContext, CreateUserProps> for CreateUser {
82 type Output = User;
83 type Error = UserError;
84
85 fn execute(context: &mut AppContext, parameters: &CreateUserProps) -> Result<User, UserError> {
86 if !parameters.email.contains('@') {
87 return Err(UserError::InvalidEmail);
88 }
89
90 context.increment_transaction();
91 let user = User {
92 id: context.transaction_count() as u64,
93 name: parameters.name.clone(),
94 email: parameters.email.clone(),
95 };
96
97 let cache_key = format!("user_{}", user.id);
98 let cache_value = format!("{}:{}", user.name, user.email);
99 context.cache_mut().insert(cache_key, cache_value);
100
101 Ok(user)
102 }
103}
104
105impl ApiOperation<AppContext, FindUserProps> for FindUser {
106 type Output = User;
107 type Error = UserError;
108
109 fn execute(context: &mut AppContext, parameters: &FindUserProps) -> Result<User, UserError> {
110 let cache_key = format!("user_{}", parameters.user_id);
111
112 if let Some(cached_data) = context.cache().get(&cache_key) {
113 let parts: Vec<&str> = cached_data.split(':').collect();
114 if parts.len() == 2 {
115 return Ok(User {
116 id: parameters.user_id,
117 name: parts[0].to_string(),
118 email: parts[1].to_string(),
119 });
120 }
121 }
122
123 Err(UserError::NotFound)
124 }
125}
126
127#[derive(Debug, Clone)]
129struct CreateProductProps {
130 name: String,
131 price: f64,
132 category: String,
133}
134
135#[derive(Debug, Clone)]
136struct FindProductProps {
137 product_id: u64,
138}
139
140#[derive(Debug, Clone)]
141struct Product {
142 id: u64,
143 name: String,
144 price: f64,
145 category: String,
146}
147
148#[derive(Debug)]
149enum ProductError {
150 InvalidPrice,
151 NotFound,
152}
153
154struct CreateProduct;
155struct FindProduct;
156
157impl ApiOperation<AppContext, CreateProductProps> for CreateProduct {
158 type Output = Product;
159 type Error = ProductError;
160
161 fn execute(
162 context: &mut AppContext,
163 parameters: &CreateProductProps,
164 ) -> Result<Product, ProductError> {
165 if parameters.price <= 0.0 || parameters.price.is_nan() {
166 return Err(ProductError::InvalidPrice);
167 }
168
169 context.increment_transaction();
170 let product = Product {
171 id: context.transaction_count() as u64,
172 name: parameters.name.clone(),
173 price: parameters.price,
174 category: parameters.category.clone(),
175 };
176
177 let cache_key = format!("product_{}", product.id);
178 let cache_value = format!("{}:{}:{}", product.name, product.price, product.category);
179 context.cache_mut().insert(cache_key, cache_value);
180
181 Ok(product)
182 }
183}
184
185impl ApiOperation<AppContext, FindProductProps> for FindProduct {
186 type Output = Product;
187 type Error = ProductError;
188
189 fn execute(
190 context: &mut AppContext,
191 parameters: &FindProductProps,
192 ) -> Result<Product, ProductError> {
193 let cache_key = format!("product_{}", parameters.product_id);
194
195 if let Some(cached_data) = context.cache().get(&cache_key) {
196 let parts: Vec<&str> = cached_data.split(':').collect();
197 if parts.len() == 3 {
198 if let Ok(price) = parts[1].parse::<f64>() {
199 return Ok(Product {
200 id: parameters.product_id,
201 name: parts[0].to_string(),
202 price,
203 category: parts[2].to_string(),
204 });
205 }
206 }
207 }
208
209 Err(ProductError::NotFound)
210 }
211}
212
213fn main() {
214 println!("š§ ApiThing Executor Pattern Example");
215 println!("====================================\n");
216
217 let mut executor = ApiExecutor::new(AppContext::new("production_db".to_string()));
219
220 println!("šļø Created ApiExecutor with context");
221 println!("š Connection: {}", executor.context().connection_pool());
222 println!(
223 "š¢ Initial transaction count: {}\n",
224 executor.context().transaction_count()
225 );
226
227 println!("š„ USER OPERATIONS");
229 println!("==================");
230
231 let users = vec![
233 ("Alice Johnson", "alice@company.com"),
234 ("Bob Smith", "bob@company.com"),
235 ("Carol Davis", "carol@company.com"),
236 ];
237
238 let mut created_users = Vec::new();
239 for (name, email) in users {
240 match executor.execute(
241 CreateUser,
242 &CreateUserProps {
243 name: name.to_string(),
244 email: email.to_string(),
245 },
246 ) {
247 Ok(user) => {
248 println!("ā
Created user: {} (ID: {})", user.name, user.id);
249 created_users.push(user);
250 }
251 Err(e) => {
252 println!("ā Failed to create user {}: {:?}", name, e);
253 return;
254 }
255 }
256 }
257
258 println!(
259 "š¢ Transaction count after user creation: {}\n",
260 executor.context().transaction_count()
261 );
262
263 println!("š¦ PRODUCT OPERATIONS");
265 println!("====================");
266
267 let products = vec![
269 ("Laptop Pro", 1299.99, "Electronics"),
270 ("Office Chair", 249.50, "Furniture"),
271 ("Coffee Mug", 12.99, "Kitchen"),
272 ];
273
274 let mut created_products = Vec::new();
275 for (name, price, category) in products {
276 match executor.execute(
277 CreateProduct,
278 &CreateProductProps {
279 name: name.to_string(),
280 price,
281 category: category.to_string(),
282 },
283 ) {
284 Ok(product) => {
285 println!(
286 "ā
Created product: {} (ID: {}, Price: ${:.2})",
287 product.name, product.id, product.price
288 );
289 created_products.push(product);
290 }
291 Err(e) => {
292 println!("ā Failed to create product {}: {:?}", name, e);
293 return;
294 }
295 }
296 }
297
298 println!(
299 "š¢ Transaction count after product creation: {}\n",
300 executor.context().transaction_count()
301 );
302
303 println!("š CROSS-FAMILY LOOKUPS");
305 println!("=======================");
306
307 println!("š Looking up created entities...");
311
312 if let Some(user) = created_users.first() {
314 match executor.execute(FindUser, &FindUserProps { user_id: user.id }) {
315 Ok(found_user) => println!("š¤ Found user: {} ({})", found_user.name, found_user.email),
316 Err(e) => println!("ā Failed to find user: {:?}", e),
317 }
318 }
319
320 if let Some(product) = created_products.first() {
322 match executor.execute(
323 FindProduct,
324 &FindProductProps {
325 product_id: product.id,
326 },
327 ) {
328 Ok(found_product) => println!(
329 "š¦ Found product: {} (${:.2})",
330 found_product.name, found_product.price
331 ),
332 Err(e) => println!("ā Failed to find product: {:?}", e),
333 }
334 }
335
336 println!("\nš CONTEXT INSPECTION");
338 println!("=====================");
339
340 let context = executor.context();
341 println!(
342 "š¢ Final transaction count: {}",
343 context.transaction_count()
344 );
345 println!("š¾ Cache entries: {} items", context.cache().len());
346
347 println!("š Cached items:");
349 for (key, value) in context.cache().iter() {
350 let display_value = if value.len() > 50 {
352 format!("{}...", &value[..47])
353 } else {
354 value.clone()
355 };
356 println!(" ⢠{}: {}", key, display_value);
357 }
358
359 println!("\nš” EXECUTOR BENEFITS");
361 println!("====================");
362
363 let final_context = executor.context();
365 println!("ā
Centralized state management:");
366 println!(
367 " ⢠{} total operations executed",
368 final_context.transaction_count()
369 );
370 println!(" ⢠{} entities cached", final_context.cache().len());
371 println!(" ⢠Single context shared across {} API families", 2);
372
373 println!("\nš Executor pattern example completed successfully!");
374 println!("š” Key advantages of ApiExecutor:");
375 println!(" ⢠Ergonomic API: executor.execute(Operation, ¶meters)");
376 println!(" ⢠Centralized context management with owned context");
377 println!(" ⢠Consistent patterns across different API families");
378 println!(" ⢠Easy context inspection and debugging");
379 println!(" ⢠Efficient state sharing (cache, connections, etc.)");
380}