use crate::tina::data::date_time::{LocalDate, LocalDateTime, LocalTime};
use crate::{
app_error_from_none_static, app_system_error,
tina::core::{domain::login_user::LoginUser, service::transaction::ITransactionService},
};
use dashmap::DashMap;
use serde::{Serialize, Serializer};
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::sync::RwLock;
use std::time::Duration;
use std::{
any::{Any, TypeId},
borrow::Cow,
};
use crate::tina::data::id::Id;
use crate::tina::data::request_ext::RequestTime;
use crate::tina::data::AppResult;
use crate::tina::i18n::ResourceBundle;
use crate::tina::server::application::{AppConfig, Application, FromApplication};
#[async_trait]
pub trait ISessionBase {
fn get_locale(&self) -> &str;
async fn get_request_time_millis(&self) -> AppResult<i64>;
async fn get_request_date_time(&self) -> AppResult<LocalDateTime>;
async fn get_request_date(&self) -> AppResult<LocalDate>;
async fn get_request_time(&self) -> AppResult<LocalTime>;
fn get_application(&self) -> &Application;
fn get_token(&self) -> Option<Cow<str>>;
fn get_user(&self) -> Option<Arc<LoginUser>>;
fn set_user(&self, user: Arc<LoginUser>) -> AppResult<()>;
fn get_user_id(&self) -> Option<Id>;
fn is_enable_token(&self) -> bool;
#[cfg(feature = "server-http")]
fn get_route_config(&self) -> AppResult<Arc<crate::tina::server::http::route::RouteBaseConfig>>;
#[cfg(feature = "server-http")]
fn disable_request_log_param(&self) -> AppResult<()>;
#[cfg(feature = "server-http")]
fn is_disable_request_log_param(&self) -> AppResult<bool>;
#[cfg(feature = "server-http")]
fn disable_oper_log(&self) -> AppResult<()>;
#[cfg(feature = "server-http")]
fn is_disable_oper_log(&self) -> AppResult<bool>;
fn get_transaction_service(&self) -> AppResult<Arc<dyn ITransactionService<Client = Box<dyn Any + Send + 'static>>>>;
fn set_transaction_service(&self, service: Arc<dyn ITransactionService<Client = Box<dyn Any + Send + 'static>>>) -> AppResult<()>;
async fn sleep(&self, duration: Duration);
}
pub trait ISession: ISessionBase + Debug + Any + Send + Sync + 'static {}
#[derive(Clone)]
pub struct Session {
pub(crate) inner: Arc<dyn ISession>,
}
impl Debug for Session {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Session").finish()
}
}
impl Session {
pub fn new(inner: impl ISession + 'static) -> Session {
Session {
inner: Arc::new(inner),
}
}
}
impl Default for Session {
fn default() -> Self {
Self::new(SessionInner::default())
}
}
impl Deref for Session {
type Target = Arc<dyn ISession>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<Session> for Session {
fn as_ref(&self) -> &Session {
self
}
}
impl Serialize for Session {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}
}
pub trait ISessionAttribute {
fn get_extension<T: Send + Sync + 'static>(&self) -> AppResult<Option<Arc<RwLock<T>>>>;
fn set_extension<T: Send + Sync + 'static>(&self, value: T) -> AppResult<()>;
fn get_attribute<T: Send + Sync + 'static>(&self, name: &str) -> AppResult<Option<Arc<RwLock<T>>>>;
fn set_attribute<T: Send + Sync + 'static>(&self, name: &str, value: T) -> AppResult<()>;
}
pub(crate) struct SessionInner {
pub(crate) application: Application,
pub(crate) enable_token: bool,
pub(crate) token: Option<String>,
pub(crate) locale: Arc<String>,
pub(crate) request_time: Arc<RequestTime>,
#[cfg(feature = "server-http")]
pub(crate) route_config: Option<Arc<crate::tina::server::http::route::RouteBaseConfig>>,
pub(crate) transaction_flag: AtomicBool,
pub(crate) extension: Arc<DashMap<TypeId, Arc<dyn Any + Send + Sync + 'static>>>,
pub(crate) attributes: Arc<DashMap<String, Arc<dyn Any + Send + Sync + 'static>>>,
}
impl Debug for SessionInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SessionInner")
.field("enable_token", &self.enable_token)
.field("locale", &self.locale)
.field("transaction_flag", &self.transaction_flag)
.finish()
}
}
impl Default for SessionInner {
fn default() -> Self {
SessionInner {
application: AppConfig::new().into(),
enable_token: false,
token: None,
locale: Arc::new("".to_string()),
request_time: Arc::new(Default::default()),
#[cfg(feature = "server-http")]
route_config: None,
transaction_flag: AtomicBool::new(false),
extension: Default::default(),
attributes: Arc::new(Default::default()),
}
}
}
impl ISession for SessionInner {}
#[async_trait]
#[allow(deprecated)]
impl ISessionBase for SessionInner {
fn get_locale(&self) -> &str {
self.locale.as_str()
}
async fn get_request_time_millis(&self) -> AppResult<i64> {
self.request_time.get_date_time().await.map(|v| v.naive_utc().timestamp_millis())
}
async fn get_request_date_time(&self) -> AppResult<LocalDateTime> {
self.request_time.get_date_time().await
}
async fn get_request_date(&self) -> AppResult<LocalDate> {
self.request_time.get_date_time().await.map(|v| LocalDate(v.date()))
}
async fn get_request_time(&self) -> AppResult<LocalTime> {
self.request_time.get_date_time().await.map(|v| LocalTime(v.time()))
}
fn get_application(&self) -> &Application {
&self.application
}
fn get_token(&self) -> Option<Cow<str>> {
self.token.as_ref().map(|s| Cow::Borrowed(s.as_str()))
}
fn get_user(&self) -> Option<Arc<LoginUser>> {
let r = self.get_extension::<Arc<LoginUser>>();
match r {
Ok(user) => match user {
None => None,
Some(u) => match u.read() {
Ok(u) => Some(u.clone()),
Err(err) => {
tracing::error!("{}", err);
None
}
},
},
Err(_) => None,
}
}
fn set_user(&self, user: Arc<LoginUser>) -> AppResult<()> {
self.set_extension(user)
}
fn get_user_id(&self) -> Option<Id> {
let user = self.get_user();
match user {
None => None,
Some(user) => user.user_id.clone(),
}
}
fn is_enable_token(&self) -> bool {
self.enable_token
}
#[cfg(feature = "server-http")]
fn get_route_config(&self) -> AppResult<Arc<crate::tina::server::http::route::RouteBaseConfig>> {
match self.route_config.as_ref() {
None => Err(crate::app_system_error!("no request found in session.")),
Some(route_config) => Ok(route_config.clone()),
}
}
#[cfg(feature = "server-http")]
fn disable_request_log_param(&self) -> AppResult<()> {
self.set_extension(crate::tina::data::http::DisableReqLogParamFlag)
}
#[cfg(feature = "server-http")]
fn is_disable_request_log_param(&self) -> AppResult<bool> {
match self.get_extension::<crate::tina::data::http::DisableReqLogParamFlag>()? {
None => Ok(false),
Some(_) => Ok(true),
}
}
#[cfg(feature = "server-http")]
fn disable_oper_log(&self) -> AppResult<()> {
self.set_extension(crate::tina::data::http::DisableOperLogFlag)
}
#[cfg(feature = "server-http")]
fn is_disable_oper_log(&self) -> AppResult<bool> {
match self.get_extension::<crate::tina::data::http::DisableOperLogFlag>()? {
None => Ok(false),
Some(_) => Ok(true),
}
}
fn get_transaction_service(&self) -> AppResult<Arc<dyn ITransactionService<Client = Box<dyn Any + Send + 'static>>>> {
let ext = self
.get_extension::<Arc<dyn ITransactionService<Client = Box<dyn Any + Send + 'static>>>>()?
.ok_or_else(|| app_system_error!("No Transaction service found in session."))?;
let lock = ext.read().map_err(app_error_from_none_static!())?;
Ok(lock.clone())
}
fn set_transaction_service(&self, service: Arc<dyn ITransactionService<Client = Box<dyn Any + Send + 'static>>>) -> AppResult<()> {
self.set_extension(service)
}
async fn sleep(&self, duration: Duration) {
tokio::time::sleep(duration).await
}
}
impl ISessionAttribute for SessionInner {
fn get_extension<T: Send + Sync + 'static>(&self) -> AppResult<Option<Arc<RwLock<T>>>> {
let type_id = TypeId::of::<Arc<RwLock<T>>>();
match self.extension.get(&type_id) {
None => Ok(None),
Some(v) => match v.downcast_ref::<Arc<RwLock<T>>>() {
None => Ok(None),
Some(v) => Ok(Some(v.clone())),
},
}
}
fn set_extension<T: Send + Sync + 'static>(&self, value: T) -> AppResult<()> {
let type_id = TypeId::of::<Arc<RwLock<T>>>();
self.extension.insert(type_id, Arc::new(Arc::new(RwLock::new(value))));
Ok(())
}
fn get_attribute<T: Send + Sync + 'static>(&self, name: &str) -> AppResult<Option<Arc<RwLock<T>>>> {
match self.attributes.get(&name.to_string()) {
None => Ok(None),
Some(v) => match v.downcast_ref::<Arc<RwLock<T>>>() {
None => Ok(None),
Some(v) => Ok(Some(v.clone())),
},
}
}
fn set_attribute<T: Send + Sync + 'static>(&self, name: &str, value: T) -> AppResult<()> {
self.attributes.insert(name.to_string(), Arc::new(Arc::new(RwLock::new(value))));
Ok(())
}
}
impl FromApplication for Session {
type Target = Session;
fn from_application(application: Application, _init_components: bool) -> AppResult<Self::Target> {
let request_time = Arc::new(RequestTime::new(application.clone()));
let session = Session::new(SessionInner {
application,
enable_token: false,
token: None,
locale: Arc::new(ResourceBundle::get_default_locale()),
request_time,
#[cfg(feature = "server-http")]
route_config: Some(Arc::new(crate::tina::server::http::route::RouteBaseConfig::default())),
transaction_flag: AtomicBool::new(false),
extension: Default::default(),
attributes: Arc::new(Default::default()),
});
Ok(session)
}
}