#![forbid(unsafe_code)]
use std::future::Future;
pub trait Use {
fn use_with<U, F: FnOnce(Self) -> U>(self, f: F) -> U
where
Self: Sized,
{
f(self)
}
fn use_with_async<F, Fut, U>(self, f: F) -> impl Future<Output = U> + Send
where
Self: Sized + Send,
F: FnOnce(Self) -> Fut + Send,
Fut: Future<Output = U> + Send,
{
async { f(self).await }
}
}
impl<T> Use for T {}
#[macro_export]
macro_rules! using {
($resource:expr, $param:ident -> $body:block) => {{
let $param = $resource;
$body
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[test]
fn test_resource_usage() {
let drop_flag = Arc::new(Mutex::new(false));
{
let drop_flag = drop_flag.clone();
struct TestResource(Arc<Mutex<bool>>);
impl Drop for TestResource {
fn drop(&mut self) {
let mut flag = self.0.lock().unwrap();
*flag = true;
println!("TestResource is dropped!");
}
}
TestResource(drop_flag).use_with(|_res| {
println!("Using the resource");
});
}
assert!(*drop_flag.lock().unwrap(), "Resource was not dropped");
}
#[test]
fn test_return_value() {
struct Resource;
impl Drop for Resource {
fn drop(&mut self) {
println!("Resource is dropped!");
}
}
let resource = Resource;
let result = resource.use_with(|_res| {
42
});
assert_eq!(result, 42);
}
#[test]
fn test_multiple_resources() {
struct Resource(&'static str);
impl Drop for Resource {
fn drop(&mut self) {
println!("{} is dropped!", self.0);
}
}
let res1 = Resource("Resource 1");
let res2 = Resource("Resource 2");
res1.use_with(|r1| {
println!("Using {}", r1.0);
});
res2.use_with(|r2| {
println!("Using {}", r2.0);
});
}
#[test]
fn test_nested_use_with() {
struct Resource(&'static str);
impl Drop for Resource {
fn drop(&mut self) {
println!("{} is dropped!", self.0);
}
}
let outer = Resource("Outer Resource");
let inner = Resource("Inner Resource");
outer.use_with(|o| {
println!("Using {}", o.0);
inner.use_with(|i| {
println!("Using {}", i.0);
});
});
}
#[test]
#[should_panic(expected = "Intentional panic")]
fn test_panic_in_use_with() {
struct Resource;
impl Drop for Resource {
fn drop(&mut self) {
println!("Resource is dropped!");
}
}
let resource = Resource;
resource.use_with(|_res| {
panic!("Intentional panic");
});
}
#[test]
fn test_resource_modification() {
struct Resource {
value: i32,
}
impl Drop for Resource {
fn drop(&mut self) {
println!("Resource with value {} is dropped!", self.value);
}
}
let resource = Resource { value: 10 };
resource.use_with(|mut res| {
res.value += 5;
println!("Modified value: {}", res.value);
assert_eq!(res.value, 15);
});
}
#[test]
fn test_resource_with_dependencies() {
struct Dependency;
impl Drop for Dependency {
fn drop(&mut self) {
println!("Dependency is dropped!");
}
}
#[allow(dead_code)]
struct Resource<'a> {
dep: &'a Dependency,
}
impl<'a> Drop for Resource<'a> {
fn drop(&mut self) {
println!("Resource is dropped!");
}
}
let dependency = Dependency;
let resource = Resource { dep: &dependency };
resource.use_with(|_res| {
println!("Using resource with dependency");
});
}
#[test]
fn test_use_with_modifies_external_state() {
#[derive(Default)]
struct Resource;
let mut external_state = 0;
Resource::default().use_with(|_res| {
external_state += 1;
});
assert_eq!(external_state, 1);
}
#[tokio::test]
async fn test_use_with_async_modifies_external_state() {
#[derive(Default)]
struct Resource;
let shared_state = Arc::new(tokio::sync::Mutex::new(0));
{
let state = Arc::clone(&shared_state);
Resource::default()
.use_with_async(|_res| async move {
let mut num = state.lock().await;
*num += 1;
println!("Shared state incremented: {}", *num);
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
})
.await;
}
assert_eq!(*shared_state.lock().await, 1);
}
}