Skip to content

Commit

Permalink
replace proc macros with matching macros use macro_metavar_expr feature
Browse files Browse the repository at this point in the history
  • Loading branch information
cartercanedy committed Jul 19, 2024
1 parent 3e9e373 commit ec89736
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 51 deletions.
10 changes: 1 addition & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
[package]
name = "zips"
description = "macro for zipping more than just 2 instances of Option<T>"
version = "0.1.1"
version = "0.1.2"
edition = "2021"
author = "Carter Canedy"
email = "[email protected]"
repository = "https://github.com/cartercanedy/zips"
license = "MIT"
license-file="LICENSE.txt"

[lib]
proc-macro = true

[dependencies]
syn = { version = "2.0.71", default-features = false, features = ["parsing", "derive", "proc-macro", "printing"] }
quote = "1.0.36"
proc-macro2 = "1.0.86"
97 changes: 55 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,68 @@
// Copyright (c) 2024 Carter Canedy <[email protected]>

use {
proc_macro::TokenStream,
syn::{parse_macro_input, Expr, Token},
syn::parse::{Parse, ParseStream},
syn::punctuated::Punctuated,
quote::quote,
};
#![feature(macro_metavar_expr)]

struct ZipArgs {
args: Punctuated<Expr, Token![,]>
}
/// Zips multiple instances of Option<T> into a single instance of Option<(T [, T...])>. Will
/// either be Some if all arguments are some, or None if one of the instances is None.
///
/// ## Usage:
/// ```
/// let zipped_some = zip!(Some(1), Some(2));
/// assert_eq!(zipped_some, Some((1, 2)));
///
/// let zipped_none = zip!(Some(1), None);
/// assert_eq!(zipped_none, None);
/// ```
#[macro_export]
macro_rules! zip {
($($args:expr),+) => ({
let mut ok = true;
$(
let arg${index()} = $args;
if arg${index()}.is_none() {
ok = false;
}
)+

impl Parse for ZipArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
args: Punctuated::<Expr, Token![,]>::parse_terminated(input)?
})
}
if ok {
Some(($(arg${index()}.unwrap(),)+))
} else {
None
}
});
}

/// Expands into a single `Option<(T [, T...])>::Some((...))` instance if all arguments
/// are instances of `Option<T>::Some(T)`, else expands to `Option<(T [, T...])>::None`
/// Zips an arbitrary number of `Result<T>` into a single instance of `Option<(T [, T...])>`. If
/// any arguments are `Err`, `None` is returned. Otherwise, `Some` is returned.
///
/// Usage:
/// ## Usage:
/// ```
/// use zips::zip;
/// let i: Result<i32, String> = Ok(1);
/// let j: Result<usize, String> = Ok(0usize);
/// let k: Result<usize, String> = Err("I'm an error");
///
/// // zipped_ok: Option<(i32, usize)>
/// let zipped_ok = zip_result!(i, j);
/// assert_eq!(zipped_ok, Some(1, 0usize));
///
/// fn main() -> () {
/// let zipped = zip!(Some(0), Some(1));
/// assert_eq!(zipped, Some(0, 1));
/// }
/// // zipped_err: Option<(i32, usize, usize)>
/// let zipped_err = zip_result!(i, j, k);
/// assert_eq!(zipped_err, None);
/// ```
#[proc_macro]
pub fn zip(input: TokenStream) -> TokenStream {
let ZipArgs { args } = parse_macro_input!(input as ZipArgs);
let arg_names: Vec<_> =
(0..args.len())
.map(|i| syn::Ident::new(&format!("arg{i}"), proc_macro2::Span::call_site()))
.collect();

let args = args.into_iter();

quote! {
{
#(let #arg_names = #args;)*
if #(#arg_names.is_some() &&)* true {
Some((#(#arg_names.unwrap()),*))
} else {
None
#[macro_export]
macro_rules! zip_result {
($($args:expr),+) => ({
let mut ok = true;
$(
if $args.is_err() {
ok = false;
}
)+

if ok {
Some(($($args.ok().unwrap(),)+))
} else {
None
}
}.into()
});
}

0 comments on commit ec89736

Please sign in to comment.