tina-core 0.0.2

Tina platform
Documentation
//! Session会话容器
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};

/// Session容器接口
#[async_trait]
pub trait ISessionBase {
    /// 获取语言Locale
    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;
    /// 获取token
    fn get_token(&self) -> Option<Cow<str>>;
    /// 获取请求中的用户
    fn get_user(&self) -> Option<Arc<LoginUser>>;
    /// 设置请求中的用户
    fn set_user(&self, user: Arc<LoginUser>) -> AppResult<()>;
    /// 获取请求中的用户ID
    fn get_user_id(&self) -> Option<Id>;
    /// 该请求是否启用token
    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);
}

/// Session容器接口
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()
    }
}

/// Session属性
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<()>;
}

/// Session的内部实现
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)
    }
}