cljrs_value/resource.rs
1//! The `Resource` trait for I/O handles and other closeable resources.
2//!
3//! Resources are ref-counted with `Arc` (NOT `GcPtr`) so that they get
4//! deterministic cleanup when the last reference drops. The GC has no
5//! finalizers, so `GcPtr` would leak file descriptors.
6
7use std::any::Any;
8use std::fmt;
9use std::sync::Arc;
10
11use crate::error::ValueResult;
12
13/// A closeable, ref-counted resource (file handle, socket, etc.).
14///
15/// All methods take `&self` because the inner state is behind a `Mutex`.
16pub trait Resource: Send + Sync + fmt::Debug + 'static {
17 /// Close the resource. Idempotent — calling on an already-closed
18 /// resource is a no-op.
19 fn close(&self) -> ValueResult<()>;
20
21 /// Whether this resource has been closed.
22 fn is_closed(&self) -> bool;
23
24 /// A short tag for display, e.g. `"reader"`, `"writer"`.
25 fn resource_type(&self) -> &'static str;
26
27 /// Downcast support so native fns can get the concrete type.
28 fn as_any(&self) -> &dyn Any;
29}
30
31/// A wrapper around `Arc<dyn Resource>` that provides `Clone` and `Debug`.
32#[derive(Clone)]
33pub struct ResourceHandle(pub Arc<dyn Resource>);
34
35impl fmt::Debug for ResourceHandle {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "Resource({})", self.0.resource_type())
38 }
39}
40
41impl ResourceHandle {
42 pub fn new(r: impl Resource) -> Self {
43 Self(Arc::new(r))
44 }
45
46 pub fn close(&self) -> ValueResult<()> {
47 self.0.close()
48 }
49
50 pub fn is_closed(&self) -> bool {
51 self.0.is_closed()
52 }
53
54 pub fn resource_type(&self) -> &'static str {
55 self.0.resource_type()
56 }
57
58 /// Attempt to downcast to a concrete resource type.
59 pub fn downcast<T: Resource>(&self) -> Option<&T> {
60 self.0.as_any().downcast_ref::<T>()
61 }
62}