//! Utility // Features #![feature( decl_macro, coroutine_trait, coroutines, never_type, type_alias_impl_trait, if_let_guard, extend_one, must_not_suspend, impl_trait_in_assoc_type, try_trait_v2, assert_matches, yeet_expr, const_trait_impl, nonpoison_mutex, sync_nonpoison, async_fn_traits, trait_alias, unboxed_closures, tuple_trait, try_blocks, proc_macro_hygiene, stmt_expr_attributes, return_type_notation, fn_traits )] // Modules pub mod loadable; mod rect; pub mod resource_manager; mod tuple_collect_res; pub mod unwrap_or_return; pub mod walk_dir; // Exports pub use { loadable::Loadable, rect::Rect, resource_manager::ResourceManager, tuple_collect_res::{TupleCollectRes1, TupleCollectRes2, TupleCollectRes3, TupleCollectRes4, TupleCollectRes5}, unwrap_or_return::{UnwrapOrReturn, UnwrapOrReturnExt}, walk_dir::WalkDir, }; // Imports use { app_error::Context, image::DynamicImage, serde::de::DeserializeOwned, std::{fs, future::Future, path::Path}, zutil_cloned::cloned, }; /// App error export with our data pub type AppError = app_error::AppError<()>; /// Parses json from a file pub fn parse_json_from_file(path: impl AsRef) -> Result { // Open the file let file = fs::File::open(path).context("Unable to open file")?; // Then parse it serde_json::from_reader(file).context("Unable to parse file") } /// Serializes json to a file pub fn serialize_json_to_file(path: impl AsRef, value: &T) -> Result<(), AppError> { // Open the file let file = fs::File::create(path).context("Unable to create file")?; // Then serialize it serde_json::to_writer_pretty(file, value).context("Unable to serialize to file") } /// Returns the image format string of an image (for logging) #[must_use] pub fn image_format(image: &DynamicImage) -> &'static str { match image { DynamicImage::ImageLuma8(_) => "Luma8", DynamicImage::ImageLumaA8(_) => "LumaA8", DynamicImage::ImageRgb8(_) => "Rgb8", DynamicImage::ImageRgba8(_) => "Rgba8", DynamicImage::ImageLuma16(_) => "Luma16", DynamicImage::ImageLumaA16(_) => "LumaA16", DynamicImage::ImageRgb16(_) => "Rgb16", DynamicImage::ImageRgba16(_) => "Rgba16", _ => "", } } /// Ensures `cond` is true in a `where` clause pub macro where_assert($cond:expr) { // Note: If `true`, this expands to `[(); 0]`, which is valid // If `false`, it expands to `[(); -1]`, which is invalid [(); ($cond as usize) - 1] } /// Blocks on a future inside a tokio task #[extend::ext(name = TokioTaskBlockOn)] pub impl F { /// Bocks on this future within a tokio task #[track_caller] fn block_on(self) -> F::Output { let handle = tokio::runtime::Handle::current(); handle.block_on(self) } } /// Logs an error and panics with the error message pub macro log_error_panic( $($rest:tt)* ) {{ ::tracing::warn!( $($rest)* ); // TODO: Better way of getting the message as the last argument? let (.., msg) = ( $( stringify!($rest) ),* ); let msg = &msg[1..]; let msg = &msg[..msg.len() - 1]; ::std::panic!("{msg}"); }} /// Returns the maximum value in an array as a `const fn` #[must_use] pub const fn array_max(values: &[usize; N]) -> Option { let mut max = None; let mut cur_idx = 0; while cur_idx < values.len() { let value = values[cur_idx]; max = Some(match max { Some(max) => self::usize_max(max, value), None => value, }); cur_idx += 1; } max } /// Returns the maximum between two `usize` values const fn usize_max(lhs: usize, rhs: usize) -> usize { if lhs > rhs { lhs } else { rhs } } /// Spawns a task #[track_caller] pub fn spawn_task(name: impl Into, fut: Fut) where Fut: Future> + Send + 'static, { let name = name.into(); #[cloned(name)] let fut = async move { let id = tokio::task::id(); tracing::debug!("Spawning task {name:?} ({id:?})"); fut.await .inspect(|()| tracing::debug!("Task {name:?} ({id:?}) finished")) .inspect_err(|err| tracing::warn!("Task {name:?} ({id:?}) returned error: {}", err.pretty())) }; if let Err(err) = tokio::task::Builder::new().name(&name.clone()).spawn(fut) { let err = AppError::new(&err); tracing::warn!("Unable to spawn task {name:?}: {}", err.pretty()); } } /// Iterator chain pub macro iter_chain { ($only:expr $(,)?) => { $only }, ($first:expr, $($rest:expr),* $(,)?) => { std::iter::chain($first, $crate::iter_chain!($($rest,)*)) }, }