diff --git a/README.md b/README.md index b5d269e8..8ae52d25 100644 --- a/README.md +++ b/README.md @@ -98,4 +98,7 @@ async fn main() -> TardisResult<()> { |-- mq Message Queue Usage Example |-- todo A complete project usage example |-- perf-test Performance test case -``` \ No newline at end of file +``` + +---- +Thanks to `Jetbrains` for the [Open Source License](https://www.jetbrains.com/community/opensource/) \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 00000000..a8ce6930 Binary files /dev/null and b/logo.png differ diff --git a/src/basic/config.rs b/src/basic/config.rs index 4e089582..4d466307 100644 --- a/src/basic/config.rs +++ b/src/basic/config.rs @@ -1,3 +1,8 @@ +/// Configuration handle / 配置处理 +/// +/// Organizing Configuration Management with Tardis Best Practices +/// +/// 使用 Tardis 最佳实践组织配置管理 use std::env; use std::fmt::Debug; use std::path::Path; @@ -11,34 +16,80 @@ use crate::log::{debug, info}; use crate::serde::{Deserialize, Serialize}; use crate::TardisFuns; +/// Configuration of Tarids / Tarids的配置 #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct TardisConfig { + /// Project custom configuration / 项目自定义的配置 pub ws: T, + /// Tardis framework configuration / Tardis框架的各功能配置 pub fw: FrameworkConfig, } +/// Configuration of each function of the Tardis framework / Tardis框架的各功能配置 #[derive(Default, Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct FrameworkConfig { + /// Application configuration / 应用配置 pub app: AppConfig, + /// Database configuration / 数据库配置 pub db: DBConfig, + /// Web service configuration / Web服务配置 pub web_server: WebServerConfig, + /// Web client configuration / Web客户端配置 pub web_client: WebClientConfig, + /// Distributed cache configuration / 分布式缓存配置 pub cache: CacheConfig, + /// Message queue configuration / 消息队列配置 pub mq: MQConfig, + /// Advanced configuration / 高级配置 pub adv: AdvConfig, } +/// Application configuration / 应用配置 +/// +/// By application, it means the current service +/// +/// 所谓应用指的就是当前的服务 +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::AppConfig; +/// AppConfig{ +/// id: "todo".to_string(), +/// name: "Todo App".to_string(), +/// version: "1.0.0".to_string(), +/// ..Default::default() +/// }; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct AppConfig { + /// Application identifier / 应用标识 + /// + /// Used to distinguish different services (applications) in a microservice environment. + /// + /// 在微服务环境下用于区别不同的服务(应用). pub id: String, + /// Application name / 应用名称 pub name: String, + /// Application description / 应用描述 pub desc: String, + /// Application version / 应用版本 pub version: String, + /// Application address / 应用地址 + /// + /// Can be either the access address or the documentation address. + /// + /// 可以是访问地址,也可以是文档地址. pub url: String, + /// Application contact email / 应用联系邮箱 pub email: String, + /// Application instance identification / 应用实例标识 + /// + /// An application can have multiple instances, each with its own identity, using the UUID by default. + /// + /// 一个应用可以有多个实例,每个实例都有自己的标识,默认使用UUID. pub inst: String, } @@ -56,14 +107,34 @@ impl Default for AppConfig { } } +/// Database configuration / 数据库配置 +/// +/// Database operations need to be enabled ```#[cfg(feature = "reldb")]``` . +/// +/// 数据库的操作需要启用 ```#[cfg(feature = "reldb")]``` . +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::DBConfig; +/// let config = DBConfig{ +/// url: "mysql://root:123456@localhost:3306/test".to_string(), +/// ..Default::default() +/// }; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct DBConfig { + /// Whether to enable the database operation function / 是否启用数据库操作功能 pub enabled: bool, + /// Database access Url, Url with permission information / 数据库访问Url,Url带权限信息 pub url: String, + /// Maximum number of connections, default 20 / 最大连接数,默认 20 pub max_connections: u32, + /// Minimum number of connections, default 5 / 最小连接数,默认 5 pub min_connections: u32, + /// Connection timeout / 连接超时时间 pub connect_timeout_sec: Option, + /// Idle connection timeout / 空闲连接超时时间 pub idle_timeout_sec: Option, } @@ -80,46 +151,136 @@ impl Default for DBConfig { } } +/// Web service configuration / Web服务配置 +/// +/// Web service operations need to be enabled ```#[cfg(feature = "web-server")]``` . +/// +/// Web服务操作需要启用 ```#[cfg(feature = "web-server")]``` . +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::{WebServerConfig, WebServerModuleConfig}; +/// let config = WebServerConfig { +/// modules: vec![ +/// WebServerModuleConfig { +/// code: "todo".to_string(), +/// title: "todo app".to_string(), +/// doc_urls: [("test env".to_string(), web_url.to_string()), ("prod env".to_string(), "http://127.0.0.1".to_string())].iter().cloned().collect(), +/// ..Default::default() +/// }, +/// WebServerModuleConfig { +/// code: "other".to_string(), +/// title: "other app".to_string(), +/// ..Default::default() +/// }, +/// ], +/// tls_key: Some(TLS_KEY.to_string()), +/// tls_cert: Some(TLS_CERT.to_string()), +/// ..Default::default() +///}; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct WebServerConfig { + /// Whether to enable the web service operation function / 是否启用Web服务操作功能 pub enabled: bool, + /// Web service Host, default is `0.0.0.0` / Web服务Host,默认为 `0.0.0.0` pub host: String, + /// Web service port, default is `8080` / Web服务端口,默认为 `8080` pub port: u16, + /// Allowed cross-domain sources, default is `*` / 允许的跨域来源,默认为 `*` pub allowed_origin: String, - pub context_flag: String, - pub lang_flag: String, + /// TLS Key, if this configuration is included then the protocol is HTTPS / TLS Key,如果包含此配置则协议为 + /// HTTPS pub tls_key: Option, + /// TLS certificate / TLS 证书 pub tls_cert: Option, + /// Web module configuration / Web模块配置 pub modules: Vec, + /// Whether to hide detailed error messages in the return message / 返回信息中是否隐藏详细错误信息 pub security_hide_err_msg: bool, + /// Tardis context configuration / Tardis上下文配置 pub context_conf: WebServerContextConfig, } +/// Web module configuration / Web模块配置 +/// +/// An application can contain multiple web modules, each of which can have its own independent +/// request root path and API documentation. +/// +/// 一个应用可以包含多个Web模块,每个模块可以有自己独立的请求根路径及API文档. +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::WebServerModuleConfig; +/// let config = WebServerModuleConfig { +/// code: "todo".to_string(), +/// title: "todo app".to_string(), +/// doc_urls: [ +/// ("test env".to_string(), "http://127.0.0.1:8081".to_string()), +/// ("prod env".to_string(), "http://127.0.0.1:8082".to_string()) +/// ].iter().cloned().collect(), +/// ..Default::default() +/// }; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct WebServerModuleConfig { + /// Module code / 模块编码 pub code: String, + /// Module title for ``OpenAPI`` / 模块标题,用于 ``OpenAPI`` pub title: String, + /// Module version for ``OpenAPI`` / 模块版本,用于 ``OpenAPI`` pub version: String, + /// Module API request path for ``OpenAPI`` / 模块API请求路径,用于 ``OpenAPI`` + /// + /// Formatted as ``[(environment identifier, request path)]`` / 格式为 ``[(环境标识,请求路径)]`` pub doc_urls: Vec<(String, String)>, - // TODO - pub authors: Vec<(String, String)>, + /// Module ``OpenAPI`` UI path / 模块 ``OpenAPI`` UI路径 pub ui_path: Option, + /// Module ``OpenAPI`` information path / 模块 ``OpenAPI`` 信息路径 pub spec_path: Option, } +/// Tardis context configuration / Tardis上下文配置 +/// +/// `Tardis Context` [TardisContext](crate::basic::dto::TardisContext) is used to bring in some +/// authentication information when a web request is received. +/// +/// `Tardis上下文` [TardisContext](crate::basic::dto::TardisContext) 用于Web请求时带入一些认证信息. +/// +/// This configuration specifies the source of the [TardisContext](crate::basic::dto::TardisContext). +/// +/// 该配置用于指明 [TardisContext](crate::basic::dto::TardisContext) 的生成来源. +/// +/// First it will try to get [context_header_name](Self::context_header_name) from the request header, +/// and if it is not specified or has no value it will try to get it from the cache. +/// +/// 首先会尝试从请求头信息中获取 [context_header_name](Self::context_header_name) ,如果没指定或是没有值时会尝试从缓存中获取. #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct WebServerContextConfig { + /// Tardis context identifier, used to specify the request header name, default is `Tardis-Context` + /// + /// Tardis上下文标识,用于指定请求头名,默认为 `Tardis-Context` pub context_header_name: String, - pub token_redis_key: String, + /// Tardis context identifier, used to specify the `key` of the cache, default is `tardis::ident::token::` + /// + /// Tardis上下文标识,用于指定缓存的 `key`,默认为 `tardis::ident::token::` + pub token_cache_key: String, } +/// Web client configuration / Web客户端配置 +/// +/// Web client operation needs to be enabled ```#[cfg(feature = "web-client")]``` . +/// +/// Web客户端操作需要启用 ```#[cfg(feature = "web-client")]``` . #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct WebClientConfig { + /// Connection timeout / 连接超时时间 pub connect_timeout_sec: u64, + /// Request timeout / 请求超时时间 pub request_timeout_sec: u64, } @@ -130,8 +291,6 @@ impl Default for WebServerConfig { host: "0.0.0.0".to_string(), port: 8080, allowed_origin: "*".to_string(), - context_flag: "Tardis-Context".to_string(), - lang_flag: "Accept-Language".to_string(), tls_key: None, tls_cert: None, modules: [WebServerModuleConfig::default()].to_vec(), @@ -148,7 +307,6 @@ impl Default for WebServerModuleConfig { title: "Tardis-based application".to_string(), version: "1.0.0".to_string(), doc_urls: [("test env".to_string(), "http://localhost:8080/".to_string())].to_vec(), - authors: [("gudaoxuri".to_string(), "i@sunisle.org".to_string())].to_vec(), ui_path: Some("ui".to_string()), spec_path: Some("spec".to_string()), } @@ -159,7 +317,7 @@ impl Default for WebServerContextConfig { fn default() -> Self { WebServerContextConfig { context_header_name: "Tardis-Context".to_string(), - token_redis_key: "tardis::ident::token::".to_string(), + token_cache_key: "tardis::ident::token::".to_string(), } } } @@ -173,10 +331,26 @@ impl Default for WebClientConfig { } } +/// Distributed cache configuration / 分布式缓存配置 +/// +/// Distributed cache operations need to be enabled ```#[cfg(feature = "cache")]``` . +/// +/// 分布式缓存操作需要启用 ```#[cfg(feature = "cache")]``` . +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::CacheConfig; +/// let config = CacheConfig { +/// url: "redis://123456@127.0.0.1:6379".to_string(), +/// ..Default::default() +///}; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct CacheConfig { + /// Whether to enable the distributed cache operation function / 是否启用分布式缓存操作功能 pub enabled: bool, + /// Cache access Url, Url with permission information / 缓存访问Url,Url带权限信息 pub url: String, } @@ -189,10 +363,26 @@ impl Default for CacheConfig { } } +/// Message queue configuration / 消息队列配置 +/// +/// Message queue operation needs to be enabled ```#[cfg(feature = "mq")]``` . +/// +/// 消息队列操作需要启用 ```#[cfg(feature = "mq")]``` . +/// +/// # Examples +/// ```rust +/// use tardis::basic::config::MQConfig; +/// let config = MQConfig { +/// url: "amqp://guest:guest@127.0.0.1:5672/%2f".to_string(), +/// ..Default::default() +///}; +/// ``` #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct MQConfig { + /// Whether to enable the message queue operation function / 是否启用消息队列操作功能 pub enabled: bool, + /// Message queue access Url, Url with permission information / 消息队列访问Url,Url带权限信息 pub url: String, } @@ -205,12 +395,23 @@ impl Default for MQConfig { } } +/// Advanced configuration / 高级配置 #[derive(Default, Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct AdvConfig { + /// Whether to capture the error stack / 是否捕捉错误堆栈 + /// + /// Enable it to locate errors easily, but it will affect performance. + /// + /// 启用后可方便定位错误,但会影响性能. pub backtrace: bool, } +/// Empty configuration / 空配置 +/// +/// For cases where project-level configuration is not needed. +/// +/// 用于不需要项目级配置的情况. #[derive(Default, Debug, Serialize, Deserialize)] #[serde(default)] pub struct NoneConfig {} diff --git a/src/basic/crypto.rs b/src/basic/crypto.rs index b9e5f1e1..c9966973 100644 --- a/src/basic/crypto.rs +++ b/src/basic/crypto.rs @@ -40,6 +40,14 @@ pub struct TardisCryptoSm2PublicKey { pub struct TardisCryptoDigest; pub struct TardisCryptoKey; +/// Base64 handle / Base64处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// let b64_str = TardisFuns::crypto.base64.encode("测试"); +/// let str = TardisFuns::crypto.base64.decode(&b64_str).unwrap(); +/// ``` impl TardisCryptoBase64 { pub fn decode(&self, data: &str) -> TardisResult { match base64::decode(data) { @@ -53,6 +61,17 @@ impl TardisCryptoBase64 { } } +/// AES handle / AES处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// let key = TardisFuns::crypto.key.rand_16_hex().unwrap(); +/// let iv = TardisFuns::crypto.key.rand_16_hex().unwrap(); +/// let text = "为什么选择 Rust?"; +/// let encrypted_data = TardisFuns::crypto.aes.encrypt_cbc(text, &key, &iv).unwrap(); +/// let data = TardisFuns::crypto.aes.decrypt_cbc(&encrypted_data, &key, &iv).unwrap(); +/// ``` impl TardisCryptoAes { pub fn encrypt_cbc(&self, data: &str, hex_key: &str, hex_iv: &str) -> TardisResult { let key_size = match hex_key.len() { @@ -108,6 +127,17 @@ impl TardisCryptoAes { } } +/// SM4 handle / SM4处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// let key = TardisFuns::crypto.key.rand_16_hex().unwrap(); +/// let iv = TardisFuns::crypto.key.rand_16_hex().unwrap(); +/// let text = "为什么选择 Rust?"; +/// let encrypted_data = TardisFuns::crypto.sm4.encrypt_cbc(text, &key, &iv).unwrap(); +/// let data = TardisFuns::crypto.sm4.decrypt_cbc(&encrypted_data, &key, &iv).unwrap(); +/// ``` impl TardisCryptoSm4 { pub fn encrypt_cbc(&self, data: &str, hex_key: &str, hex_iv: &str) -> TardisResult { let encrypted_data = gmsm::sm4::sm4_cbc_encrypt_byte(data.as_bytes(), hex::decode(hex_key)?.as_slice(), hex::decode(hex_iv)?.as_slice()); @@ -121,6 +151,20 @@ impl TardisCryptoSm4 { } } +/// RSA handle / RSA处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// let private_key = TardisFuns::crypto.rsa.new_private_key(2048).unwrap(); +/// let public_key = TardisFuns::crypto.rsa.new_public_key(&private_key).unwrap(); +/// +/// let signed_data = private_key.sign("测试").unwrap(); +/// public_key.verify("测试", &signed_data).unwrap(); +/// +/// let encrypted_data = public_key.encrypt("测试").unwrap(); +/// private_key.decrypt(&encrypted_data).unwrap(); +/// ``` impl TardisCryptoRsa { pub fn new_private_key(&self, bits: usize) -> TardisResult { TardisCryptoRsaPrivateKey::new(bits) @@ -234,6 +278,18 @@ impl TardisCryptoRsaPublicKey { } } +/// SM2 handle / SM2处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// let private_key = TardisFuns::crypto.sm2.new_private_key().unwrap(); +/// let private_key_str = private_key.to_private_key().unwrap(); +/// let public_key = TardisFuns::crypto.sm2.new_public_key_from_private_key(&private_key_str).unwrap(); +/// +/// let encrypted_data = public_key.encrypt("测试").unwrap(); +/// private_key.decrypt(&encrypted_data).unwrap(); +/// ``` impl TardisCryptoSm2 { pub fn new_private_key(&self) -> TardisResult { TardisCryptoSm2PrivateKey::new() @@ -301,6 +357,22 @@ impl TardisCryptoSm2PublicKey { } } +/// Digest handle / 摘要处理 +/// +/// # Examples +/// ```rust +/// use tardis::TardisFuns; +/// TardisFuns::crypto.digest.md5("测试").unwrap(); +/// TardisFuns::crypto.digest.sha1("测试").unwrap(); +/// TardisFuns::crypto.digest.sha256("测试").unwrap(); +/// TardisFuns::crypto.digest.sha512("测试").unwrap(); +/// +/// TardisFuns::crypto.digest.hmac_sha1("测试", "pwd").unwrap(); +/// TardisFuns::crypto.digest.hmac_sha256("测试", "pwd").unwrap(); +/// TardisFuns::crypto.digest.hmac_sha512("测试", "pwd").unwrap(); +/// +/// TardisFuns::crypto.digest.sm3("测试").unwrap(); +/// ``` impl TardisCryptoDigest { pub fn sha1(&self, data: &str) -> TardisResult { self.digest(data, crypto::sha1::Sha1::new()) diff --git a/src/basic/dto.rs b/src/basic/dto.rs index bdadca10..b79f9a8b 100644 --- a/src/basic/dto.rs +++ b/src/basic/dto.rs @@ -1,15 +1,33 @@ +//! Common DTOs / 常用的DTO use crate::serde::{Deserialize, Serialize}; +/// ardis context / Tardis上下文 +/// +/// Used to bring in some authentication information when a web request is received. +/// +/// 用于Web请求时带入一些认证信息. +/// +/// This information needs to be supported by the IAM service. +/// +/// 该信息需要与 IAM 服务对应. #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(default)] pub struct TardisContext { + /// The requested application Id / 请求的应用Id pub app_id: String, + /// The requested tenant Id / 请求的租户Id pub tenant_id: String, + /// The requested Ak / 请求的Ak pub ak: String, + /// The requested account id / 请求的账号Id pub account_id: String, + /// The requested Token / 请求的Token pub token: String, + /// The requested Token type / 请求的Token类型 pub token_kind: String, + /// List of requested role ids / 请求的角色Id列表 pub roles: Vec, + /// List of requested group ids / 请求的群组Id列表 pub groups: Vec, } diff --git a/src/basic/error.rs b/src/basic/error.rs index 0d2ba894..343a3631 100644 --- a/src/basic/error.rs +++ b/src/basic/error.rs @@ -10,6 +10,7 @@ use crate::basic::field::GENERAL_SPLIT; pub static ERROR_DEFAULT_CODE: &str = "-1"; +/// Tardis unified error wrapper / Tardis统一错误封装 #[derive(Display, Debug)] pub enum TardisError { #[display(fmt = "{}##{}", _0, _1)] diff --git a/src/basic/field.rs b/src/basic/field.rs index a45826e4..f6762ada 100644 --- a/src/basic/field.rs +++ b/src/basic/field.rs @@ -1,3 +1,8 @@ +//! Field handle / 字段处理 +//! +//! Provides some common regular, Id generation and other functions. +//! +//! 提供了一些常用的正则判断、Id生成等功能. use std::fmt::{Display, Formatter}; use regex::Regex; @@ -20,38 +25,99 @@ pub static GENERAL_SPLIT: &str = "##"; pub struct TardisField; impl TardisField { + /// Determine if it is a cell phone number (only supports mainland China) / 判断是否是手机号(仅支持中国大陆) pub fn is_phone(&self, phone: &str) -> bool { R_PHONE.is_match(phone) } + /// Determine if it is a email / 判断是否是邮箱 pub fn is_mail(&self, mail: &str) -> bool { R_MAIL.is_match(mail) } + /// Determine if it contains only numbers, lowercase letters and underscores / + /// 判断是否只包含数字、小写字母及下划线 pub fn is_code_cs(&self, str: &str) -> bool { R_CODE_CS.is_match(str) } + /// Determine if only numbers, upper and lower case letters and underscores are included / + /// 判断是否只包含数字、大小写字母及下划线 pub fn is_code_ncs(&self, str: &str) -> bool { R_CODE_NCS.is_match(str) } + /// Generate UUID / 生成UUID pub fn uuid(&self) -> Uuid { uuid::Uuid::new_v4() } + /// Generate UUID as a string / 生成字符串形式的UUID pub fn uuid_str(&self) -> String { uuid::Uuid::new_v4().to_simple().to_string() } + /// Generate self-incrementing ID based on base62 code / 根据base62编码生成自增ID + /// + /// `BASE62` refers to Base64 encoding that does not contain `+` + /// `-` . + /// + /// `BASE62` 指的是不包含 `+` `-` 的Base64编码. + /// + /// # Arguments + /// + /// * `str` - current string / 当前字符串 + /// + /// # Examples + /// + /// ```rust + /// use tardis::TardisFuns; + /// assert_eq!(TardisFuns::field.incr_by_base62("abcd1").unwrap(), "abcd2"); + /// assert_eq!(TardisFuns::field.incr_by_base62("abcd12").unwrap(), "abcd13"); + /// assert_eq!(TardisFuns::field.incr_by_base62("abcd9").unwrap(), "abceA"); + /// assert_eq!(TardisFuns::field.incr_by_base62("azzz9").unwrap(), "azz0A"); + /// assert_eq!(TardisFuns::field.incr_by_base62("a9999").unwrap(), "bAAAA"); + /// assert!(TardisFuns::field.incr_by_base62("999").is_none()); + /// ``` + /// pub fn incr_by_base62(&self, str: &str) -> Option { self.incr_by(str, BASE62) } + /// Generate self-incrementing ID based on base36 code / 根据base36编码生成自增ID + /// + /// `BASE36` refers to Base64 encoding that does not contain `+` `-` + /// `A-Z` . + /// + /// `BASE36` 指的是不包含 `+` `-` `A-Z` 的Base64编码. + /// + /// # Arguments + /// + /// * `str` - current string / 当前字符串 + /// + /// # Examples + /// + /// ```rust + /// use tardis::TardisFuns; + /// assert_eq!(TardisFuns::field.incr_by_base36("abcd1").unwrap(), "abcd2"); + /// assert_eq!(TardisFuns::field.incr_by_base36("abcd12").unwrap(), "abcd13"); + /// assert_eq!(TardisFuns::field.incr_by_base36("abcd9").unwrap(), "abcea"); + /// assert_eq!(TardisFuns::field.incr_by_base36("azzz9").unwrap(), "azz0a"); + /// assert_eq!(TardisFuns::field.incr_by_base36("a9999").unwrap(), "baaaa"); + /// assert!(TardisFuns::field.incr_by_base36("999").is_none()); + /// ``` + /// pub fn incr_by_base36(&self, str: &str) -> Option { self.incr_by(str, BASE36) } + /// Using custom codes to generate self-incrementing ID / 使用自定义编码生成自增ID + /// + /// # Arguments + /// + /// * `str` - current string / 当前字符串 + /// * `chars` - custom encoded string / 自定义的编码字符串 + /// pub fn incr_by(&self, str: &str, chars: &str) -> Option { let mut result = Vec::new(); let mut up = true; @@ -78,6 +144,27 @@ impl TardisField { } } +/// String types that support auto-trim / 支持自动trim的字符串类型 +/// +/// Valid by default when using [serde] serialization and deserialization. +/// +/// 默认情况下,在使用 [serde] 序列化与反序列化时有效. +/// +/// Valid when request body to Rust object when `web-server` feature is enabled. +/// +/// 当启用 `web-server` feature时,在请求体转Rust对象时有效. +/// +/// ```rust +/// use serde::{Serialize,Deserialize}; +/// use serde_json::Value::Object; +/// use tardis::basic::field::TrimString; +/// #[derive(Object, Serialize, Deserialize, Debug)] +/// struct TodoAddReq { +/// code: TrimString, +/// description: String, +/// done: bool, +/// } +/// ``` #[derive(Debug, Eq, PartialEq, Hash)] pub struct TrimString(String); diff --git a/src/basic/json.rs b/src/basic/json.rs index 9afd14fb..46cb9215 100644 --- a/src/basic/json.rs +++ b/src/basic/json.rs @@ -1,13 +1,24 @@ +//! Json handle / Json处理 +use crate::basic::error::TardisError; +use crate::basic::result::TardisResult; use crate::serde::de::DeserializeOwned; use crate::serde::{Deserialize, Serialize}; use crate::serde_json::Value; -use crate::basic::error::TardisError; -use crate::basic::result::TardisResult; - pub struct TardisJson; impl TardisJson { + /// Convert Json string to Rust object / 将Json字符串转换为Rust对象 + /// + /// # Arguments + /// + /// * `str` - Json string / Json字符串 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.str_to_obj::>(&json_str); + /// ``` pub fn str_to_obj<'a, T: Deserialize<'a>>(&self, str: &'a str) -> TardisResult { let result = serde_json::from_str::<'a, T>(str); match result { @@ -16,6 +27,17 @@ impl TardisJson { } } + /// Convert Json string to Json object / 将Json字符串转换为Json对象 + /// + /// # Arguments + /// + /// * `str` - Json string / Json字符串 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.str_to_json(&json_str); + /// ``` pub fn str_to_json<'a>(&self, str: &'a str) -> TardisResult { let result = serde_json::from_str::<'a, Value>(str); match result { @@ -24,6 +46,17 @@ impl TardisJson { } } + /// Convert Json object to Rust object / 将Json对象转换为Rust对象 + /// + /// # Arguments + /// + /// * `value` - Json object / Json对象 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.json_to_obj::>(json_value); + /// ``` pub fn json_to_obj(&self, value: Value) -> TardisResult { let result = serde_json::from_value::(value); match result { @@ -32,6 +65,17 @@ impl TardisJson { } } + /// Convert Rust string to Json string / 将Rust对象转换为Json字符串 + /// + /// # Arguments + /// + /// * `obj` - Rust object / Rust对象 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.obj_to_string(&rust_obj); + /// ``` pub fn obj_to_string(&self, obj: &T) -> TardisResult { let result = serde_json::to_string(obj); match result { @@ -40,6 +84,17 @@ impl TardisJson { } } + /// Convert Rust object to Json object / 将Rust对象转换为Json对象 + /// + /// # Arguments + /// + /// * `obj` - Rust object / Rust对象 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.obj_to_json(&rust_obj); + /// ``` pub fn obj_to_json(&self, obj: &T) -> TardisResult { let result = serde_json::to_value(obj); match result { @@ -48,6 +103,17 @@ impl TardisJson { } } + /// Convert Json object to Json string / 将Json对象转换成Json字符串 + /// + /// # Arguments + /// + /// * `value` - Json object / Json对象 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::json.json_to_string(json_value); + /// ``` pub fn json_to_string(&self, value: Value) -> TardisResult { let result = serde_json::to_string(&value); match result { diff --git a/src/basic/result.rs b/src/basic/result.rs index 256e31d7..b5473a12 100644 --- a/src/basic/result.rs +++ b/src/basic/result.rs @@ -6,8 +6,12 @@ use derive_more::Display; use crate::basic::error::TardisError; use crate::basic::field::GENERAL_SPLIT; +/// Tardis return object wrapper / Tardis返回对象封装 pub type TardisResult = Result; +/// Common return type enumerations / 常用的返回类型枚举 +/// +/// Used for uniform error wrapper / 用于统一错误封装 #[derive(Display, Debug)] pub enum StatusCodeKind { #[display(fmt = "200")] @@ -46,6 +50,9 @@ impl StatusCodeKind { } } +/// Commonly used enumerations of operation types / 常用的操作类型枚举 +/// +/// Used for uniform error wrapper / 用于统一错误封装 #[derive(Display, Debug)] pub enum ActionKind { #[display(fmt = "01")] diff --git a/src/basic/uri.rs b/src/basic/uri.rs index b615ef75..8614d85c 100644 --- a/src/basic/uri.rs +++ b/src/basic/uri.rs @@ -1,8 +1,19 @@ +//! Uri handle / Uri处理 use crate::basic::result::TardisResult; pub struct TardisUri; impl TardisUri { + /// Format Uri / 格式化Uri + /// + /// Return the standard, Query parameter sorted Uri. + /// + /// 返回标准的、Query参数排序后的Uri. + /// + /// # Arguments + /// + /// * `host` - Host + /// * `path_and_query` - Path and Query pub fn format_with_item(&self, host: &str, path_and_query: &str) -> TardisResult { if path_and_query.is_empty() { self.format(host) @@ -15,6 +26,21 @@ impl TardisUri { } } + /// Format Uri / 格式化Uri + /// + /// Return the standard, Query parameter sorted Uri. + /// + /// 返回标准的、Query参数排序后的Uri. + /// + /// # Arguments + /// + /// * `uri_str` - Uri string + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// assert_eq!(TardisFuns::uri.format("api://a1.t1/e1?q2=2&q1=1&q3=3").unwrap(), "api://a1.t1/e1?q1=1&q2=2&q3=3"); + /// ``` pub fn format(&self, uri_str: &str) -> TardisResult { let uri = url::Url::parse(uri_str)?; let host = match uri.host() { @@ -45,6 +71,12 @@ impl TardisUri { Ok(formatted_uri) } + /// Get the Path and Query parts of the Uri / 获取Uri中的Path和Query部分 + /// + /// # Arguments + /// + /// * `uri_str` - Uri string + /// pub fn get_path_and_query(&self, uri_str: &str) -> TardisResult { let uri = url::Url::parse(uri_str)?; let path = if uri.path().is_empty() { diff --git a/src/lib.rs b/src/lib.rs index 8bfd20a8..677e7d18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! **Elegant, Clean Rust development framework🛸** //! -//! > TARDIS([tɑːrdɪs] "Time And Relative Dimension In Space") From "Doctor Who". +//! > TARDIS(\[tɑːrdɪs\] "Time And Relative Dimension In Space") From "Doctor Who". //! //! ## 💖 Core functions //! @@ -11,8 +11,7 @@ //! * Mainstream encryption algorithms and SM2/3/4 algorithms //! * Containerized unit testing of mainstream middleware //! * Multi-environment configuration -//! * Commonly used operations (E.g. uniform error handling, encryption and decryption, regular -//! checksums) +//! * Commonly used operations (E.g. uniform error handling, encryption and decryption, regular checksums) //! //! ## ⚙️Feature description //! @@ -46,7 +45,8 @@ //! ``` //! //! Processor Configuration -//! ```rust +//!```rust +//! use tardis::web::poem_openapi::OpenApi; //! pub struct Api; //! //! #[OpenApi] @@ -62,11 +62,14 @@ //! ``` //! //! Startup class configuration -//! ```rust +//!```rust +//! use tardis::basic::result::TardisResult; //! #[tokio::main] //! async fn main() -> TardisResult<()> { -//! // Initial configuration -//! TardisFuns::init::("config").await?; +//! use tardis::basic::config::NoneConfig; +//! // Initial configuration +//! use tardis::basic::result::TardisResult; +//! use tardis::TardisFuns;TardisFuns::init::("config").await?; //! // Register the processor and start the web service //! TardisFuns::web_server().add_module("", Api).start().await //! } @@ -85,6 +88,7 @@ //!> |-- perf-test Performance test case //! +#![doc(html_logo_url = "https://raw.githubusercontent.com/ideal-wrold/tardis/main/logo.png")] #![cfg_attr(docsrs, feature(doc_cfg))] extern crate core; @@ -120,13 +124,14 @@ use crate::web::web_client::TardisWebClient; #[cfg(feature = "web-server")] use crate::web::web_server::TardisWebServer; -/// The operational portal for Tardis core functions / Tardis核心功能的操作入口 +/// The operational portal for Tardis core features / Tardis核心功能的操作入口 /// /// # Initialization / 初始化 /// -/// ## Define project-level configuration objects / 定义项目级配置对象 +/// ## Define project-level configuration object / 定义项目级配置对象 /// /// ```rust +/// use serde::{Serialize,Deserialize}; /// #[derive(Debug, Serialize, Deserialize)] /// #[serde(default)] /// struct ExampleConfig { @@ -196,7 +201,7 @@ use crate::web::web_server::TardisWebServer; /// /// More examples of initialization can be found in: `test_basic_config.rs` . /// -/// 更多初始化的示例可参考: [`test_basic_config`] `test_basic_config.rs` . +/// 更多初始化的示例可参考: `test_basic_config.rs` . /// /// # 使用 /// @@ -214,8 +219,6 @@ use crate::web::web_server::TardisWebServer; /// TardisFuns::cache(); /// TardisFuns::mq(); /// ``` -/// -/// pub struct TardisFuns { workspace_config: Option>, framework_config: Option, @@ -258,6 +261,8 @@ impl TardisFuns { /// # Examples /// /// ```rust + /// use std::env; + /// use tardis::TardisFuns; /// env::set_var("PROFILE", "test"); /// TardisFuns::init::("proj/config").await; /// ``` @@ -290,7 +295,9 @@ impl TardisFuns { /// # Examples /// /// ```rust - /// TardisFuns::init_conf(TardisConfig { + /// use tardis::basic::config::{CacheConfig, DBConfig, FrameworkConfig, MQConfig, NoneConfig, TardisConfig, WebServerConfig}; + /// use tardis::TardisFuns; + /// let result = TardisFuns::init_conf(TardisConfig { /// ws: NoneConfig {}, /// fw: FrameworkConfig { /// app: Default::default(), @@ -311,7 +318,7 @@ impl TardisFuns { /// adv: Default::default(), /// }, /// }) - /// .await + /// .await; /// ``` pub async fn init_conf(conf: TardisConfig) -> TardisResult<()> { TardisLogger::init()?; @@ -365,6 +372,7 @@ impl TardisFuns { TardisResult::Ok(()) } + /// Get the project-level configuration object / 获取项目级配置对象 pub fn ws_config() -> &'static T { unsafe { match &TARDIS_INST.workspace_config { @@ -377,6 +385,7 @@ impl TardisFuns { } } + /// Get the Tardis configuration object / 获取Tardis配置对象 pub fn fw_config() -> &'static FrameworkConfig { unsafe { match &TARDIS_INST.framework_config { @@ -386,15 +395,67 @@ impl TardisFuns { } } + /// Using the field feature / 使用字段功能 + /// + /// # Examples + /// ```rust + /// + /// use tardis::TardisFuns; + /// TardisFuns::field.is_phone("18657120202"); + /// + /// TardisFuns::field.incr_by_base62("abcd1"); + /// ``` #[allow(non_upper_case_globals)] pub const field: TardisField = TardisField {}; + /// Using the json feature / 使用Json功能 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// let test_config = TestConfig { + /// project_name: "测试".to_string(), + /// level_num: 0, + /// db_proj: DatabaseConfig { url: "http://xxx".to_string() }, + /// }; + /// + /// // Rust object to Json string / Rust对象转成Json字符串 + /// let json_str = TardisFuns::json.obj_to_string(&test_config).unwrap(); + /// + /// // Json string to Rust Object / Json字符串转成Rust对象 + /// TardisFuns::json.str_to_obj::>(&json_str).unwrap(); + /// ``` #[allow(non_upper_case_globals)] pub const json: TardisJson = TardisJson {}; + /// Using the uri feature / 使用Url功能 + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// // Query sort + /// assert_eq!(TardisFuns::uri.format("api://a1.t1/e1?q2=2&q1=1&q3=3").unwrap(), "api://a1.t1/e1?q1=1&q2=2&q3=3"); + /// ``` #[allow(non_upper_case_globals)] pub const uri: TardisUri = TardisUri {}; + /// Use of encryption/decryption/digest features / 使用加解密/摘要功能 + /// + /// Supported algorithms: base64/md5/sha/mac/aes/rsa/sm2/sm3/sm4. + /// + /// 支持的算法: base64/md5/sha/hmac/aes/rsa/sm2/sm3/sm4. + /// + /// This feature needs to be enabled #[cfg(feature = "crypto")] . + /// + /// 本功能需要启用 #[cfg(feature = "crypto")] . + /// + /// # Examples + /// ```rust + /// use tardis::TardisFuns; + /// TardisFuns::crypto.base64.decode(&b64_str); + /// TardisFuns::crypto.digest.sha256("测试"); + /// TardisFuns::crypto.digest.sm3("测试"); + /// ``` #[allow(non_upper_case_globals)] #[cfg(feature = "crypto")] pub const crypto: crate::basic::crypto::TardisCrypto = crate::basic::crypto::TardisCrypto { @@ -407,6 +468,75 @@ impl TardisFuns { key: crate::basic::crypto::TardisCryptoKey {}, }; + /// Use the relational database feature / 使用关系型数据库功能 + /// + /// This feature needs to be enabled #[cfg(feature = "reldb")] . + /// + /// 本功能需要启用 #[cfg(feature = "reldb")] . + /// + /// # Steps to use / 使用步骤 + /// + /// 1. Initialize the database configuration / 初始化数据库配置 @see [init](Self::init) + /// 2. Add the database / 添加数据库 E.g. + /// ```rust + /// mod todos{ + /// use tardis::basic::dto::TardisContext; + /// use tardis::db::reldb_client::TardisActiveModel; + /// use tardis::db::sea_orm::*; + /// + /// #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] + /// #[sea_orm(table_name = "todos")] + /// pub struct Model { + /// #[sea_orm(primary_key)] + /// pub id: i32, + /// pub code: String, + /// pub description: String, + /// pub done: bool, + /// } + /// + /// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] + /// pub enum Relation {} + /// + /// impl TardisActiveModel for ActiveModel { + /// fn fill_cxt(&mut self, _: &TardisContext, _: bool) {} + /// } + /// + /// impl ActiveModelBehavior for ActiveModel {} + /// } + /// ``` + /// 3. Call this function to complete various data processing operations / 调用本函数完成各种数据处理操作 E.g. + /// ```rust + /// use std::process::id; + /// use tardis::basic::error::TardisError; + /// use tardis::TardisFuns; + /// use tardis::db::sea_orm::*; + /// use tardis::db::sea_query::Query; + /// // Initialize table structure + /// TardisFuns::reldb().conn().create_table_from_entity(todos::Entity).await?; + /// // Create record + /// let todo_id = TardisFuns::reldb() + /// .conn() + /// .insert_one( + /// todos::ActiveModel { + /// code: Set(todo_add_req.code.to_string()), + /// description: Set(todo_add_req.description.to_string()), + /// done: Set(todo_add_req.done), + /// ..Default::default() + /// }, + /// &cxt.0, + /// ).unwrap() + /// .last_insert_id; + /// // Query record + /// let todo = TardisFuns::reldb() + /// .conn() + /// .get_dto( + /// DbQuery::select() + /// .columns(vec![todos::Column::Id, todos::Column::Code, todos::Column::Description, todos::Column::Done]) + /// .from(todos::Entity) + /// .and_where(todos::Column::Id.eq(todo_id)), + /// ) + /// .await.unwrap(); + /// ``` #[cfg(feature = "reldb")] pub fn reldb() -> &'static TardisRelDBClient { unsafe { diff --git a/src/web/context_extractor.rs b/src/web/context_extractor.rs index 8cdea6b8..2ff8d2e7 100644 --- a/src/web/context_extractor.rs +++ b/src/web/context_extractor.rs @@ -38,7 +38,7 @@ async fn extract_context(req: &Request) -> TardisResult { #[cfg(feature = "cache")] { let token = context.split(TOKEN_FLAG).nth(1).ok_or_else(|| TardisError::BadRequest("[Tardis.WebServer] Context header is not valid".to_string()))?; - let context = TardisFuns::cache().get(format!("{}{}", TardisFuns::fw_config().web_server.context_conf.token_redis_key, token).as_str()).await?; + let context = TardisFuns::cache().get(format!("{}{}", TardisFuns::fw_config().web_server.context_conf.token_cache_key, token).as_str()).await?; let context = context.ok_or_else(|| TardisError::BadRequest("[Tardis.WebServer] Token is not in cache".to_string()))?; let context = TardisFuns::json.str_to_obj(&context).map_err(|_| TardisError::BadRequest("[Tardis.WebServer] Context cache is not valid json".to_string()))?; Ok(context) diff --git a/tests/test_basic_json.rs b/tests/test_basic_json.rs index b884a963..cfce5c14 100644 --- a/tests/test_basic_json.rs +++ b/tests/test_basic_json.rs @@ -10,25 +10,25 @@ async fn test_basic_json() -> TardisResult<()> { db_proj: DatabaseConfig { url: "http://xxx".to_string() }, }; - let json_str = TardisFuns::json.obj_to_string(&test_config).unwrap(); + let json_str = TardisFuns::json.obj_to_string(&test_config)?; assert_eq!(json_str, r#"{"project_name":"测试","level_num":0,"db_proj":{"url":"http://xxx"}}"#); - let json_obj = TardisFuns::json.str_to_obj::>(&json_str).unwrap(); + let json_obj = TardisFuns::json.str_to_obj::>(&json_str)?; assert_eq!(json_obj.project_name, "测试"); assert_eq!(json_obj.level_num, 0); assert_eq!(json_obj.db_proj.url, "http://xxx"); - let json_value = TardisFuns::json.str_to_json(&json_str).unwrap(); + let json_value = TardisFuns::json.str_to_json(&json_str)?; assert_eq!(json_value["project_name"], "测试"); assert_eq!(json_value["level_num"], 0); assert_eq!(json_value["db_proj"]["url"], "http://xxx"); - let json_value = TardisFuns::json.obj_to_json(&json_obj).unwrap(); + let json_value = TardisFuns::json.obj_to_json(&json_obj)?; assert_eq!(json_value["project_name"], "测试"); assert_eq!(json_value["level_num"], 0); assert_eq!(json_value["db_proj"]["url"], "http://xxx"); - let json_obj = TardisFuns::json.json_to_obj::>(json_value).unwrap(); + let json_obj = TardisFuns::json.json_to_obj::>(json_value)?; assert_eq!(json_obj.project_name, "测试"); assert_eq!(json_obj.level_num, 0); assert_eq!(json_obj.db_proj.url, "http://xxx"); diff --git a/tests/test_web_server.rs b/tests/test_web_server.rs index 1fae0ccb..a924ffca 100644 --- a/tests/test_web_server.rs +++ b/tests/test_web_server.rs @@ -475,7 +475,7 @@ async fn test_context(url: &str) -> TardisResult<()> { }; TardisFuns::cache() .set( - format!("{}token1", TardisFuns::fw_config().web_server.context_conf.token_redis_key).as_str(), + format!("{}token1", TardisFuns::fw_config().web_server.context_conf.token_cache_key).as_str(), TardisFuns::json.obj_to_string(&context).unwrap().as_str(), ) .await