Skip to content

Commit fd352a9

Browse files
committed
fix: access to the internal service state by handlers
1 parent 3e374cf commit fd352a9

File tree

4 files changed

+74
-63
lines changed

4 files changed

+74
-63
lines changed

examples/services/http/src/main.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
use std::any::Any;
21
use std::sync::Arc;
32

43
use axum::extract::State;
54
use axum::routing::get;
65
use mikros::FutureMutex;
7-
use mikros::http::{ServiceInternalState, ServiceState};
6+
use mikros::http::ServiceState;
87
use mikros::service::builder::ServiceBuilder;
9-
//use mikros::FutureMutex;
108

119
#[derive(Clone, Default)]
1210
pub struct AppState {
@@ -19,18 +17,7 @@ impl AppState {
1917
}
2018
}
2119

22-
impl ServiceInternalState for AppState {
23-
fn clone_box(&self) -> Box<dyn ServiceInternalState> {
24-
Box::new(self.clone())
25-
}
26-
27-
fn as_any(&self) -> &dyn Any {
28-
self
29-
}
30-
}
31-
3220
// Handler method for the first endpoint
33-
//async fn handler_one(ctx: Option<State<Arc<Context>>>) -> String {
3421
async fn handler_one(State(state): State<Arc<FutureMutex<ServiceState>>>) -> String {
3522
println!("Handler One");
3623
let context = state.lock().await.context();
@@ -43,11 +30,12 @@ async fn handler_one(State(state): State<Arc<FutureMutex<ServiceState>>>) -> Str
4330
async fn handler_two(State(state): State<Arc<FutureMutex<ServiceState>>>) -> String {
4431
println!("Handler Two");
4532

46-
if let Some(app_state) = state.lock().await.state::<AppState>().await {
47-
println!("App State current value: {}", app_state.value);
48-
app_state.increase();
49-
let mut x = app_state.clone_box();
50-
x.in
33+
let context = state.lock().await.context();
34+
if let Some(app_state) = &state.lock().await.app_state {
35+
let mut locked = app_state.as_ref().lock().await;
36+
let x = locked.downcast_mut::<AppState>().unwrap();
37+
x.value += 1;
38+
context.logger().info(format!("value: {}", x.value).as_str());
5139
}
5240

5341
format!("Handler Two")
@@ -62,7 +50,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
6250
let state = AppState::default();
6351
let svc = ServiceBuilder::default()
6452
.http_with_state(api, Box::new(state))
65-
// .http_with_state(api, Arc::new(FutureMutex::new(state)))
6653
// .http(api)
6754
.build();
6855

src/http/mod.rs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,55 @@ use crate::service::context::Context;
88
#[derive(Clone)]
99
pub struct ServiceState {
1010
context: Arc<Context>,
11-
state: Arc<Mutex<Option<Box<dyn ServiceInternalState>>>>
11+
12+
/// This member gives access to the service own state (added when it is
13+
/// created, with the ServiceBuilder::http_with_state API).
14+
///
15+
/// One can retrieve the proper service state structure like the example:
16+
///
17+
/// ```
18+
/// use std::sync::Arc;
19+
///
20+
/// use axum::extract::State;
21+
/// use mikros::FutureMutex;
22+
/// use mikros::http::ServiceState;
23+
///
24+
/// #[derive(Clone)]
25+
/// pub struct AppState;
26+
///
27+
/// async fn handler(State(state): State<Arc<FutureMutex<ServiceState>>>) -> String {
28+
/// if let Some(app_state) = &state.lock().await.app_state {
29+
/// let mut locked = app_state.as_ref().lock().await;
30+
/// let svc_state = locked.downcast_mut::<AppState>().unwrap();
31+
///
32+
/// // svc_state can be manipulated from here.
33+
/// }
34+
///
35+
/// "Ok".to_string()
36+
/// }
37+
/// ```
38+
///
39+
pub app_state: Option<Arc<Mutex<Box<dyn Any + Send + Sync>>>>,
1240
}
1341

1442
impl ServiceState {
1543
pub(crate) fn new(context: &Context) -> Self {
1644
Self {
1745
context: Arc::new(context.clone()),
18-
state: Arc::new(Mutex::new(None)),
46+
app_state: None,
1947
}
2048
}
2149

22-
pub(crate) fn new_with_state(context: &Context, state: Box<dyn ServiceInternalState>) -> Self {
23-
Self {
24-
context: Arc::new(context.clone()),
25-
state: Arc::new(Mutex::new(Some(state))),
26-
}
50+
pub(crate) fn new_with_state(context: &Context, internal_state: Arc<Mutex<Box<dyn Any + Send + Sync>>>) -> Self {
51+
let mut s = Self::new(context);
52+
s.app_state = Some(internal_state);
53+
s
2754
}
2855

56+
/// Allows retrieving the mikros Context object from a handler state. Even
57+
/// if the service was not initialized with state, the context will always
58+
/// be available.
2959
pub fn context(&self) -> Arc<Context> {
3060
self.context.clone()
3161
}
32-
33-
pub async fn state<T: Clone + 'static>(&self) -> Option<Arc<T>> {
34-
let state = self.state.lock().await;
35-
state.as_ref()?.as_any().downcast_ref::<T>().map(|t|Arc::new(t.clone()))
36-
}
37-
}
38-
39-
pub trait ServiceInternalState: Send + Sync + 'static {
40-
fn clone_box(&self) -> Box<dyn ServiceInternalState>;
41-
fn as_any(&self) -> &dyn Any;
42-
}
43-
44-
impl Clone for Box<dyn ServiceInternalState> {
45-
fn clone(&self) -> Self {
46-
self.clone_box()
47-
}
4862
}

src/service/builder.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
1+
use std::any::Any;
12
use std::collections::HashMap;
2-
use std::sync::{Arc};
33
use std::convert::Infallible;
4+
use std::sync::Arc;
45

56
use axum::Router;
67
use futures::lock::Mutex;
78
use http::{request::Request, response::Response};
89
use tonic::body::BoxBody;
910
use tonic::server::NamedService;
1011

11-
use crate::{definition, errors as merrors, plugin};
12-
use crate::http::{ServiceInternalState, ServiceState};
13-
use crate::service::native::{NativeService, Native};
14-
use crate::service::script::{ScriptService, Script};
12+
use crate::http::ServiceState;
13+
use crate::service::Service;
1514
use crate::service::grpc::Grpc;
1615
use crate::service::http::Http;
1716
use crate::service::lifecycle::Lifecycle;
18-
use crate::service::Service;
17+
use crate::service::native::{NativeService, Native};
18+
use crate::service::script::{ScriptService, Script};
19+
use crate::{definition, errors as merrors, plugin};
1920

21+
/// The builder API to build a mikros service instance. It allows to initialize
22+
/// each type of configured service (inside the service.toml file) with its own
23+
/// data type.
2024
pub struct ServiceBuilder {
2125
pub(crate) servers: HashMap<String, Box<dyn plugin::service::Service>>,
2226
pub(crate) features: Vec<Box<dyn plugin::feature::Feature>>,
@@ -104,12 +108,19 @@ impl ServiceBuilder {
104108
self
105109
}
106110

107-
pub fn http_with_state(mut self, router: Router<Arc<Mutex<ServiceState>>>, state: Box<dyn ServiceInternalState>) -> Self {
111+
/// Initializes the HTTP service type with the required structure
112+
/// implementing the service endpoint handlers. It also receives an
113+
/// object that will be passed inside the handlers state.
114+
pub fn http_with_state(mut self, router: Router<Arc<Mutex<ServiceState>>>, state: Box<dyn Any + Send + Sync>) -> Self {
108115
self.servers.insert(definition::ServiceKind::Http.to_string(), Box::new(Http::new_with_state(router, state)));
109116
self
110117
}
111118

112-
pub fn http_with_lifecycle_and_state<L>(mut self, router: Router<Arc<Mutex<ServiceState>>>, lifecycle: Arc<Mutex<L>>, state: Box<dyn ServiceInternalState>) -> Self
119+
/// Initializes the HTTP service type with the required structure
120+
/// implementing the service endpoint handlers and another with
121+
/// implementing the Lifecycle API. It also receives an object that
122+
/// will be passed inside the handlers state.
123+
pub fn http_with_lifecycle_and_state<L>(mut self, router: Router<Arc<Mutex<ServiceState>>>, lifecycle: Arc<Mutex<L>>, state: Box<dyn Any + Send + Sync>) -> Self
113124
where
114125
L: Lifecycle + 'static,
115126
{

src/service/http/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
use std::any::Any;
12
use std::collections::HashMap;
2-
use std::ops::Deref;
33
use std::sync::Arc;
44

55
use axum::Router;
@@ -8,17 +8,17 @@ use logger::fields::FieldValue;
88
use tokio::net::TcpListener;
99
use tokio::sync::watch::Receiver;
1010

11-
use crate::{definition, env, errors as merrors, plugin};
12-
use crate::http::{ServiceInternalState, ServiceState};
11+
use crate::http::ServiceState;
1312
use crate::service::context::Context;
1413
use crate::service::lifecycle::Lifecycle;
14+
use crate::{definition, env, errors as merrors, plugin};
1515

1616
#[derive(Clone)]
1717
pub(crate) struct Http {
1818
port: i32,
1919
router: Router<Arc<Mutex<ServiceState>>>,
2020
lifecycle: Option<Box<Arc<Mutex<dyn Lifecycle>>>>,
21-
internal_state: Arc<Mutex<Option<Box<dyn ServiceInternalState>>>>
21+
app_state: Option<Arc<Mutex<Box<dyn Any + Send + Sync>>>>,
2222
}
2323

2424
impl Http {
@@ -27,7 +27,7 @@ impl Http {
2727
port: 0,
2828
router,
2929
lifecycle: None,
30-
internal_state: Arc::new(Mutex::new(None))
30+
app_state: None,
3131
}
3232
}
3333

@@ -40,19 +40,19 @@ impl Http {
4040
s
4141
}
4242

43-
pub fn new_with_state(router: Router<Arc<Mutex<ServiceState>>>, state: Box<dyn ServiceInternalState>) -> Self {
43+
pub fn new_with_state(router: Router<Arc<Mutex<ServiceState>>>, state: Box<dyn Any + Send + Sync>) -> Self {
4444
let mut s = Self::new(router);
45-
s.internal_state = Arc::new(Mutex::new(Some(state)));
45+
s.app_state = Some(Arc::new(Mutex::new(state)));
4646
s
4747
}
4848

49-
pub fn new_with_lifecycle_and_state<L>(router: Router<Arc<Mutex<ServiceState>>>, lifecycle: Arc<Mutex<L>>, state: Box<dyn ServiceInternalState>) -> Self
49+
pub fn new_with_lifecycle_and_state<L>(router: Router<Arc<Mutex<ServiceState>>>, lifecycle: Arc<Mutex<L>>, state: Box<dyn Any + Send + Sync>) -> Self
5050
where
5151
L: Lifecycle + 'static,
5252
{
5353
let mut s = Self::new(router);
5454
s.lifecycle = Some(Box::new(lifecycle));
55-
s.internal_state = Arc::new(Mutex::new(Some(state)));
55+
s.app_state = Some(Arc::new(Mutex::new(state)));
5656
s
5757
}
5858
}
@@ -108,10 +108,9 @@ impl plugin::service::Service for Http {
108108
shutdown_rx.changed().await.ok();
109109
};
110110

111-
let internal_state = self.internal_state.clone();
112-
let state = match internal_state.lock().await.deref() {
111+
let state = match &self.app_state {
113112
None => ServiceState::new(ctx),
114-
Some(state) => ServiceState::new_with_state(ctx, state.clone_box())
113+
Some(st) => ServiceState::new_with_state(ctx, st.clone())
115114
};
116115

117116
let app = Router::new().merge(self.router.clone()).with_state(Arc::new(Mutex::new(state)));

0 commit comments

Comments
 (0)