Skip to content

Commit 74467dd

Browse files
committed
Refactor service framework (#25)
* Service framework improvements - Way better handling of mutable/immutable attributes - Less Mutexes - Better handling of passing references through the service framework's API - Reimplement get_service accepting a TypeId as a generic parameter for easier usage - Reimplement status_map and status_tree as a result of the above adaptations, resulting in way simpler versions * More service framework improvements - Replace all Mutexes with RwLock - Remove status_map method of ServiceManager - Services vector in ServiceManager now wraps the Service trait objects in a RwLock to potentially make them available mutably through the public API of ServiceManager * Implement get_service<T> method - Add downcast-rs crate - Implement get_service<T> method of ServiceManager Had to use unsafe Rust for this. Tried it with safe Rust for 3 days and couldn't do it. With unsafe Rust, it's very easy. It's also still kinda safe, as the crash case is checked and prevented before going into the unsafe block. * Finish refactor of service framework - ServiceManager now holds an Arc to itself - Self-Arc is now passed to services when initializing them, so they can access other services and copy Arcs to those for themselves - Implement SetLock<T> struct which is a wrapper around Option<T> for lazy-initialization - ServiceManagerBuilder handles the creation and injection of the Self-Arc of ServiceManager. That's why the build() method is now async and the From trait had to be removed. The From trait cannot be implemented async. - To keep everything consistent, the From trait has also been removed from the BotBuilder and the build() method becase async. * Adapt Discord service - Adapt Discord service to new service framework and SetLock type
1 parent 9c07b1e commit 74467dd

File tree

7 files changed

+335
-211
lines changed

7 files changed

+335
-211
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ repository = "https://github.com/Kitt3120/lum"
1313

1414
[dependencies]
1515
dirs = "5.0.1"
16+
downcast-rs = "1.2.0"
1617
fern = { version = "0.6.2", features = ["chrono", "colored", "date-based"] }
1718
humantime = "2.1.0"
1819
log = { version = "0.4.20", features = ["serde"] }

src/bot.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::sync::Arc;
2+
3+
use tokio::sync::RwLock;
4+
15
use crate::service::{PinnedBoxedFuture, Service, ServiceManager, ServiceManagerBuilder};
26

37
pub struct BotBuilder {
@@ -13,28 +17,31 @@ impl BotBuilder {
1317
}
1418
}
1519

16-
pub fn with_service(mut self, service: Box<dyn Service>) -> Self {
17-
self.service_manager = self.service_manager.with_service(service); // The ServiceManagerBuilder itself will warn when adding a service multiple times
20+
pub async fn with_service(mut self, service: Arc<RwLock<dyn Service>>) -> Self {
21+
self.service_manager = self.service_manager.with_service(service).await; // The ServiceManagerBuilder itself will warn when adding a service multiple times
1822

1923
self
2024
}
2125

22-
pub fn with_services(mut self, services: Vec<Box<dyn Service>>) -> Self {
26+
pub async fn with_services(mut self, services: Vec<Arc<RwLock<dyn Service>>>) -> Self {
2327
for service in services {
24-
self.service_manager = self.service_manager.with_service(service);
28+
self.service_manager = self.service_manager.with_service(service).await;
2529
}
2630

2731
self
2832
}
2933

30-
pub fn build(self) -> Bot {
31-
Bot::from(self)
34+
pub async fn build(self) -> Bot {
35+
Bot {
36+
name: self.name,
37+
service_manager: self.service_manager.build().await,
38+
}
3239
}
3340
}
3441

3542
pub struct Bot {
3643
pub name: String,
37-
pub service_manager: ServiceManager,
44+
pub service_manager: Arc<RwLock<ServiceManager>>,
3845
}
3946

4047
impl Bot {
@@ -45,25 +52,16 @@ impl Bot {
4552
//TODO: When Rust allows async trait methods to be object-safe, refactor this to use async instead of returning a future
4653
pub fn start(&mut self) -> PinnedBoxedFuture<'_, ()> {
4754
Box::pin(async move {
48-
self.service_manager.start_services().await;
55+
self.service_manager.write().await.start_services().await;
4956
//TODO: Potential for further initialization here, like modules
5057
})
5158
}
5259

5360
//TODO: When Rust allows async trait methods to be object-safe, refactor this to use async instead of returning a future
5461
pub fn stop(&mut self) -> PinnedBoxedFuture<'_, ()> {
5562
Box::pin(async move {
56-
self.service_manager.stop_services().await;
63+
self.service_manager.write().await.stop_services().await;
5764
//TODO: Potential for further deinitialization here, like modules
5865
})
5966
}
6067
}
61-
62-
impl From<BotBuilder> for Bot {
63-
fn from(builder: BotBuilder) -> Self {
64-
Self {
65-
name: builder.name,
66-
service_manager: builder.service_manager.build(),
67-
}
68-
}
69-
}

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod bot;
77
pub mod config;
88
pub mod log;
99
pub mod service;
10+
pub mod setlock;
1011

1112
pub fn is_debug() -> bool {
1213
cfg!(debug_assertions)
@@ -35,15 +36,17 @@ pub async fn run(mut bot: Bot) {
3536
}
3637
};
3738

38-
if bot.service_manager.overall_status().await != OverallStatus::Healthy {
39-
let status_tree = bot.service_manager.status_tree().await;
39+
let service_manager = bot.service_manager.read().await;
40+
if service_manager.overall_status().await != OverallStatus::Healthy {
41+
let status_tree = service_manager.status_tree().await;
4042

4143
error!("{} is not healthy! Some essential services did not start up successfully. Please check the logs.\nService status tree:\n{}\n{} will exit.",
4244
bot.name,
4345
status_tree,
4446
bot.name);
4547
return;
4648
}
49+
drop(service_manager);
4750

4851
info!("{} is alive", bot.name,);
4952

src/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use std::sync::Arc;
2+
13
use ::log::{error, warn};
24
use lum::{
35
bot::Bot,
46
config::{Config, ConfigHandler},
57
log,
68
service::{discord::DiscordService, Service},
79
};
10+
use tokio::sync::RwLock;
811

912
const BOT_NAME: &str = "Lum";
1013

@@ -31,7 +34,9 @@ async fn main() {
3134

3235
let bot = Bot::builder(BOT_NAME)
3336
.with_services(initialize_services(&config))
34-
.build();
37+
.await
38+
.build()
39+
.await;
3540

3641
lum::run(bot).await;
3742
}
@@ -45,12 +50,12 @@ fn setup_logger() {
4550
}
4651
}
4752

48-
fn initialize_services(config: &Config) -> Vec<Box<dyn Service>> {
53+
fn initialize_services(config: &Config) -> Vec<Arc<RwLock<dyn Service>>> {
4954
//TODO: Add services
5055
//...
5156

5257
let discord_service =
5358
DiscordService::new(config.discord_token.as_str(), config.discord_timeout);
5459

55-
vec![Box::new(discord_service)]
60+
vec![Arc::new(RwLock::new(discord_service))]
5661
}

0 commit comments

Comments
 (0)