use dashmap::DashMap;
use futures::Stream;
use std::sync::atomic::AtomicU64;
pub fn response_id(prefix: Option<&str>, created: u64) -> String {
let rand: u64 = rand::random();
let rand_b62 = format!("{:0>11}", base62::encode(rand as u128));
let ts_b62 = base62::encode(created as u128);
match prefix {
Some(p) => format!("{p}-{rand_b62}{ts_b62}"),
None => format!("{rand_b62}{ts_b62}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn response_id_no_prefix_shape() {
let id = response_id(None, 1_700_000_000);
assert!(!id.contains('-'), "no-prefix id must not contain a dash, got {id:?}");
assert!(id.len() > 11, "id should have content past the random half, got {id:?}");
for c in id.chars() {
assert!(c.is_ascii_alphanumeric(), "non-base62 char in {id:?}");
}
}
#[test]
fn response_id_with_prefix_shape() {
let id = response_id(Some("vctcpl"), 1_700_000_000);
let dash_pos = id.find('-').expect("prefixed id should have a dash");
let (prefix, rest) = id.split_at(dash_pos);
assert_eq!(prefix, "vctcpl");
let body = &rest[1..];
assert!(!body.contains('-'), "body after the prefix dash must not contain a dash, got {id:?}");
assert!(body.len() > 11, "body should have content past the random half, got {id:?}");
}
}
pub struct ChoiceIndexer {
counter: AtomicU64,
indices: DashMap<usize, u64>,
}
impl ChoiceIndexer {
pub fn new(initial: u64) -> Self {
Self {
counter: AtomicU64::new(initial),
indices: DashMap::new(),
}
}
pub fn get(&self, native_index: usize) -> u64 {
*self.indices.entry(native_index).or_insert_with(|| {
self.counter
.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
})
}
}
pub struct StreamOnce<T>(Option<T>);
impl<T> StreamOnce<T> {
pub fn new(item: T) -> Self {
Self(Some(item))
}
}
impl<T> Stream for StreamOnce<T>
where
T: Unpin,
{
type Item = T;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
std::task::Poll::Ready(self.as_mut().get_mut().0.take())
}
}