use std::collections::HashMap;
use std::sync::OnceLock;
use parking_lot::Mutex;
#[derive(Clone, Copy, Debug)]
struct SeqMeta {
current: i64,
increment: i64,
}
impl Default for SeqMeta {
fn default() -> Self {
Self {
current: 0,
increment: 1,
}
}
}
fn store() -> &'static Mutex<HashMap<String, SeqMeta>> {
static STORE: OnceLock<Mutex<HashMap<String, SeqMeta>>> = OnceLock::new();
STORE.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn create_sequence(name: &str, if_not_exists: bool, start: Option<i64>, increment: Option<i64>) {
let increment = match increment.unwrap_or(1) {
0 => 1,
n => n,
};
let start = start.unwrap_or(1);
let mut guard = store().lock();
if guard.contains_key(name) && if_not_exists {
return;
}
guard.insert(
name.to_string(),
SeqMeta {
current: start.saturating_sub(increment),
increment,
},
);
}
pub fn nextval(name: &str) -> i64 {
let mut guard = store().lock();
let slot = guard.entry(name.to_string()).or_default();
slot.current += slot.increment;
slot.current
}
pub fn currval(name: &str) -> i64 {
store().lock().get(name).map_or(0, |m| m.current)
}
pub fn setval(name: &str, value: i64, is_called: bool) -> i64 {
let mut guard = store().lock();
let entry = guard.entry(name.to_string()).or_default();
entry.current = if is_called {
value
} else {
value.saturating_sub(entry.increment)
};
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_sequence_starts_at_one() {
create_sequence("seq_default", false, None, None);
assert_eq!(nextval("seq_default"), 1);
assert_eq!(nextval("seq_default"), 2);
assert_eq!(currval("seq_default"), 2);
}
#[test]
fn honors_start_and_increment() {
create_sequence("seq_si", false, Some(100), Some(10));
assert_eq!(nextval("seq_si"), 100);
assert_eq!(nextval("seq_si"), 110);
assert_eq!(nextval("seq_si"), 120);
}
#[test]
fn setval_preserves_increment() {
create_sequence("seq_sv", false, Some(1), Some(5));
assert_eq!(nextval("seq_sv"), 1);
setval("seq_sv", 50, true);
assert_eq!(nextval("seq_sv"), 55);
}
#[test]
fn setval_is_called_false_makes_next_nextval_equal_value() {
create_sequence("seq_sv_false", false, Some(1), Some(5));
assert_eq!(nextval("seq_sv_false"), 1);
setval("seq_sv_false", 300, false);
assert_eq!(nextval("seq_sv_false"), 300);
assert_eq!(nextval("seq_sv_false"), 305);
}
#[test]
fn unknown_sequence_auto_vivifies_at_one() {
assert_eq!(nextval("seq_never_created_xyz"), 1);
}
}