-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implement service framework - Make main async - Implement Status enum - Implement Priority enum - Implement ServiceInfo struct - Implement ServiceInternals trait - Implement Service trait * Bot library - Add fern crate - Add humantime crate - Add log crate - Implement Bot - Implement BotBuilder - Refactor config Display trait implementation - Implement library is_debug() function - Implement library run(Bot) function - Implement log module (log::setup(), log::is_set_up() and log::get_min_log_level()) - Adapt main to new changes * WIP: Finish services framework Just a lot of refactoring and fixing. No time to describe all this now. Happy new year! :) * Finish services framework Too much to describe. It's done, that's it. This was one hell of a ride.
- Loading branch information
Showing
7 changed files
with
685 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use crate::service::{PinnedBoxedFuture, Service, ServiceManager, ServiceManagerBuilder}; | ||
|
||
pub struct BotBuilder { | ||
name: String, | ||
service_manager: ServiceManagerBuilder, | ||
} | ||
|
||
impl BotBuilder { | ||
pub fn new(name: &str) -> Self { | ||
Self { | ||
name: name.to_string(), | ||
service_manager: ServiceManager::builder(), | ||
} | ||
} | ||
|
||
pub fn with_service(mut self, service: Box<dyn Service>) -> Self { | ||
self.service_manager = self.service_manager.with_service(service); // The ServiceManagerBuilder itself will warn when adding a service multiple times | ||
|
||
self | ||
} | ||
|
||
pub fn with_services(mut self, services: Vec<Box<dyn Service>>) -> Self { | ||
for service in services { | ||
self.service_manager = self.service_manager.with_service(service); | ||
} | ||
|
||
self | ||
} | ||
|
||
pub fn build(self) -> Bot { | ||
Bot::from(self) | ||
} | ||
} | ||
|
||
pub struct Bot { | ||
pub name: String, | ||
pub service_manager: ServiceManager, | ||
} | ||
|
||
impl Bot { | ||
pub fn builder(name: &str) -> BotBuilder { | ||
BotBuilder::new(name) | ||
} | ||
|
||
//TODO: When Rust allows async trait methods to be object-safe, refactor this to use async instead of returning a future | ||
pub fn start(&mut self) -> PinnedBoxedFuture<'_, ()> { | ||
Box::pin(async move { | ||
self.service_manager.start_services().await; | ||
//TODO: Potential for further initialization here, like modules | ||
}) | ||
} | ||
|
||
//TODO: When Rust allows async trait methods to be object-safe, refactor this to use async instead of returning a future | ||
pub fn stop(&mut self) -> PinnedBoxedFuture<'_, ()> { | ||
Box::pin(async move { | ||
self.service_manager.stop_services().await; | ||
//TODO: Potential for further deinitialization here, like modules | ||
}) | ||
} | ||
} | ||
|
||
impl From<BotBuilder> for Bot { | ||
fn from(builder: BotBuilder) -> Self { | ||
Self { | ||
name: builder.name, | ||
service_manager: builder.service_manager.build(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
use crate::service::OverallStatus; | ||
use ::log::{error, info}; | ||
use bot::Bot; | ||
use std::time::SystemTime; | ||
|
||
pub mod bot; | ||
pub mod config; | ||
pub mod log; | ||
pub mod service; | ||
|
||
pub fn is_debug() -> bool { | ||
cfg!(debug_assertions) | ||
} | ||
|
||
pub async fn run(mut bot: Bot) { | ||
if !log::is_set_up() { | ||
eprintln!("Logger has not been set up!\n{} will exit.", bot.name); | ||
|
||
return; | ||
} | ||
|
||
let now = SystemTime::now(); | ||
|
||
bot.start().await; | ||
|
||
match now.elapsed() { | ||
Ok(elapsed) => info!("Startup took {}ms", elapsed.as_millis()), | ||
Err(error) => { | ||
error!( | ||
"Error getting elapsed startup time: {}\n{} will exit.", | ||
error, bot.name | ||
); | ||
|
||
return; | ||
} | ||
}; | ||
|
||
if bot.service_manager.overall_status().await != OverallStatus::Healthy { | ||
let status_tree = bot.service_manager.status_tree().await; | ||
|
||
error!("{} is not healthy! Some essential services did not start up successfully. Please check the logs.\nService status tree:\n{}\n{} will exit.", | ||
bot.name, | ||
status_tree, | ||
bot.name); | ||
return; | ||
} | ||
|
||
info!("{} is alive", bot.name,); | ||
|
||
//TODO: Add CLI commands | ||
match tokio::signal::ctrl_c().await { | ||
Ok(_) => { | ||
info!("Received SIGINT, {} will now shut down", bot.name); | ||
} | ||
Err(error) => { | ||
panic!("Error receiving SIGINT: {}\n{} will exit.", error, bot.name); | ||
} | ||
} | ||
|
||
bot.stop().await; | ||
|
||
info!("{} has shut down", bot.name); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use fern::colors::{Color, ColoredLevelConfig}; | ||
use log::{LevelFilter, SetLoggerError}; | ||
use std::{ | ||
io, | ||
sync::atomic::{AtomicBool, Ordering}, | ||
time::SystemTime, | ||
}; | ||
|
||
use crate::is_debug; | ||
|
||
static IS_LOGGER_SET_UP: AtomicBool = AtomicBool::new(false); | ||
|
||
pub fn is_set_up() -> bool { | ||
IS_LOGGER_SET_UP.load(Ordering::Relaxed) | ||
} | ||
|
||
pub fn setup() -> Result<(), SetLoggerError> { | ||
let colors = ColoredLevelConfig::new() | ||
.info(Color::Green) | ||
.debug(Color::Magenta) | ||
.warn(Color::Yellow) | ||
.error(Color::Red) | ||
.trace(Color::Cyan); | ||
|
||
fern::Dispatch::new() | ||
.format(move |out, message, record| { | ||
out.finish(format_args!( | ||
"[{} {: <25} {: <5}] {}", | ||
humantime::format_rfc3339_seconds(SystemTime::now()), | ||
record.target(), | ||
colors.color(record.level()), | ||
message | ||
)) | ||
}) | ||
.level(get_min_log_level()) | ||
.chain(io::stdout()) | ||
.apply()?; | ||
|
||
IS_LOGGER_SET_UP.store(true, Ordering::Relaxed); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_min_log_level() -> LevelFilter { | ||
if is_debug() { | ||
LevelFilter::Debug | ||
} else { | ||
LevelFilter::Info | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,57 @@ | ||
mod config; | ||
use ::log::{error, warn}; | ||
use lum::{ | ||
bot::Bot, | ||
config::{Config, ConfigHandler, ConfigParseError}, | ||
log, | ||
service::Service, | ||
}; | ||
|
||
pub const BOT_NAME: &str = "Lum"; | ||
const BOT_NAME: &str = "Lum"; | ||
|
||
fn main() { | ||
let config_handler = config::ConfigHandler::new(BOT_NAME.to_lowercase().as_str()); | ||
let config = match config_handler.get_config() { | ||
#[tokio::main] | ||
async fn main() { | ||
setup_logger(); | ||
|
||
if lum::is_debug() { | ||
warn!("THIS IS A DEBUG RELEASE!"); | ||
} | ||
|
||
let _config = match get_config() { | ||
Ok(config) => config, | ||
Err(err) => { | ||
panic!("Error reading config file: {}", err); | ||
error!( | ||
"Error reading config file: {}\n{} will exit.", | ||
err, BOT_NAME | ||
); | ||
|
||
return; | ||
} | ||
}; | ||
|
||
println!("Config: {}", config); | ||
let bot = Bot::builder(BOT_NAME) | ||
.with_services(initialize_services()) | ||
.build(); | ||
|
||
lum::run(bot).await; | ||
} | ||
|
||
fn setup_logger() { | ||
if let Err(error) = log::setup() { | ||
panic!( | ||
"Error setting up the Logger: {}\n{} will exit.", | ||
error, BOT_NAME | ||
); | ||
} | ||
} | ||
|
||
fn get_config() -> Result<Config, ConfigParseError> { | ||
let config_handler = ConfigHandler::new(BOT_NAME.to_lowercase().as_str()); | ||
config_handler.get_config() | ||
} | ||
|
||
fn initialize_services() -> Vec<Box<dyn Service>> { | ||
//TODO: Add services | ||
//... | ||
|
||
vec![] | ||
} |
Oops, something went wrong.