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}