1use crate::{RandSource, Result, SmolSleep, TimeSource, Ulid, UlidGenerator};
2
3pub trait UlidGeneratorAsyncSmolExt<ID, T, R>
12where
13 ID: Ulid,
14 T: TimeSource<ID::Ty>,
15 R: RandSource<ID::Ty>,
16{
17 fn try_next_id_async(&self) -> impl Future<Output = Result<ID>>;
31}
32
33impl<G, ID, T, R> UlidGeneratorAsyncSmolExt<ID, T, R> for G
34where
35 G: UlidGenerator<ID, T, R>,
36 ID: Ulid,
37 T: TimeSource<ID::Ty>,
38 R: RandSource<ID::Ty>,
39{
40 fn try_next_id_async(&self) -> impl Future<Output = Result<ID>> {
41 <Self as crate::UlidGeneratorAsyncExt<ID, T, R>>::try_next_id_async::<SmolSleep>(self)
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::{
49 LockUlidGenerator, MonotonicClock, RandSource, Result, SleepProvider, SmolYield,
50 ThreadRandom, TimeSource, ULID, Ulid, UlidGenerator,
51 };
52 use core::fmt;
53 use futures::future::try_join_all;
54 use smol::Task;
55 use std::collections::HashSet;
56
57 const TOTAL_IDS: usize = 4096;
58 const NUM_GENERATORS: u64 = 8;
59 const IDS_PER_GENERATOR: usize = TOTAL_IDS * 8;
60
61 #[test]
63 fn generates_many_unique_ids_basic_smol_sleep() {
64 smol::block_on(async {
65 test_many_ulid_unique_ids_explicit::<ULID, _, _, _, SmolSleep>(
66 LockUlidGenerator::new,
67 MonotonicClock::default,
68 ThreadRandom::default,
69 )
70 .await
71 .unwrap();
72 });
73 }
74 #[test]
75 fn generates_many_unique_ids_basic_smol_yield() {
76 smol::block_on(async {
77 test_many_ulid_unique_ids_explicit::<ULID, _, _, _, SmolYield>(
78 LockUlidGenerator::new,
79 MonotonicClock::default,
80 ThreadRandom::default,
81 )
82 .await
83 .unwrap();
84 });
85 }
86 #[test]
87 fn generates_many_unique_ids_basic_smol_convience() {
88 smol::block_on(async {
89 test_many_ulid_unique_ids_convenience::<ULID, _, _, _>(
90 LockUlidGenerator::new,
91 MonotonicClock::default,
92 ThreadRandom::default,
93 )
94 .await
95 .unwrap();
96 });
97 }
98
99 async fn test_many_ulid_unique_ids_explicit<ID, G, T, R, S>(
101 generator_fn: impl Fn(T, R) -> G,
102 clock_factory: impl Fn() -> T,
103 rand_factory: impl Fn() -> R,
104 ) -> Result<()>
105 where
106 G: UlidGenerator<ID, T, R> + Send + Sync + 'static,
107 ID: Ulid + fmt::Debug + Send + 'static,
108 T: TimeSource<ID::Ty> + Clone + Send,
109 R: RandSource<ID::Ty> + Clone + Send,
110 S: SleepProvider,
111 {
112 let clock = clock_factory();
113 let rand = rand_factory();
114 let generators: Vec<_> = (0..NUM_GENERATORS)
115 .map(|_| generator_fn(clock.clone(), rand.clone()))
116 .collect();
117
118 let tasks: Vec<Task<Result<Vec<ID>>>> = generators
120 .into_iter()
121 .map(|g| {
122 smol::spawn(async move {
123 let mut ids = Vec::with_capacity(IDS_PER_GENERATOR);
124 for _ in 0..IDS_PER_GENERATOR {
125 let id = crate::UlidGeneratorAsyncExt::try_next_id_async::<S>(&g).await?;
126 ids.push(id);
127 }
128 Ok(ids)
129 })
130 })
131 .collect();
132
133 validate_unique_ulid_ids(tasks).await
134 }
135
136 async fn test_many_ulid_unique_ids_convenience<ID, G, T, R>(
138 generator_fn: impl Fn(T, R) -> G,
139 clock_factory: impl Fn() -> T,
140 rand_factory: impl Fn() -> R,
141 ) -> Result<()>
142 where
143 G: UlidGenerator<ID, T, R> + Send + Sync + 'static,
144 ID: Ulid + fmt::Debug + Send + 'static,
145 T: TimeSource<ID::Ty> + Clone + Send,
146 R: RandSource<ID::Ty> + Clone + Send,
147 {
148 let clock = clock_factory();
149 let rand = rand_factory();
150 let generators: Vec<_> = (0..NUM_GENERATORS)
151 .map(|_| generator_fn(clock.clone(), rand.clone()))
152 .collect();
153
154 let tasks: Vec<Task<Result<Vec<ID>>>> = generators
156 .into_iter()
157 .map(|g| {
158 smol::spawn(async move {
159 let mut ids = Vec::with_capacity(IDS_PER_GENERATOR);
160 for _ in 0..IDS_PER_GENERATOR {
161 let id = g.try_next_id_async().await?;
164 ids.push(id);
165 }
166 Ok(ids)
167 })
168 })
169 .collect();
170
171 validate_unique_ulid_ids(tasks).await
172 }
173
174 async fn validate_unique_ulid_ids(
176 tasks: Vec<Task<Result<Vec<impl Ulid>>>>,
177 ) -> Result<()> {
178 let all_ids: Vec<_> = try_join_all(tasks).await?.into_iter().flatten().collect();
179
180 let expected_total = NUM_GENERATORS as usize * IDS_PER_GENERATOR;
181 assert_eq!(
182 all_ids.len(),
183 expected_total,
184 "Expected {} IDs but got {}",
185 expected_total,
186 all_ids.len()
187 );
188
189 let mut seen = HashSet::with_capacity(all_ids.len());
190 for id in &all_ids {
191 assert!(seen.insert(id), "Duplicate ID found: {id:?}");
192 }
193
194 Ok(())
195 }
196}