use crate::prelude::*;
#[derive(Clone)]
pub struct NorthServiceOptions {
pub address: Option<String>,
pub name: Option<String>,
pub path_prefix: Option<String>,
pub version: Option<String>,
pub port: Option<u16>,
pub graceful_shutdown: bool,
pub enable_swagger: bool,
pub auto_acme: bool,
pub keep_alive: u32,
pub read_timeout: u32,
pub write_timeout: u32,
pub registry: Option<BoxedServiceRegistry>,
}
impl Default for NorthServiceOptions {
fn default() -> Self {
NorthServiceOptions {
address: Some("127.0.0.1".to_string()),
name: Some("service".to_string()),
path_prefix: Some("/".to_string()),
version: Some("latest".to_string()),
port: Some(5000),
graceful_shutdown: false,
enable_swagger: false,
auto_acme: false,
keep_alive: 1,
read_timeout: 2,
write_timeout: 2,
registry: None,
}
}
}
pub struct NorthService {
pub options: Box<NorthServiceOptions>,
pub state_data_list: Vec<Box<dyn NorthStateData>>,
#[cfg(feature = "api-poem")]
pub poem_app: Box<Route>,
}
pub struct NorthServiceBuilder<T> where T: PoemOpenApi + Clone + 'static {
pub(crate) options: Box<NorthServiceOptions>,
pub(crate) state_data_list: Vec<Box<dyn NorthStateData>>,
#[cfg(feature = "api-poem")]
pub(crate) poem_app: Route,
#[cfg(feature = "api-poem")]
pub(crate) custom_poem_app: Option<Box<Route>>,
#[cfg(feature = "api-poem")]
pub(crate) apis: Option<T>,
#[cfg(feature = "db-arango")]
pub(crate) db_connection: Option<ArcArangoConnection>,
}
impl<T> Default for NorthServiceBuilder<T> where T: PoemOpenApi + Clone + 'static
{
fn default() -> Self {
NorthServiceBuilder {
options: Box::new(Default::default()),
state_data_list: vec![],
#[cfg(feature = "api-poem")]
poem_app: Route::new(),
#[cfg(feature = "api-poem")]
custom_poem_app: None,
apis: None,
#[cfg(feature = "db-arango")]
db_connection: None,
}
}
}
impl<T> NorthServiceBuilder<T> where T: PoemOpenApi + Clone + 'static {
pub(crate) fn app_prefix(&self) -> String {
let mut prefix = self.options.path_prefix.clone().unwrap();
if prefix.starts_with('/') {
prefix = prefix.strip_prefix('/').unwrap().to_string();
};
prefix
}
pub(crate) fn full_address(&self) -> String {
let full_addr = format!(
"{}:{}",
self.options.address.clone().unwrap(),
self.options.port.unwrap(),
);
format!("http://{}/{}", full_addr, self.app_prefix())
}
}
impl<T> NorthServiceBuilderTrait<T> for NorthServiceBuilder<T> where T: PoemOpenApi + Clone + 'static {
#[cfg(feature = "api-poem")]
fn handler<E>(self, _path: impl AsRef<str>, _ep: E) -> Self
where
E: IntoEndpoint,
E::Endpoint: 'static,
{
self
}
fn version(mut self, version: &str) -> Self {
self.options.name = Some(version.to_string());
self
}
fn name(mut self, name: &str) -> Self {
self.options.name = Some(name.to_string());
self
}
fn path_prefix(mut self, path: &str) -> Self {
self.options.path_prefix = Some(path.to_string());
self
}
fn keep_alive(mut self, timeout: u32) -> Self {
self.options.keep_alive = timeout;
self
}
fn read_timeout(mut self, timeout: u32) -> Self {
self.options.read_timeout = timeout;
self
}
fn write_timeout(mut self, timeout: u32) -> Self {
self.options.write_timeout = timeout;
self
}
fn address(mut self, address: &str) -> Self {
self.options.address = Some(address.to_string());
self
}
fn port(mut self, port: u16) -> Self {
self.options.port = Some(port);
self
}
fn wrapper(self) -> Self {
todo!("Implement wrapper method")
}
#[cfg(feature = "api-poem")]
fn custom_http_server(mut self, app: Route) -> Self {
self.custom_poem_app = Some(Box::new(app));
self
}
#[cfg(feature = "api-poem")]
fn controller(mut self, api: T) -> Self{
self.apis = Some(api);
self
}
fn with_swagger(mut self, enable_swagger: bool) -> Self {
self.options.enable_swagger = enable_swagger;
self
}
#[cfg(feature = "db-arango")]
fn with_database(mut self, db_connection: Arc<DatabaseConnection>) -> Self {
self.db_connection = Some(ArcArangoConnection{connection: db_connection});
self
}
fn with_auto_acme(mut self, enable_acme: bool) -> Self {
self.options.auto_acme = enable_acme;
self
}
fn service_registry(mut self, registry: BoxedServiceRegistry) -> Self {
self.options.registry = Some(registry);
self
}
#[cfg(feature = "api-poem")]
fn with_data<S: NorthStateData + Send + Sync + 'static>(mut self, data: S) -> Self {
self.state_data_list.push(Box::new(data));
self
}
fn graceful_shutdown(mut self) -> Self {
self.options.graceful_shutdown = true;
self
}
#[cfg(feature = "api-poem")]
fn build(&mut self) -> NorthService {
let title = self.options.name.as_ref().unwrap().clone();
let version = self.options.version.as_ref().unwrap().clone();
let api_service = OpenApiService::new(
self.apis.clone().unwrap(),
title,
version,
).server(self.full_address());
let ui = api_service.swagger_ui();
let prefix = self.app_prefix();
let c_app = std::mem::take::<Option<Box<Route>>>(&mut self.custom_poem_app);
let def_app = std::mem::take::<Route>(&mut self.poem_app);
NorthService {
options: self.options.clone(),
state_data_list: self.state_data_list.clone(),
poem_app: c_app.unwrap_or(Box::new(def_app
.nest(format!("/{prefix}"), api_service)
.nest("/docs", ui)))
}
}
#[cfg(all(feature = "api-native", not(feature = "api-poem")))]
fn build(mut self) -> NorthService {
NorthService {
options: self.options.clone(),
}
}
}