Skip to main content

alun_utils/
serial.rs

1//! 业务单号生成器:基于规则配置的分布式单号引擎
2//!
3//! # 设计理念
4//!
5//! 将"格式模板 + 循环周期 + 计数策略"抽象为可配置的 `SerialRule`,
6//! 通过 `SerialGenerator` trait 统一三种后端实现(内存锁/Redis锁/PG锁)。
7//!
8//! # 格式语法
9//!
10//! ```text
11//! {YYYY}    - 4 位年份
12//! {YY}      - 2 位年份
13//! {MM}      - 2 位月份
14//! {DD}      - 2 位日期
15//! {SEQ:n}   - 定长顺序号(n 位,不足补零)
16//! {RAND:n}  - 定长随机数(n 位)
17//! {TS}      - Unix 时间戳(秒)
18//! {TSMS}    - Unix 时间戳(毫秒)
19//! ```
20//!
21//! # 示例
22//!
23//! ```ignore
24//! let rule = SerialRule::new("order", "ORD{YYYY}{MM}{DD}{SEQ:8}")
25//!     .with_cycle(CyclePeriod::Daily);
26//! backend.register_rule(rule).await?;
27//! let no = backend.generate("order").await?; // ORD2024052400000001
28//! ```
29
30use std::collections::HashMap;
31use std::fmt;
32use async_trait::async_trait;
33use chrono::{Local, Datelike};
34use serde::{Deserialize, Serialize};
35use tokio::sync::Mutex;
36
37// ──── 错误类型 ────────────────────────────────────
38
39/// 单号生成器错误
40#[derive(Debug, Clone)]
41pub enum SerialError {
42    /// 规则未找到
43    RuleNotFound(String),
44    /// 规则已禁用
45    RuleDisabled(String),
46    /// 格式解析错误
47    FormatError(String),
48    /// 计数器溢出
49    CounterOverflow(String),
50    /// 后端存储错误
51    StorageError(String),
52}
53
54impl fmt::Display for SerialError {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Self::RuleNotFound(k) => write!(f, "单号规则未找到: {}", k),
58            Self::RuleDisabled(k) => write!(f, "单号规则已禁用: {}", k),
59            Self::FormatError(msg) => write!(f, "格式错误: {}", msg),
60            Self::CounterOverflow(msg) => write!(f, "计数器溢出: {}", msg),
61            Self::StorageError(msg) => write!(f, "存储错误: {}", msg),
62        }
63    }
64}
65
66impl std::error::Error for SerialError {}
67
68// ──── 周期类型 ────────────────────────────────────
69
70/// 循环周期:计数器何时重置
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
72#[serde(rename_all = "snake_case")]
73pub enum CyclePeriod {
74    /// 不循环——计数器终生递增
75    #[default]
76    NoCycle,
77    /// 按天循环(YYYYMMDD)
78    Daily,
79    /// 按月循环(YYYYMM)
80    Monthly,
81    /// 按年循环(YYYY)
82    Yearly,
83}
84
85impl CyclePeriod {
86    /// 计算当前周期值:Daily→20240524,Monthly→202405,Yearly→2024,NoCycle→空
87    pub fn current_value(&self) -> String {
88        let now = Local::now();
89        match self {
90            Self::NoCycle => String::new(),
91            Self::Daily => format!("{:04}{:02}{:02}", now.year(), now.month(), now.day()),
92            Self::Monthly => format!("{:04}{:02}", now.year(), now.month()),
93            Self::Yearly => format!("{:04}", now.year()),
94        }
95    }
96}
97
98// ──── 增量策略 ────────────────────────────────────
99
100/// 增量方式
101#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum IncrementStrategy {
104    /// 顺序递增(步长 1)
105    Sequential,
106    /// 随机增量——在 `[1, max]` 范围内随机跳动
107    Random { max: u64 },
108}
109
110impl Default for IncrementStrategy {
111    fn default() -> Self { Self::Sequential }
112}
113
114// ──── 单号规则 ────────────────────────────────────
115
116/// 业务单号规则定义
117///
118/// 一条规则 = 格式模板 + 循环周期 + 计数策略 + 启用状态。
119/// 可通过静态配置(`config.toml`)或运行时 API(`register_rule`)注册。
120/// 禁用后的规则调用 `generate()` 将返回 `SerialError::RuleDisabled`。
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct SerialRule {
123    /// 规则唯一标识,如 "order"、"contract"、"purchase"
124    pub key: String,
125    /// 单号格式,如 "ORD{YYYY}{MM}{DD}{SEQ:8}"
126    pub format: String,
127    /// 循环周期
128    #[serde(default)]
129    pub cycle: CyclePeriod,
130    /// 计数器初始值(每次周期重置后从此值开始)
131    #[serde(default = "default_initial_value")]
132    pub initial_value: u64,
133    /// 增量策略
134    #[serde(default)]
135    pub step: IncrementStrategy,
136    /// 是否启用(默认 true)。禁用后 `generate()` 返回错误。
137    #[serde(default = "default_true")]
138    pub is_enabled: bool,
139}
140
141fn default_initial_value() -> u64 { 1 }
142fn default_true() -> bool { true }
143
144impl SerialRule {
145    /// 创建新规则(默认启用)
146    pub fn new(key: impl Into<String>, format: impl Into<String>) -> Self {
147        Self {
148            key: key.into(),
149            format: format.into(),
150            cycle: CyclePeriod::NoCycle,
151            initial_value: 1,
152            step: IncrementStrategy::Sequential,
153            is_enabled: true,
154        }
155    }
156
157    /// 设置循环周期
158    pub fn with_cycle(mut self, cycle: CyclePeriod) -> Self {
159        self.cycle = cycle;
160        self
161    }
162
163    /// 设置初始值
164    pub fn with_initial_value(mut self, val: u64) -> Self {
165        self.initial_value = val;
166        self
167    }
168
169    /// 设置增量策略
170    pub fn with_step(mut self, step: IncrementStrategy) -> Self {
171        self.step = step;
172        self
173    }
174}
175
176// ──── 生成记录 ────────────────────────────────────
177
178/// 单号生成记录
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct SerialRecord {
181    /// 规则 key
182    pub rule_key: String,
183    /// 生成的单号
184    pub serial_no: String,
185    /// 当前计数器值
186    pub counter: u64,
187    /// 周期值(Daily→YYYYMMDD,NoCycle→空)
188    pub cycle_value: String,
189    /// 生成时间(RFC3339)
190    pub created_at: String,
191}
192
193// ──── 格式引擎 ────────────────────────────────────
194
195/// 格式片段
196#[derive(Debug, Clone, PartialEq, Eq)]
197enum FormatSegment {
198    /// 原始文本
199    Literal(String),
200    /// {YYYY}(false)或 {YY}(true)
201    Year(bool),
202    /// {MM}
203    Month,
204    /// {DD}
205    Day,
206    /// {SEQ:n}
207    Seq(u32),
208    /// {RAND:n}
209    Random(u32),
210    /// {TS}(false)或 {TSMS}(true)
211    Timestamp(bool),
212}
213
214/// 格式引擎:解析并渲染格式字符串
215///
216/// 预编译格式字符串为片段列表,生成单号时填充日期和序列号。
217#[derive(Debug, Clone)]
218pub struct FormatEngine {
219    segments: Vec<FormatSegment>,
220}
221
222impl FormatEngine {
223    /// 从格式字符串编译格式引擎
224    ///
225    /// # 错误
226    ///
227    /// 格式字符串含未知占位符或语法错误时返回 `SerialError::FormatError`。
228    pub fn compile(format: &str) -> Result<Self, SerialError> {
229        let mut segments = Vec::new();
230        let chars: Vec<char> = format.chars().collect();
231        let len = chars.len();
232        let mut i = 0;
233
234        while i < len {
235            if chars[i] == '{' {
236                let mut j = i + 1;
237                while j < len && chars[j] != '}' {
238                    j += 1;
239                }
240                if j >= len {
241                    return Err(SerialError::FormatError(format!(
242                        "未闭合的 '{{': 位置 {} 格式 '{}'", i, format
243                    )));
244                }
245                let token: String = chars[i + 1..j].iter().collect();
246                segments.push(Self::parse_token(&token, format)?);
247                i = j + 1;
248            } else {
249                let start = i;
250                while i < len && chars[i] != '{' {
251                    i += 1;
252                }
253                let lit: String = chars[start..i].iter().collect();
254                if !lit.is_empty() {
255                    segments.push(FormatSegment::Literal(lit));
256                }
257            }
258        }
259
260        Ok(Self { segments })
261    }
262
263    fn parse_token(token: &str, full_format: &str) -> Result<FormatSegment, SerialError> {
264        match token {
265            "YYYY" => Ok(FormatSegment::Year(false)),
266            "YY" => Ok(FormatSegment::Year(true)),
267            "MM" => Ok(FormatSegment::Month),
268            "DD" => Ok(FormatSegment::Day),
269            "TS" => Ok(FormatSegment::Timestamp(false)),
270            "TSMS" => Ok(FormatSegment::Timestamp(true)),
271            _ => {
272                if let Some(w) = token.strip_prefix("SEQ:") {
273                    let width: u32 = w.parse().map_err(|_| {
274                        SerialError::FormatError(format!("SEQ 位数无效: '{}'", token))
275                    })?;
276                    if width == 0 {
277                        return Err(SerialError::FormatError("SEQ 位数必须大于 0".into()));
278                    }
279                    Ok(FormatSegment::Seq(width))
280                } else if let Some(w) = token.strip_prefix("RAND:") {
281                    let width: u32 = w.parse().map_err(|_| {
282                        SerialError::FormatError(format!("RAND 位数无效: '{}'", token))
283                    })?;
284                    if width == 0 {
285                        return Err(SerialError::FormatError("RAND 位数必须大于 0".into()));
286                    }
287                    Ok(FormatSegment::Random(width))
288                } else {
289                    Err(SerialError::FormatError(format!(
290                        "未知占位符: '{{{}}}' 在格式 '{}'", token, full_format
291                    )))
292                }
293            }
294        }
295    }
296
297    /// 渲染最终单号
298    ///
299    /// - `seq`: 序列号值
300    /// - `ts`: 可选的时间戳(秒),无则使用当前时间
301    pub fn render(&self, seq: u64, ts: Option<i64>) -> String {
302        let now = ts.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
303            .unwrap_or_else(|| {
304                Local::now().naive_local().and_utc()
305            });
306
307        let mut result = String::new();
308        for seg in &self.segments {
309            match seg {
310                FormatSegment::Literal(s) => result.push_str(s),
311                FormatSegment::Year(short) => {
312                    let y = now.year();
313                    if *short {
314                        result.push_str(&format!("{:02}", y % 100));
315                    } else {
316                        result.push_str(&format!("{:04}", y));
317                    }
318                }
319                FormatSegment::Month => result.push_str(&format!("{:02}", now.month())),
320                FormatSegment::Day => result.push_str(&format!("{:02}", now.day())),
321                FormatSegment::Seq(w) => {
322                    result.push_str(&format!("{:0width$}", seq, width = *w as usize));
323                }
324                FormatSegment::Random(w) => {
325                    let max = 10u64.pow(*w);
326                    let rand_val: u64 = rand::random::<u64>() % max;
327                    result.push_str(&format!("{:0width$}", rand_val, width = *w as usize));
328                }
329                FormatSegment::Timestamp(ms) => {
330                    if *ms {
331                        result.push_str(&format!("{}", now.timestamp_millis()));
332                    } else {
333                        result.push_str(&format!("{}", now.timestamp()));
334                    }
335                }
336            }
337        }
338        result
339    }
340}
341
342/// 检测序列号部分的宽度(用于溢出检查)
343fn detect_seq_width(segments: &[FormatSegment]) -> u32 {
344    for seg in segments {
345        if let FormatSegment::Seq(w) = seg {
346            return *w;
347        }
348    }
349    20 // 默认 20 位
350}
351
352// ──── SerialGenerator Trait ───────────────────────
353
354/// 单号生成器抽象 trait
355///
356/// 三种后端实现共享此接口,遵循 `TaskStorage` 设计模式:
357/// - 业务方实现自己的后端(或使用内置的 Memory/Redis/PG 后端)
358/// - 通过 `Arc<dyn SerialGenerator>` 注入到 `SerialPlugin`
359///
360/// # 示例
361///
362/// ```ignore
363/// let backend = Arc::new(MemorySerialBackend::new());
364/// backend.register_rule(rule).await?;
365/// let order_no = backend.generate("order").await?;
366/// ```
367#[async_trait]
368pub trait SerialGenerator: Send + Sync {
369    /// 生成下一个单号
370    async fn generate(&self, rule_key: &str) -> Result<String, SerialError>;
371
372    /// 批量生成单号
373    async fn batch_generate(
374        &self, rule_key: &str, count: u32,
375    ) -> Result<Vec<String>, SerialError>;
376
377    /// 预览下一个单号(不消耗计数器)
378    async fn peek(&self, rule_key: &str) -> Result<String, SerialError>;
379
380    /// 运行时注册/更新一条规则
381    async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError>;
382
383    /// 运行时删除一条规则
384    async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError>;
385
386    /// 运行时启用一条规则
387    async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
388
389    /// 运行时禁用一条规则
390    async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
391
392    /// 查询生成记录(分页),返回 (记录列表, 总数)
393    async fn query_records(
394        &self, rule_key: &str, page: u64, page_size: u64,
395    ) -> Result<(Vec<SerialRecord>, u64), SerialError>;
396
397    /// 获取所有已注册的规则
398    async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError>;
399}
400
401// ──── MemorySerialBackend ─────────────────────────
402
403/// 计数器状态
404#[derive(Debug, Clone)]
405struct CounterState {
406    value: u64,
407    cycle: String,
408}
409
410/// 基于内存 + `tokio::sync::Mutex` 的单号生成后端
411///
412/// 适用于单机部署。计数器按 `{rule_key}:{cycle_value}` 维度分组存储。
413///
414/// # 示例
415///
416/// ```ignore
417/// let backend = MemorySerialBackend::new();
418/// backend.register_rule(SerialRule::new("order", "ORD{SEQ:6}")).await?;
419/// let no = backend.generate("order").await?;
420/// ```
421pub struct MemorySerialBackend {
422    rules: Mutex<HashMap<String, SerialRule>>,
423    counters: Mutex<HashMap<String, CounterState>>,
424    engines: Mutex<HashMap<String, FormatEngine>>,
425    records: Mutex<Vec<SerialRecord>>,
426    max_records: usize,
427}
428
429impl MemorySerialBackend {
430    /// 创建内存后端(最多保留 10000 条记录)
431    pub fn new() -> Self {
432        Self {
433            rules: Mutex::new(HashMap::new()),
434            counters: Mutex::new(HashMap::new()),
435            engines: Mutex::new(HashMap::new()),
436            records: Mutex::new(Vec::new()),
437            max_records: 10000,
438        }
439    }
440
441    /// 设置最大记录数
442    pub fn with_max_records(mut self, max: usize) -> Self {
443        self.max_records = max;
444        self
445    }
446
447    fn counter_key(rule_key: &str, cycle_value: &str) -> String {
448        if cycle_value.is_empty() {
449            rule_key.to_string()
450        } else {
451            format!("{}:{}", rule_key, cycle_value)
452        }
453    }
454
455    async fn get_or_init_counter(
456        &self, rule: &SerialRule,
457    ) -> Result<(String, u64), SerialError> {
458        let cycle_val = rule.cycle.current_value();
459        let ck = Self::counter_key(&rule.key, &cycle_val);
460
461        let mut counters = self.counters.lock().await;
462        if let Some(state) = counters.get(&ck) {
463            if state.cycle == cycle_val {
464                return Ok((ck, state.value));
465            }
466        }
467
468        let init = rule.initial_value;
469        counters.insert(
470            ck.clone(),
471            CounterState { value: init, cycle: cycle_val },
472        );
473        Ok((ck, init))
474    }
475}
476
477impl Default for MemorySerialBackend {
478    fn default() -> Self { Self::new() }
479}
480
481#[async_trait]
482impl SerialGenerator for MemorySerialBackend {
483    async fn generate(&self, rule_key: &str) -> Result<String, SerialError> {
484        let rules = self.rules.lock().await;
485        let rule = rules
486            .get(rule_key)
487            .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
488            .clone();
489        drop(rules);
490
491        // 检查规则是否启用
492        if !rule.is_enabled {
493            return Err(SerialError::RuleDisabled(rule_key.to_string()));
494        }
495
496        let engine = {
497            let mut engines = self.engines.lock().await;
498            if !engines.contains_key(rule_key) {
499                let eng = FormatEngine::compile(&rule.format)?;
500                engines.insert(rule_key.to_string(), eng);
501            }
502            engines.get(rule_key).unwrap().clone()
503        };
504
505        let (ck, current_val) = self.get_or_init_counter(&rule).await?;
506
507        let next_val = match &rule.step {
508            IncrementStrategy::Sequential => current_val + 1,
509            IncrementStrategy::Random { max } => {
510                let max = *max;
511                let step: u64 = rand::random::<u64>() % if max > 0 { max } else { 1 } + 1;
512                current_val + step
513            }
514        };
515
516        let seq_width = detect_seq_width(&engine.segments);
517        let max_val = 10u64.pow(seq_width).saturating_sub(1);
518        if next_val > max_val {
519            return Err(SerialError::CounterOverflow(format!(
520                "规则 '{}' 序列号溢出: {} > 最大值 {}", rule_key, next_val, max_val
521            )));
522        }
523
524        let serial_no = engine.render(current_val, None);
525
526        {
527            let mut counters = self.counters.lock().await;
528            if let Some(state) = counters.get_mut(&ck) {
529                state.value = next_val;
530            }
531        }
532
533        {
534            let mut records = self.records.lock().await;
535            records.push(SerialRecord {
536                rule_key: rule_key.to_string(),
537                serial_no: serial_no.clone(),
538                counter: current_val,
539                cycle_value: rule.cycle.current_value(),
540                created_at: Local::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
541            });
542            while records.len() > self.max_records {
543                records.remove(0);
544            }
545        }
546
547        Ok(serial_no)
548    }
549
550    async fn batch_generate(
551        &self, rule_key: &str, count: u32,
552    ) -> Result<Vec<String>, SerialError> {
553        let mut result = Vec::with_capacity(count as usize);
554        for _ in 0..count {
555            result.push(self.generate(rule_key).await?);
556        }
557        Ok(result)
558    }
559
560    async fn peek(&self, rule_key: &str) -> Result<String, SerialError> {
561        let rules = self.rules.lock().await;
562        let rule = rules
563            .get(rule_key)
564            .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
565            .clone();
566        drop(rules);
567
568        let engine = {
569            let mut engines = self.engines.lock().await;
570            if !engines.contains_key(rule_key) {
571                let eng = FormatEngine::compile(&rule.format)?;
572                engines.insert(rule_key.to_string(), eng);
573            }
574            engines.get(rule_key).unwrap().clone()
575        };
576
577        let cycle_val = rule.cycle.current_value();
578        let ck = Self::counter_key(rule_key, &cycle_val);
579
580        let current_val = {
581            let counters = self.counters.lock().await;
582            counters.get(&ck).map(|s| s.value).unwrap_or(rule.initial_value)
583        };
584
585        Ok(engine.render(current_val, None))
586    }
587
588    async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError> {
589        FormatEngine::compile(&rule.format)?;
590        let key = rule.key.clone();
591        self.rules.lock().await.insert(key.clone(), rule);
592        self.engines.lock().await.remove(&key);
593        Ok(())
594    }
595
596    async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError> {
597        self.rules.lock().await.remove(rule_key);
598        self.engines.lock().await.remove(rule_key);
599        Ok(())
600    }
601
602    async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
603        let mut rules = self.rules.lock().await;
604        let rule = rules
605            .get_mut(rule_key)
606            .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
607        rule.is_enabled = true;
608        Ok(())
609    }
610
611    async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
612        let mut rules = self.rules.lock().await;
613        let rule = rules
614            .get_mut(rule_key)
615            .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
616        rule.is_enabled = false;
617        Ok(())
618    }
619
620    async fn query_records(
621        &self, rule_key: &str, page: u64, page_size: u64,
622    ) -> Result<(Vec<SerialRecord>, u64), SerialError> {
623        let records = self.records.lock().await;
624        let filtered: Vec<&SerialRecord> = records
625            .iter()
626            .filter(|r| r.rule_key == rule_key)
627            .collect();
628        let total = filtered.len() as u64;
629        let offset = ((page.saturating_sub(1)) * page_size) as usize;
630        let data: Vec<SerialRecord> = filtered
631            .into_iter().rev()
632            .skip(offset).take(page_size as usize).cloned().collect();
633        Ok((data, total))
634    }
635
636    async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError> {
637        Ok(self.rules.lock().await.values().cloned().collect())
638    }
639}
640
641// ──── 测试 ────────────────────────────────────────
642
643#[cfg(test)]
644mod tests {
645    use super::*;
646
647    #[test]
648    fn test_format_compile_basic() {
649        let eng = FormatEngine::compile("ORD{YYYY}{MM}{DD}{SEQ:8}").unwrap();
650        assert_eq!(eng.segments.len(), 5);
651    }
652
653    #[test]
654    fn test_format_compile_all_types() {
655        let eng = FormatEngine::compile("P{YY}{MM}{DD}-{RAND:4}{SEQ:6}").unwrap();
656        assert_eq!(eng.segments.len(), 7);
657    }
658
659    #[test]
660    fn test_format_render() {
661        let eng = FormatEngine::compile("TST{YYYY}{MM}{DD}{SEQ:4}").unwrap();
662        let ts = chrono::NaiveDate::from_ymd_opt(2024, 5, 24)
663            .unwrap()
664            .and_hms_opt(0, 0, 0)
665            .unwrap()
666            .and_utc()
667            .timestamp();
668        assert_eq!(eng.render(1, Some(ts)), "TST202405240001");
669    }
670
671    #[test]
672    fn test_format_render_padding() {
673        let eng = FormatEngine::compile("SEQ:{SEQ:6}").unwrap();
674        assert_eq!(eng.render(123, None), "SEQ:000123");
675    }
676
677    #[test]
678    fn test_format_parse_error() {
679        assert!(FormatEngine::compile("X{UNKNOWN}").is_err());
680    }
681
682    #[tokio::test]
683    async fn test_memory_generate_simple() {
684        let backend = MemorySerialBackend::new();
685        backend.register_rule(SerialRule::new("t", "T{SEQ:4}").with_initial_value(1)).await.unwrap();
686        assert_eq!(backend.generate("t").await.unwrap(), "T0001");
687        assert_eq!(backend.generate("t").await.unwrap(), "T0002");
688    }
689
690    #[tokio::test]
691    async fn test_memory_daily_cycle() {
692        let backend = MemorySerialBackend::new();
693        let rule = SerialRule::new("d", "{SEQ:3}").with_cycle(CyclePeriod::Daily).with_initial_value(1);
694        backend.register_rule(rule).await.unwrap();
695        assert_eq!(backend.generate("d").await.unwrap(), "001");
696        assert_eq!(backend.generate("d").await.unwrap(), "002");
697    }
698
699    #[tokio::test]
700    async fn test_memory_peek() {
701        let backend = MemorySerialBackend::new();
702        backend.register_rule(SerialRule::new("p", "{SEQ:3}").with_initial_value(100)).await.unwrap();
703        assert_eq!(backend.peek("p").await.unwrap(), "100");
704        assert_eq!(backend.generate("p").await.unwrap(), "100");
705    }
706
707    #[tokio::test]
708    async fn test_memory_register_remove() {
709        let backend = MemorySerialBackend::new();
710        backend.register_rule(SerialRule::new("tmp", "{SEQ:2}")).await.unwrap();
711        assert!(backend.list_rules().await.unwrap().len() == 1);
712        backend.remove_rule("tmp").await.unwrap();
713        assert!(backend.list_rules().await.unwrap().is_empty());
714        assert!(backend.generate("tmp").await.is_err());
715    }
716}