This commit is contained in:
152
server-rs/crates/shared-kernel/src/lib.rs
Normal file
152
server-rs/crates/shared-kernel/src/lib.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use time::OffsetDateTime;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use uuid::Uuid;
|
||||
|
||||
/// 统一做必填字符串归一化,避免各模块散落重复的 `trim().to_string()`。
|
||||
pub fn normalize_required_string(value: impl AsRef<str>) -> Option<String> {
|
||||
let normalized = value.as_ref().trim();
|
||||
if normalized.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(normalized.to_string())
|
||||
}
|
||||
|
||||
/// 统一做可选字符串归一化,空白字符串一律视为 `None`。
|
||||
pub fn normalize_optional_string(value: Option<String>) -> Option<String> {
|
||||
value.and_then(normalize_required_string)
|
||||
}
|
||||
|
||||
/// 统一做字符串列表归一化,逐项裁剪并丢弃空白项。
|
||||
pub fn normalize_string_list(values: Vec<String>) -> Vec<String> {
|
||||
values
|
||||
.into_iter()
|
||||
.filter_map(|value| normalize_required_string(value))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 统一生成“前缀 + 十六进制微秒种子”的稳定 ID,适合业务对象主键。
|
||||
pub fn build_prefixed_seed_id(prefix: &str, seed_micros: i64) -> String {
|
||||
format!("{prefix}{seed_micros:x}")
|
||||
}
|
||||
|
||||
/// 统一生成“前缀 + UUID simple”随机 ID,适合会话态或一次性票据主键。
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn build_prefixed_uuid_id(prefix: &str) -> String {
|
||||
format!("{prefix}{}", Uuid::new_v4().simple())
|
||||
}
|
||||
|
||||
/// SpacetimeDB 的 wasm32 模块不应走浏览器/本地随机 UUID 生成。
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn build_prefixed_uuid_id(_prefix: &str) -> String {
|
||||
panic!(
|
||||
"shared-kernel::build_prefixed_uuid_id 不支持 wasm32,请改用显式 ID 或 SpacetimeDB 上下文生成能力"
|
||||
)
|
||||
}
|
||||
|
||||
/// 统一生成 UUID simple 字符串,供 token、随机种子等轻量场景复用。
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new_uuid_simple_string() -> String {
|
||||
Uuid::new_v4().simple().to_string()
|
||||
}
|
||||
|
||||
/// SpacetimeDB 的 wasm32 模块不应走浏览器/本地随机 UUID 生成。
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn new_uuid_simple_string() -> String {
|
||||
panic!(
|
||||
"shared-kernel::new_uuid_simple_string 不支持 wasm32,请改用显式 ID 或 SpacetimeDB 上下文生成能力"
|
||||
)
|
||||
}
|
||||
|
||||
/// 统一格式化微秒时间戳,当前阶段固定为 `seconds.microsZ` 文本口径。
|
||||
pub fn format_timestamp_micros(micros: i64) -> String {
|
||||
let seconds = micros.div_euclid(1_000_000);
|
||||
let subsec_micros = micros.rem_euclid(1_000_000);
|
||||
format!("{seconds}.{subsec_micros:06}Z")
|
||||
}
|
||||
|
||||
/// 统一把 `OffsetDateTime` 转成 Unix 微秒时间戳,避免各模块重复手写纳秒除法。
|
||||
pub fn offset_datetime_to_unix_micros(value: OffsetDateTime) -> i64 {
|
||||
(value.unix_timestamp_nanos() / 1_000) as i64
|
||||
}
|
||||
|
||||
/// 统一格式化 RFC3339 字符串,避免每个模块自己拼格式化错误文案。
|
||||
pub fn format_rfc3339(value: OffsetDateTime) -> Result<String, String> {
|
||||
value
|
||||
.format(&time::format_description::well_known::Rfc3339)
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
/// 统一解析 RFC3339 字符串,供模块自行补充更贴近业务的错误上下文。
|
||||
pub fn parse_rfc3339(value: &str) -> Result<OffsetDateTime, String> {
|
||||
OffsetDateTime::parse(value, &time::format_description::well_known::Rfc3339)
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn normalize_required_string_trims_and_filters_blank() {
|
||||
assert_eq!(
|
||||
normalize_required_string(" hero_001 "),
|
||||
Some("hero_001".to_string())
|
||||
);
|
||||
assert_eq!(normalize_required_string(" "), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_optional_string_filters_blank() {
|
||||
assert_eq!(
|
||||
normalize_optional_string(Some(" profile_001 ".to_string())),
|
||||
Some("profile_001".to_string())
|
||||
);
|
||||
assert_eq!(normalize_optional_string(Some(" ".to_string())), None);
|
||||
assert_eq!(normalize_optional_string(None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_string_list_trims_and_filters_blank() {
|
||||
assert_eq!(
|
||||
normalize_string_list(vec![
|
||||
" alpha ".to_string(),
|
||||
"".to_string(),
|
||||
" ".to_string(),
|
||||
"beta".to_string()
|
||||
]),
|
||||
vec!["alpha".to_string(), "beta".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_prefixed_seed_id_uses_hex_seed() {
|
||||
assert_eq!(build_prefixed_seed_id("assetobj_", 255), "assetobj_ff");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_timestamp_micros_is_stable() {
|
||||
assert_eq!(
|
||||
format_timestamp_micros(1_713_686_401_234_567),
|
||||
"1713686401.234567Z"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset_datetime_to_unix_micros_is_stable() {
|
||||
let value = OffsetDateTime::UNIX_EPOCH
|
||||
+ time::Duration::seconds(1_713_686_401)
|
||||
+ time::Duration::microseconds(234_567);
|
||||
|
||||
assert_eq!(offset_datetime_to_unix_micros(value), 1_713_686_401_234_567);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_and_parse_rfc3339_round_trip() {
|
||||
let now = OffsetDateTime::UNIX_EPOCH + time::Duration::seconds(1_713_686_400);
|
||||
let text = format_rfc3339(now).expect("rfc3339 should format");
|
||||
let parsed = parse_rfc3339(&text).expect("rfc3339 should parse");
|
||||
|
||||
assert_eq!(parsed.unix_timestamp(), now.unix_timestamp());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user