mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-03 17:52:15 +00:00
182 lines
4.4 KiB
Rust
182 lines
4.4 KiB
Rust
//! 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<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T, AppError> {
|
|
// 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<T: serde::Serialize>(path: impl AsRef<Path>, 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",
|
|
_ => "<unknown>",
|
|
}
|
|
}
|
|
|
|
/// 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: Future> 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<const N: usize>(values: &[usize; N]) -> Option<usize> {
|
|
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<Fut>(name: impl Into<String>, fut: Fut)
|
|
where
|
|
Fut: Future<Output = Result<(), AppError>> + 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,)*))
|
|
},
|
|
}
|