mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-04 02:08:37 +00:00
Added proc macro to create side effect functions.
This commit is contained in:
parent
1017a766f1
commit
36ef4213a3
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -1384,6 +1384,30 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
@ -2164,4 +2188,14 @@ dependencies = [
|
||||
"wgpu",
|
||||
"winit",
|
||||
"x11",
|
||||
"zsw-side-effect-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zsw-side-effect-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
|
||||
members = ["zsw"]
|
||||
members = ["zsw", "zsw-side-effect-macros"]
|
||||
resolver = "2"
|
||||
|
||||
# Compile `image` in release mode, else it's too slow to meaningfully
|
||||
|
||||
14
zsw-side-effect-macros/Cargo.toml
Normal file
14
zsw-side-effect-macros/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "zsw-side-effect-macros"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Macro
|
||||
proc-macro-error = "1.0.4"
|
||||
quote = "1.0.15"
|
||||
syn = {version = "1.0.86", features = ["full", "extra-traits"]}
|
||||
45
zsw-side-effect-macros/src/lib.rs
Normal file
45
zsw-side-effect-macros/src/lib.rs
Normal file
@ -0,0 +1,45 @@
|
||||
//! Side effect macros
|
||||
|
||||
// Imports
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
/// Adds a side effect to a function
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn side_effect(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// Get all effects as a type
|
||||
let effects = syn::parse_macro_input!(attr as syn::Type);
|
||||
|
||||
// Then parse the function
|
||||
let func = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
// Create the outer function by wrapping the output type
|
||||
let outer_func = {
|
||||
let mut outer_func = func.clone();
|
||||
// Wrap the return type
|
||||
let (r_arrow, return_ty) = match func.sig.output {
|
||||
syn::ReturnType::Default =>
|
||||
proc_macro_error::abort!(func.sig.output, "Cannot use an empty output (yet), add `-> ()`"),
|
||||
syn::ReturnType::Type(r_arrow, ty) => (r_arrow, ty),
|
||||
};
|
||||
outer_func.sig.output = syn::ReturnType::Type(
|
||||
r_arrow,
|
||||
syn::parse_quote!(crate::util::WithSideEffect<#return_ty, (#effects)>),
|
||||
);
|
||||
|
||||
// Wrap the body
|
||||
// TODO: Deal with async, unsafe and what not.
|
||||
let inner_fn_body = func.block;
|
||||
outer_func.block = syn::parse_quote! {{
|
||||
let mut __inner_fn = move || { #inner_fn_body };
|
||||
crate::util::WithSideEffect::new(__inner_fn())
|
||||
}};
|
||||
|
||||
outer_func
|
||||
};
|
||||
|
||||
quote::quote! {
|
||||
#outer_func
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@ -6,6 +6,9 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Zsw
|
||||
zsw-side-effect-macros = {path = "../zsw-side-effect-macros"}
|
||||
|
||||
# Windowing
|
||||
winit = "0.26.1"
|
||||
|
||||
|
||||
@ -4,19 +4,9 @@
|
||||
// `egui` returns a response on every operation, but we don't use them
|
||||
#![allow(unused_results)]
|
||||
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::{
|
||||
paths,
|
||||
util::{MightDeadlock, WithSideEffect},
|
||||
Egui,
|
||||
Panel,
|
||||
PanelState,
|
||||
Panels,
|
||||
Rect,
|
||||
Wgpu,
|
||||
},
|
||||
crate::{paths, util::MightDeadlock, Egui, Panel, PanelState, Panels, Rect, Wgpu},
|
||||
cgmath::{Point2, Vector2},
|
||||
crossbeam::atomic::AtomicCell,
|
||||
egui::Widget,
|
||||
@ -25,6 +15,7 @@ use {
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
window::Window,
|
||||
},
|
||||
zsw_side_effect_macros::side_effect,
|
||||
};
|
||||
|
||||
/// Settings window
|
||||
@ -50,6 +41,7 @@ impl SettingsWindow {
|
||||
/// # Deadlock
|
||||
/// Cannot be called from within `Wgpu::Render`
|
||||
// TODO: Not use a channel, but instead something else
|
||||
#[side_effect(MightDeadlock)]
|
||||
pub fn run(
|
||||
mut self,
|
||||
wgpu: &Wgpu,
|
||||
@ -59,7 +51,7 @@ impl SettingsWindow {
|
||||
paths_distributer: &paths::Distributer,
|
||||
queued_settings_window_open_click: &AtomicCell<Option<PhysicalPosition<f64>>>,
|
||||
paint_jobs_tx: &crossbeam::channel::Sender<Vec<egui::epaint::ClippedMesh>>,
|
||||
) -> WithSideEffect<(), MightDeadlock> {
|
||||
) -> () {
|
||||
loop {
|
||||
// Get the surface size
|
||||
// TODO: This can deadlock if put inside the `egui.draw` closure.
|
||||
@ -93,8 +85,6 @@ impl SettingsWindow {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WithSideEffect::new(())
|
||||
}
|
||||
|
||||
/// Draws the settings window
|
||||
|
||||
@ -11,9 +11,10 @@ use {
|
||||
super::Image,
|
||||
crate::{
|
||||
paths,
|
||||
util::{self, extse::CrossBeamChannelReceiverSE, MightDeadlock, WithSideEffect},
|
||||
util::{self, extse::CrossBeamChannelReceiverSE, MightDeadlock},
|
||||
},
|
||||
anyhow::Context,
|
||||
zsw_side_effect_macros::side_effect,
|
||||
};
|
||||
|
||||
/// Image loader
|
||||
@ -34,7 +35,8 @@ impl ImageLoader {
|
||||
/// # Deadlock
|
||||
/// Deadlocks if the path distributer deadlocks in [`paths::Distributer::run`],
|
||||
/// or if all receivers' deadlock in [`ImageReceiver::recv`].
|
||||
pub fn run(self) -> WithSideEffect<Result<(), anyhow::Error>, MightDeadlock> {
|
||||
#[side_effect(MightDeadlock)]
|
||||
pub fn run(self) -> Result<(), anyhow::Error> {
|
||||
// DEADLOCK: Caller ensures the paths distributer doesn't deadlock
|
||||
while let Ok(path) = self.paths_rx.recv().allow::<MightDeadlock>() {
|
||||
match util::measure(|| load::load_image(&path)) {
|
||||
@ -57,7 +59,7 @@ impl ImageLoader {
|
||||
};
|
||||
}
|
||||
|
||||
WithSideEffect::new(Ok(()))
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,10 +75,13 @@ impl ImageReceiver {
|
||||
///
|
||||
/// # Deadlock
|
||||
/// Deadlocks if the image loader deadlocks in [`ImageLoader::run`]
|
||||
pub fn recv(&self) -> WithSideEffect<Result<Image, anyhow::Error>, MightDeadlock> {
|
||||
#[side_effect(MightDeadlock)]
|
||||
pub fn recv(&self) -> Result<Image, anyhow::Error> {
|
||||
// DEADLOCK: Caller ensures we don't deadlock
|
||||
self.image_rx
|
||||
.recv_se()
|
||||
.map(|res| res.context("Unable to get image from loader thread"))
|
||||
.allow::<MightDeadlock>()
|
||||
.context("Unable to get image from loader thread")
|
||||
}
|
||||
|
||||
/// Attempts to receive the image
|
||||
|
||||
@ -3,9 +3,10 @@
|
||||
// Imports
|
||||
use {
|
||||
super::Inner,
|
||||
crate::util::{extse::CrossBeamChannelReceiverSE, MightDeadlock, WithSideEffect},
|
||||
crate::util::{extse::CrossBeamChannelReceiverSE, MightDeadlock},
|
||||
parking_lot::Mutex,
|
||||
std::{path::PathBuf, sync::Arc},
|
||||
zsw_side_effect_macros::side_effect,
|
||||
};
|
||||
|
||||
/// A receiver
|
||||
@ -23,8 +24,10 @@ impl Receiver {
|
||||
///
|
||||
/// # Deadlock
|
||||
/// Deadlocks if the path distributer deadlocks in [`Distributer::run`](super::Distributer::run)
|
||||
pub fn recv(&self) -> WithSideEffect<Result<Arc<PathBuf>, DistributerQuitError>, MightDeadlock> {
|
||||
self.rx.recv_se().map(|res| res.map_err(|_| DistributerQuitError))
|
||||
#[side_effect(MightDeadlock)]
|
||||
pub fn recv(&self) ->Result<Arc<PathBuf>, DistributerQuitError> {
|
||||
// DEADLOCK: Caller ensures we don't deadlock
|
||||
self.rx.recv_se().allow::<MightDeadlock>().map_err(|_| DistributerQuitError)
|
||||
}
|
||||
|
||||
/// Removes a path
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user