From 75227dfd44bf2a8604e39681d5b6027e1e609cc0 Mon Sep 17 00:00:00 2001 From: gudaoxuri Date: Fri, 11 Mar 2022 22:07:46 +0800 Subject: [PATCH] Update documentation. #6 --- README.md | 5 +- logo.png | Bin 0 -> 28696 bytes src/basic/config.rs | 219 +++++++++++++++++++++++++++++++++-- src/basic/crypto.rs | 72 ++++++++++++ src/basic/dto.rs | 18 +++ src/basic/error.rs | 1 + src/basic/field.rs | 87 ++++++++++++++ src/basic/json.rs | 72 +++++++++++- src/basic/result.rs | 7 ++ src/basic/uri.rs | 32 +++++ src/lib.rs | 158 ++++++++++++++++++++++--- src/web/context_extractor.rs | 2 +- tests/test_basic_json.rs | 10 +- tests/test_web_server.rs | 2 +- 14 files changed, 651 insertions(+), 34 deletions(-) create mode 100644 logo.png 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 0000000000000000000000000000000000000000..a8ce6930df5d6792382e5c59e81779f7bdf33863 GIT binary patch literal 28696 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

?O8LQ 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

ys z&m?==&!JkF39^vXq0pAD+^w@o?L7)dir1DJU%QPBhwaoD3`VX#0jU*AXYC^F-}E2s zU(Ug`F*B3?2W(Jm~-C(vM2wZ zpoA~?+o~%{4AznzkgmFAa?b~M;=o2u#u3w1kpM$WP9Tn+4FU$8t%zodfjon1Tod*O z6O7)u9gz?tI{vVO&A{)oSIF&nS8Q8x7Yf%h&(LB9 z(hx1bb@y~A_A4R0dJ(L6NNXCON73=K;@tAaEQph@Z$$oz|Dbv|ieisrmFn7Qt4*Z*$Mz(pd@+m4_!5l2u4PXMm{9EFdh+pvegTZ7NV*)}b tb|3+O_ys_ynZ*o*1aH_m54_E0{r@TgO+|3B^A7+3002ovPDHLkV1oN*#p(b6 literal 0 HcmV?d00001 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