use std::io::{self, Write as IoWrite};
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use stillwater::effect::bracket::{acquiring, bracket, bracket2, bracket_full, BracketError};
use stillwater::{fail, from_fn, pure, Effect};
async fn example_basic_bracket() {
println!("\n=== Example 1: Basic Bracket Pattern ===");
let cleaned_up = Arc::new(AtomicBool::new(false));
let cleaned_up_clone = cleaned_up.clone();
#[derive(Clone)]
struct Resource {
id: u32,
}
let effect = bracket(
pure::<_, String, ()>(Resource { id: 42 }),
move |_resource: Resource| {
cleaned_up_clone.store(true, Ordering::SeqCst);
println!(" Resource cleaned up!");
async { Ok(()) }
},
|resource: &Resource| {
println!(" Using resource with id: {}", resource.id);
pure::<_, String, ()>(resource.id * 2)
},
);
let result = effect.run(&()).await;
println!(" Result: {:?}", result);
println!(" Cleanup ran: {}", cleaned_up.load(Ordering::SeqCst));
}
async fn example_bracket_use_failure() {
println!("\n=== Example 2: Bracket Cleanup on Use Failure ===");
let cleaned_up = Arc::new(AtomicBool::new(false));
let cleaned_up_clone = cleaned_up.clone();
let effect = bracket(
pure::<_, String, ()>("resource"),
move |_: &str| {
cleaned_up_clone.store(true, Ordering::SeqCst);
println!(" Cleanup ran despite failure!");
async { Ok(()) }
},
|_: &&str| fail::<i32, String, ()>("use phase failed".to_string()),
);
let result = effect.run(&()).await;
println!(" Result: {:?}", result);
println!(" Cleanup ran: {}", cleaned_up.load(Ordering::SeqCst));
}
async fn example_bracket2_lifo() {
println!("\n=== Example 3: Multiple Resources (LIFO Cleanup) ===");
let cleanup_order = Arc::new(std::sync::Mutex::new(Vec::new()));
let order1 = cleanup_order.clone();
let order2 = cleanup_order.clone();
let effect = bracket2(
{
println!(" Acquiring first resource...");
pure::<_, String, ()>("database")
},
{
println!(" Acquiring second resource...");
pure::<_, String, ()>("file")
},
move |_: &str| {
order1.lock().unwrap().push("database");
println!(" Released database");
async { Ok(()) }
},
move |_: &str| {
order2.lock().unwrap().push("file");
println!(" Released file");
async { Ok(()) }
},
|db: &&str, file: &&str| {
println!(" Using {} and {}", db, file);
pure::<_, String, ()>(format!("{} + {}", db, file))
},
);
let result = effect.run(&()).await;
println!(" Result: {:?}", result);
println!(
" Cleanup order: {:?} (LIFO: file first, then database)",
cleanup_order.lock().unwrap()
);
}
async fn example_acquiring_builder() {
println!("\n=== Example 4: Acquiring Builder Pattern ===");
let cleanup_order = Arc::new(std::sync::Mutex::new(Vec::new()));
let order1 = cleanup_order.clone();
let order2 = cleanup_order.clone();
let order3 = cleanup_order.clone();
let effect = acquiring(pure::<_, String, ()>("connection"), move |_: &str| {
order1.lock().unwrap().push("connection");
async { Ok(()) }
})
.and(pure::<_, String, ()>("transaction"), move |_: &str| {
order2.lock().unwrap().push("transaction");
async { Ok(()) }
})
.and(pure::<_, String, ()>("lock"), move |_: &str| {
order3.lock().unwrap().push("lock");
async { Ok(()) }
})
.with_flat3(|conn: &&str, txn: &&str, lock: &&str| {
println!(" Using: {}, {}, {}", conn, txn, lock);
pure::<_, String, ()>(format!("{} -> {} -> {}", conn, txn, lock))
});
let result = effect.run(&()).await;
println!(" Result: {:?}", result);
println!(
" Cleanup order (LIFO): {:?}",
cleanup_order.lock().unwrap()
);
}
async fn example_bracket_full() {
println!("\n=== Example 5: BracketFull for Explicit Errors ===");
println!("\n Case 1: Use fails, cleanup succeeds");
let result1 = bracket_full(
pure::<_, String, ()>(42),
|_: i32| async { Ok(()) },
|_: &i32| fail::<i32, String, ()>("use failed".to_string()),
)
.run(&())
.await;
match result1 {
Err(BracketError::UseError(e)) => println!(" UseError: {}", e),
other => println!(" Unexpected: {:?}", other),
}
println!("\n Case 2: Use succeeds, cleanup fails");
let result2 = bracket_full(
pure::<_, String, ()>(42),
|_: i32| async { Err::<(), String>("cleanup failed".to_string()) },
|val: &i32| pure::<_, String, ()>(*val * 2),
)
.run(&())
.await;
match result2 {
Err(BracketError::CleanupError(e)) => println!(" CleanupError: {}", e),
other => println!(" Unexpected: {:?}", other),
}
println!("\n Case 3: Both use and cleanup fail");
let result3 = bracket_full(
pure::<_, String, ()>(42),
|_: i32| async { Err::<(), String>("cleanup failed".to_string()) },
|_: &i32| fail::<i32, String, ()>("use failed".to_string()),
)
.run(&())
.await;
match result3 {
Err(BracketError::Both {
use_error,
cleanup_error,
}) => {
println!(" Both failed!");
println!(" Use error: {}", use_error);
println!(" Cleanup error: {}", cleanup_error);
}
other => println!(" Unexpected: {:?}", other),
}
}
async fn example_file_io() {
println!("\n=== Example 6: File I/O with Automatic Cleanup ===");
let file_closed = Arc::new(AtomicBool::new(false));
let file_closed_clone = file_closed.clone();
let temp_path = std::env::temp_dir().join("stillwater_bracket_example.txt");
let temp_path_clone = temp_path.clone();
#[derive(Debug, Clone)]
struct FileHandle {
path: PathBuf,
content: Arc<std::sync::Mutex<Vec<String>>>,
}
let effect = bracket(
from_fn(move |_: &()| {
println!(" Opening file: {:?}", temp_path_clone);
Ok::<_, io::Error>(FileHandle {
path: temp_path_clone.clone(),
content: Arc::new(std::sync::Mutex::new(Vec::new())),
})
}),
move |handle: FileHandle| {
file_closed_clone.store(true, Ordering::SeqCst);
let content = handle.content.lock().unwrap().clone();
println!(" Closing file and writing {} lines", content.len());
let path = handle.path.clone();
async move {
let mut file = std::fs::File::create(&path)?;
for line in content {
writeln!(file, "{}", line)?;
}
println!(" File closed successfully");
Ok(())
}
},
|handle: &FileHandle| {
let mut content = handle.content.lock().unwrap();
content.push("Line 1: Hello from bracket pattern!".to_string());
content.push("Line 2: Safe resource management".to_string());
content.push("Line 3: Cleanup guaranteed".to_string());
println!(" Writing {} lines to file", content.len());
pure::<_, io::Error, ()>(content.len())
},
);
let result = effect.run(&()).await;
println!(" Result: {:?}", result);
println!(" File closed: {}", file_closed.load(Ordering::SeqCst));
if temp_path.exists() {
let contents = std::fs::read_to_string(&temp_path).unwrap_or_default();
println!(" File contents:\n{}", contents);
let _ = std::fs::remove_file(&temp_path);
}
}
async fn example_connection_pool() {
println!("\n=== Example 7: Connection Pool Pattern ===");
#[derive(Clone)]
struct ConnectionPool {
active_connections: Arc<AtomicUsize>,
max_connections: usize,
}
#[derive(Debug)]
struct Connection {
id: usize,
}
impl ConnectionPool {
fn new(max: usize) -> Self {
ConnectionPool {
active_connections: Arc::new(AtomicUsize::new(0)),
max_connections: max,
}
}
fn acquire(&self) -> Result<Connection, String> {
let current = self.active_connections.fetch_add(1, Ordering::SeqCst);
if current >= self.max_connections {
self.active_connections.fetch_sub(1, Ordering::SeqCst);
Err("Pool exhausted".to_string())
} else {
Ok(Connection { id: current + 1 })
}
}
#[allow(dead_code)]
fn release(&self, _conn: Connection) {
self.active_connections.fetch_sub(1, Ordering::SeqCst);
}
}
#[derive(Clone)]
struct Env {
pool: ConnectionPool,
}
let pool = ConnectionPool::new(5);
let pool_for_release = pool.clone();
let env = Env { pool };
let effect = bracket(
from_fn(|env: &Env| {
let conn = env.pool.acquire()?;
println!(" Acquired connection #{}", conn.id);
Ok::<_, String>(conn)
}),
move |conn: Connection| {
println!(" Releasing connection #{}", conn.id);
pool_for_release
.active_connections
.fetch_sub(1, Ordering::SeqCst);
async move { Ok(()) }
},
|conn: &Connection| {
println!(" Using connection #{} for query", conn.id);
pure::<_, String, Env>(format!("Query result from connection #{}", conn.id))
},
);
let result = effect.run(&env).await;
println!(" Result: {:?}", result);
println!(
" Active connections after: {}",
env.pool.active_connections.load(Ordering::SeqCst)
);
}
#[tokio::main]
async fn main() {
println!("Resource Scopes Examples");
println!("========================");
println!("Demonstrating the bracket pattern for safe resource management.");
example_basic_bracket().await;
example_bracket_use_failure().await;
example_bracket2_lifo().await;
example_acquiring_builder().await;
example_bracket_full().await;
example_file_io().await;
example_connection_pool().await;
println!("\n=== All examples completed successfully! ===");
}