munge_macro-0.4.1/.cargo_vcs_info.json0000644000000001510000000000100133270ustar { "git": { "sha1": "aea7ab19a38fc2627f1bc6a2a071b0d40c70810e" }, "path_in_vcs": "munge_macro" }munge_macro-0.4.1/Cargo.toml0000644000000024240000000000100113320ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "munge_macro" version = "0.4.1" authors = ["David Koloski "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Macro for custom destructuring" documentation = "https://docs.rs/munge_macro" readme = "README.md" keywords = [ "macro", "no_std", ] categories = [ "no-std", "no-std::no-alloc", "rust-patterns", ] license = "MIT" repository = "https://github.com/djkoloski/munge" [lib] name = "munge_macro" path = "src/lib.rs" proc-macro = true [dependencies.proc-macro2] version = "1" default-features = false [dependencies.quote] version = "1" default-features = false [dependencies.syn] version = "2" features = [ "full", "parsing", "printing", "proc-macro", ] default-features = false munge_macro-0.4.1/Cargo.toml.orig000064400000000000000000000011360072674642500150420ustar 00000000000000[package] name = "munge_macro" description = "Macro for custom destructuring" version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true readme.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true documentation = "https://docs.rs/munge_macro" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] proc-macro2.workspace = true quote.workspace = true syn = { workspace = true, features = ["full", "parsing", "printing", "proc-macro"] } munge_macro-0.4.1/LICENSE000064400000000000000000000020440072674642500131570ustar 00000000000000Copyright 2024 David Koloski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. munge_macro-0.4.1/README.md000064400000000000000000000070010072674642500134270ustar 00000000000000# `munge` [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] [crates.io badge]: https://img.shields.io/crates/v/munge.svg [crates.io]: https://crates.io/crates/munge [docs badge]: https://img.shields.io/docsrs/munge [docs]: https://docs.rs/munge [license badge]: https://img.shields.io/badge/license-MIT-blue.svg [license]: https://github.com/rkyv/munge/blob/master/LICENSE Munge makes it easy and safe to destructure `MaybeUninit`s, `Cell`s, `UnsafeCell`s, `ManuallyDrop`s, and more. ## Documentation - [munge](https://docs.rs/munge), the core library - [munge_macro](https://docs.rs/munge_macro), the proc macro at the core of munge. ## Examples Initialize `MaybeUninit`s: ```rust use core::mem::MaybeUninit; use munge::munge; pub struct Example { a: u32, b: (char, f32), } let mut mu = MaybeUninit::::uninit(); munge!(let Example { a, b: (c, mut f) } = &mut mu); assert_eq!(a.write(10), &10); assert_eq!(c.write('x'), &'x'); assert_eq!(f.write(3.14), &3.14); // Note that `mut` bindings can be reassigned like you'd expect: f = &mut MaybeUninit::uninit(); // SAFETY: `mu` is completely initialized. let init = unsafe { mu.assume_init() }; assert_eq!(init.a, 10); assert_eq!(init.b.0, 'x'); assert_eq!(init.b.1, 3.14); ``` Destructure `Cell`s: ```rust use core::cell::Cell; use munge::munge; pub struct Example { a: u32, b: (char, f32), } let value = Example { a: 10, b: ('x', 3.14), }; let cell = Cell::::new(value); munge!(let Example { a, b: (c, f) } = &cell); assert_eq!(a.get(), 10); a.set(42); assert_eq!(c.get(), 'x'); c.set('!'); assert_eq!(f.get(), 3.14); f.set(1.41); let value = cell.into_inner(); assert_eq!(value.a, 42); assert_eq!(value.b.0, '!'); assert_eq!(value.b.1, 1.41); ``` You can even extend munge to work with your own types by implementing its `Destructure` and `Restructure` traits: ```rust use munge::{Destructure, Restructure, Move, munge}; pub struct Invariant(T); impl Invariant { /// # Safety /// /// `value` must uphold my custom invariant. pub unsafe fn new_unchecked(value: T) -> Self { Self(value) } pub fn unwrap(self) -> T { self.0 } } // SAFETY: // - `Invariant` is destructured by move, so its `Destructuring` type is // `Move`. // - `underlying` returns a pointer to its inner type, so it is guaranteed // to be non-null, properly aligned, and valid for reads. unsafe impl Destructure for Invariant { type Underlying = T; type Destructuring = Move; fn underlying(&mut self) -> *mut Self::Underlying { &mut self.0 as *mut Self::Underlying } } // SAFETY: `restructure` returns an `Invariant` that takes ownership of // the restructured field because `Invariant` is destructured by move. unsafe impl Restructure for Invariant { type Restructured = Invariant; unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured { // SAFETY: The caller has guaranteed that `ptr` is a pointer to a // subfield of some `T`, so it must be properly aligned, valid for // reads, and initialized. We may move the fields because the // destructuring type for `Invariant` is `Move`. let value = unsafe { ptr.read() }; Invariant(value) } } // SAFETY: `(1, 2, 3)` upholds my custom invariant. let value = unsafe { Invariant::new_unchecked((1, 2, 3)) }; munge!(let (one, two, three) = value); assert_eq!(one.unwrap(), 1); assert_eq!(two.unwrap(), 2); assert_eq!(three.unwrap(), 3); ``` munge_macro-0.4.1/src/lib.rs000064400000000000000000000201610072674642500140550ustar 00000000000000//! The proc macro at the core of munge. #![deny( missing_docs, unsafe_op_in_unsafe_fn, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks, rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs )] use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::{ parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{Eq, FatArrow, Let, Semi}, Error, Expr, Index, Pat, PatRest, PatTuple, PatTupleStruct, Path, }; /// Destructures a value by projecting pointers. #[proc_macro] pub fn munge_with_path( input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let input = parse_macro_input!(input as Input); destructure(input) .unwrap_or_else(|e| e.to_compile_error()) .into() } struct Input { crate_path: Path, _arrow: FatArrow, destructures: Punctuated, } impl parse::Parse for Input { fn parse(input: parse::ParseStream) -> parse::Result { Ok(Input { crate_path: input.parse::()?, _arrow: input.parse::()?, destructures: input.parse_terminated(Destructure::parse, Semi)?, }) } } struct Destructure { _let_token: Let, pat: Pat, _eq_token: Eq, expr: Expr, } impl parse::Parse for Destructure { fn parse(input: parse::ParseStream) -> parse::Result { Ok(Destructure { _let_token: input.parse::()?, pat: Pat::parse_single(input)?, _eq_token: input.parse::()?, expr: input.parse::()?, }) } } fn rest_check(crate_path: &Path, rest: &PatRest) -> (TokenStream, TokenStream) { let span = rest.dot2_token.span(); let destructurer = quote! { destructurer }; let expr = quote_spanned! { span => { let phantom = #crate_path::__macro::get_destructure(&#destructurer); #crate_path::__macro::only_borrow_destructuring_may_use_rest_patterns( phantom ) } }; (quote_spanned! { span => _ }, expr) } fn parse_pat( crate_path: &Path, pat: &Pat, ) -> Result<(TokenStream, TokenStream), Error> { Ok(match pat { Pat::Ident(pat_ident) => { let mutability = &pat_ident.mutability; let ident = &pat_ident.ident; if let Some(r#ref) = &pat_ident.by_ref { return Err(Error::new_spanned( r#ref, "`ref` is not allowed in munge destructures", )); } if let Some((at, _)) = &pat_ident.subpat { return Err(Error::new_spanned( at, "subpatterns are not allowed in munge destructures", )); } ( quote! { #mutability #ident }, quote! { // SAFETY: `ptr` is a properly-aligned pointer to a subfield // of the pointer underlying `destructurer`. unsafe { #crate_path::__macro::restructure_destructurer( &destructurer, ptr, ) } }, ) } Pat::Tuple(PatTuple { elems, .. }) | Pat::TupleStruct(PatTupleStruct { elems, .. }) => { let parsed = elems .iter() .map(|e| parse_pat(crate_path, e)) .collect::, Error>>()?; let (bindings, (exprs, indices)) = parsed .iter() .enumerate() .map(|(i, x)| (&x.0, (&x.1, Index::from(i)))) .unzip::<_, _, Vec<_>, (Vec<_>, Vec<_>)>(); ( quote! { (#(#bindings,)*) }, quote! { ( #({ // SAFETY: `ptr` is guaranteed to always be non-null, // properly-aligned, and valid for reads. let ptr = unsafe { ::core::ptr::addr_of_mut!((*ptr).#indices) }; #exprs },)* ) }, ) } Pat::Slice(pat_slice) => { let parsed = pat_slice .elems .iter() .map(|e| parse_pat(crate_path, e)) .collect::, Error>>()?; let (bindings, (exprs, indices)) = parsed .iter() .enumerate() .map(|(i, x)| (&x.0, (&x.1, Index::from(i)))) .unzip::<_, _, Vec<_>, (Vec<_>, Vec<_>)>(); ( quote! { (#(#bindings,)*) }, quote! { ( #({ // SAFETY: `ptr` is guaranteed to always be non-null, // properly-aligned, and valid for reads. let ptr = unsafe { ::core::ptr::addr_of_mut!((*ptr)[#indices]) }; #exprs },)* ) }, ) } Pat::Struct(pat_struct) => { let parsed = pat_struct .fields .iter() .map(|fp| { parse_pat(crate_path, &fp.pat).map(|ie| (&fp.member, ie)) }) .collect::, Error>>()?; let (members, (bindings, exprs)) = parsed.into_iter().unzip::<_, _, Vec<_>, (Vec<_>, Vec<_>)>(); let (rest_binding, rest_expr) = if let Some(rest) = &pat_struct.rest { let (binding, expr) = rest_check(crate_path, rest); (Some(binding), Some(expr)) } else { (None, None) }; ( quote! { ( #(#bindings,)* #rest_binding ) }, quote! { ( #({ // SAFETY: `ptr` is guaranteed to always be non-null, // properly-aligned, and valid for reads. let ptr = unsafe { ::core::ptr::addr_of_mut!((*ptr).#members) }; #exprs },)* #rest_expr ) }, ) } Pat::Rest(pat_rest) => rest_check(crate_path, pat_rest), Pat::Wild(pat_wild) => { let token = &pat_wild.underscore_token; (quote! { #token }, quote! {}) } _ => { return Err(Error::new_spanned( pat, "expected a destructuring pattern", )) } }) } fn destructure(input: Input) -> Result { let crate_path = &input.crate_path; let mut result = TokenStream::new(); for destructure in input.destructures.iter() { let pat = &destructure.pat; let expr = &destructure.expr; let (bindings, exprs) = parse_pat(crate_path, pat)?; result.extend(quote! { let mut destructurer = #crate_path::__macro::make_destructurer( #expr ); let #bindings = { #[allow( unused_mut, unused_unsafe, clippy::undocumented_unsafe_blocks, )] { let ptr = #crate_path::__macro::destructurer_ptr( &mut destructurer ); #[allow(unreachable_code, unused_variables)] if false { // SAFETY: This can never be called. unsafe { ::core::hint::unreachable_unchecked(); let #pat = #crate_path::__macro::test_destructurer( &mut destructurer, ); } } #exprs } }; }); } Ok(result) }