actor_framework/client_trait.rs
1//! # ActorClient Trait
2//!
3//! Provides a common interface for resource‑specific clients, adding default `get` and `delete` methods built on top of a generic `ResourceClient`.
4use crate::{ActorEntity, FrameworkError, ResourceClient};
5use async_trait::async_trait;
6
7/// Trait for resource-specific clients to inherit standard CRUD operations.
8///
9/// This trait reduces boilerplate by providing default implementations for
10/// common operations like `get` and `delete`.
11///
12/// # Example
13///
14/// ```rust
15/// use actor_framework::{ActorClient, ActorEntity, FrameworkError, ResourceClient};
16/// use async_trait::async_trait;
17///
18/// // 1. Define Entity
19/// #[derive(Clone, Debug)]
20/// struct User { id: u32 }
21/// #[derive(Debug)] struct UserCreate;
22/// #[derive(Debug)] struct UserUpdate;
23/// #[derive(Debug)] enum UserAction {}
24/// #[derive(Debug)] struct UserError(String);
25///
26/// // Error must implement Display + Error + From<String> + Send + Sync
27/// impl std::fmt::Display for UserError {
28/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29/// write!(f, "{}", self.0)
30/// }
31/// }
32/// impl std::error::Error for UserError {}
33///
34/// impl From<String> for UserError {
35/// fn from(s: String) -> Self { UserError(s) }
36/// }
37///
38/// #[async_trait]
39/// impl ActorEntity for User {
40/// type Id = u32;
41/// type Create = UserCreate;
42/// type Update = UserUpdate;
43/// type Action = UserAction;
44/// type ActionResult = ();
45/// type Context = ();
46/// type Error = UserError;
47///
48/// fn from_create_params(id: u32, _: UserCreate) -> Result<Self, Self::Error> {
49/// Ok(Self { id })
50/// }
51/// async fn on_update(&mut self, _: UserUpdate, _: &()) -> Result<(), Self::Error> { Ok(()) }
52/// async fn handle_action(&mut self, _: UserAction, _: &()) -> Result<(), Self::Error> { Ok(()) }
53/// }
54///
55/// // 2. Define Client Wrapper
56/// struct UserClient {
57/// inner: ResourceClient<User>,
58/// }
59///
60/// // 3. Implement ActorClient
61/// #[async_trait]
62/// impl ActorClient<User> for UserClient {
63/// type Error = UserError;
64///
65/// fn inner(&self) -> &ResourceClient<User> {
66/// &self.inner
67/// }
68///
69/// fn map_error(e: FrameworkError) -> Self::Error {
70/// UserError(e.to_string())
71/// }
72/// }
73///
74/// // 4. Usage
75/// async fn usage(client: UserClient) {
76/// // get() and delete() are provided automatically!
77/// let _ = client.get(1).await;
78/// let _ = client.delete(1).await;
79/// }
80/// ```
81#[async_trait]
82pub trait ActorClient<T: ActorEntity>: Send + Sync {
83 /// The resource-specific error type.
84 type Error: From<String> + Send + Sync;
85
86 /// Access the inner generic ResourceClient.
87 fn inner(&self) -> &ResourceClient<T>;
88
89 /// Map framework errors to the specific resource error type.
90 fn map_error(e: FrameworkError) -> Self::Error;
91
92 /// Fetch an entity by ID.
93 #[tracing::instrument(skip(self))]
94 async fn get(&self, id: T::Id) -> Result<Option<T>, Self::Error> {
95 tracing::debug!("Sending request");
96 self.inner().get(id).await.map_err(Self::map_error)
97 }
98
99 /// Delete an entity by ID.
100 #[tracing::instrument(skip(self))]
101 async fn delete(&self, id: T::Id) -> Result<(), Self::Error> {
102 tracing::debug!("Sending request");
103 self.inner().delete(id).await.map_err(Self::map_error)
104 }
105}