synthphonia/utils/mod.rs
1
2use derive_more::{From, Into, Deref, DerefMut, Display, DebugCustom};
3use futures::{future::select, FutureExt};
4use futures_core::Future;
5
6pub mod join;
7pub mod nested;
8#[derive(From, Into, Deref, DerefMut, DebugCustom, Display, PartialEq, PartialOrd, Clone, Copy)]
9#[debug(fmt = "{:?}", _0)]
10#[display(fmt = "{:?}", _0)]
11/// A newtype wrapper encapsulating a 64-bit floating-point value.
12/// It provides an abstraction over the underlying primitive to support precise numerical handling.
13///
14/// This structure is designed to integrate seamlessly with various trait implementations, enabling convenient conversion, dereferencing, cloning, and formatted output operations based on its internal floating-point representation.
15pub struct F64(pub f64);
16impl F64 {
17 /// Creates a new floating-point instance ensuring numerical precision by rounding the input to 10 decimal places.
18 ///
19 /// Rounds the provided f64 value by multiplying it by 1e10, applying rounding, and then dividing by 1e10 to harmonize precision before encapsulating it within the new type.
20 pub fn new(value: f64) -> Self {
21 Self((value * 1e10).round() / 1e10)
22 }
23 /// Converts an unsigned integer into a floating-point representation encapsulated by the custom numeric wrapper.
24 ///
25 ///
26 /// Enables users to create a new instance of the f64-based type from a usize value by performing a straightforward type conversion.
27 pub fn from_usize(value: usize) -> Self {
28 Self(value as f64)
29 }
30}
31impl Eq for F64 {}
32
33impl std::hash::Hash for F64 {
34 /// Computes a hash for the wrapped floating-point value based on its bit representation.
35 ///
36 ///
37 /// Hashes the underlying value by interpreting the bits of the inner floating-point as an integer and feeding it into the provided hasher state.
38 /// This ensures that two values with the same numerical representation produce identical hash codes.
39 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
40 self.0.to_bits().hash(state)
41 }
42}
43
44
45use std::cell::UnsafeCell;
46
47use ext_trait::extension;
48
49
50#[extension(pub trait UnsafeCellExt)]
51impl<T> UnsafeCell<T> {
52 /// Returns a mutable reference to the underlying data contained within an unsafe cell.
53 /// This function is marked as unsafe because it directly casts the internal pointer to a mutable reference, thereby bypassing Rust’s normal borrowing rules.
54 unsafe fn as_mut(&self) -> &mut T {
55 &mut *self.get()
56 }
57 /// Replaces the value contained within an internal cell with a new value while returning the previous content.
58 ///
59 /// This function consumes a new value to update the internal state and returns the original value that was stored, effectively performing an atomic swap operation in the cell's memory.
60 fn replace(&self, v : T) -> T {
61 std::mem::replace(unsafe { self.as_mut() }, v)
62 }
63}
64
65/// Awaits a collection of futures concurrently and returns the output of the first future that completes.
66///
67///
68/// Collects the input futures into a vector, then, if the collection is empty, it stalls by awaiting a pending future; otherwise, it races all the futures and returns the output from the one that finishes first.
69pub async fn select_all<T>(
70 futures: impl IntoIterator<Item = impl std::future::Future<Output = T>>,
71) -> T {
72 let futures = futures.into_iter().collect::<Vec<_>>();
73 // Workaround against select_all's arbitrary assert
74 if futures.is_empty() {
75 return std::future::pending().await;
76 }
77 futures::future::select_all(futures.into_iter().map(Box::pin)).await.0
78}
79
80/// Selects between two futures and returns the output from the one that completes first.
81/// This function accepts two futures and, using a combinator, races them to yield the result of the first future that resolves.
82pub fn select_ret<T>(f1: impl Future<Output=T> + Unpin, f2: impl Future<Output=T> + Unpin) -> impl Future<Output=T> + Unpin {
83 select(f1, f2).map(|a| {
84 match a {
85 futures::future::Either::Left(a) => a.0,
86 futures::future::Either::Right(a) => a.0,
87 }
88 })
89}
90/// Returns a future that resolves with the output of the first among three given futures to complete.
91/// This function concurrently races the three input futures and awaits the one that finishes first, effectively combining them into a single asynchronous computation whose result is that of the first completed future.
92pub fn select_ret3<T>(f1: impl Future<Output=T> + Unpin, f2: impl Future<Output=T> + Unpin, f3: impl Future<Output=T> + Unpin) ->
93 impl Future<Output = T> {
94 select_ret(f1, select_ret(f2, f3))
95}
96/// Returns the output of the first completed future among four concurrently evaluated futures.
97/// This function accepts four futures as parameters and returns a new future that resolves with the result of the first future to complete, ensuring a non-blocking selection process.
98pub fn select_ret4<T>(f1: impl Future<Output=T> + Unpin, f2: impl Future<Output=T> + Unpin, f3: impl Future<Output=T> + Unpin, f4: impl Future<Output=T> + Unpin) ->
99 impl Future<Output = T> {
100 select_ret(f1, select_ret(f2, select_ret(f3, f4)))
101}
102/// Returns a future that yields the output of the earliest completed one among five provided futures.
103///
104/// Selects five futures to run concurrently and returns a new future that resolves with the value from the first future to complete.
105/// This utility function chains the selections to ultimately combine all five inputs into a single asynchronous operation without blocking for the slower ones.
106pub fn select_ret5<T>(f1: impl Future<Output=T> + Unpin, f2: impl Future<Output=T> + Unpin, f3: impl Future<Output=T> + Unpin, f4: impl Future<Output=T> + Unpin, f5: impl Future<Output=T> + Unpin) ->
107 impl Future<Output = T> {
108 select_ret(f1, select_ret(f2, select_ret(f3, select_ret(f4, f5))))
109}
110
111/// Awaits a provided future if a condition holds, otherwise yields a pending future that never resolves.
112/// This function evaluates a boolean and, when true, awaits and returns the output of the supplied future; if false, it defers execution indefinitely by awaiting a future that remains pending.
113pub async fn pending_if<T>(condition: bool, fut: impl Future<Output=T>) -> T {
114 if condition { fut.await } else { crate::never!() }
115}
116
117
118#[macro_export]
119/// Creates an asynchronous block that clones a collection of variables before evaluating a given expression.
120///
121///
122/// This macro accepts a list of identifiers enclosed in square brackets and an expression.
123/// It clones each specified variable and produces an async move block that evaluates the expression, thereby reducing boilerplate code when working with asynchronous closures that require cloned values.
124macro_rules! async_clone {
125 ( [$( $x:ident )*] $y:expr ) => {
126 {
127 $(let $x = $x.clone();)*
128 async move { $y }
129 }
130 };
131}
132
133#[macro_export]
134/// Provides utility for rebinding a variable with different semantics.
135/// This macro accepts a pattern specifying the rebinding mode—ref, clone, move, or mut—followed by an identifier, and expands it into a let binding that creates a new binding corresponding to that mode (borrowing immutably, cloning, moving, or borrowing mutably).
136macro_rules! rebinding {
137 (ref $x:ident) => { let $x = &$x; };
138 (clone $x:ident) => { let $x = $x.clone(); };
139 (move $x:ident) => { let $x = $x; };
140 (mut $x:ident) => { let $x = &mut $x; };
141}
142
143#[macro_export]
144/// Creates a closure-like block that performs variable rebinding before evaluating an expression.
145/// This macro takes a comma-separated list of identifier pairs, where each pair specifies a rebinding using a helper mechanism, and a trailing expression; it expands to a block that first applies the rebinding operations and then evaluates the provided expression within that modified scope.
146macro_rules! closure {
147 ( $( $x:ident $v:ident ),*; $y:expr ) => {
148 {
149 $($crate::rebinding!($x $v); )*
150 { $y }
151 }
152 };
153}
154#[macro_export]
155/// Generates an asynchronous closure that rebinding the provided expressions and evaluates a given expression within the async context.
156/// This macro takes a list of expressions to be rebound using a helper rebinding macro, then returns an async move block that evaluates the specified expression while capturing the rebound variables.
157macro_rules! async_closure {
158 ( [$( $x:expr );*] $y:expr ) => {
159 {
160 $($crate::rebinding!($x); )*
161 async move { $y }
162 }
163 };
164}
165
166#[macro_export]
167/// Generates an asynchronous expression that yields a future which never resolves.
168/// This macro conditionally produces a pending future, either using the default type or a specified type parameter, blocking execution indefinitely when awaited.
169macro_rules! never {
170 () => { futures::future::pending().await };
171 ($t:ty) => { futures::future::pending::<$t>().await };
172}
173#[extension(pub trait TryRetain)]
174impl<T> Vec<T> {
175 /// Filters vector elements in-place by applying a predicate function that can return an error.
176 ///
177 ///
178 /// Evaluates each element using the provided function, retaining or removing elements based on the returned boolean value when successful; if the function returns an error for an element, the error is recorded and the element is retained, with the final result reflecting any encountered error.
179 fn try_retain<E, F>(&mut self, mut f: F) -> Result<(), E>
180 where F: FnMut(&T) -> Result<bool, E> {
181
182 let mut result = Ok(());
183 self.retain(|v| match f(v) {
184 Ok(b) => b,
185 Err(e) => {
186 result = Err(e);
187 true
188 }
189 });
190
191 result
192 }
193}
194
195
196
197
198
199
200