Files
Genarrative/server-rs/crates/shared-kernel/src/lib.rs
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

153 lines
5.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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());
}
}