#enums #proc-macro #match #variant #arms #procedural #copy-pastes

macro all-the-same

A procedural macro that copy-pastes match arms for new type variant enums

2 stable releases

1.1.0 Feb 6, 2024
1.0.0 Feb 6, 2024

#1034 in Procedural macros

Download history 174/week @ 2024-07-29 124/week @ 2024-08-05 50/week @ 2024-08-12 147/week @ 2024-08-19 232/week @ 2024-08-26 234/week @ 2024-09-02 353/week @ 2024-09-09 196/week @ 2024-09-16 208/week @ 2024-09-23 102/week @ 2024-09-30 290/week @ 2024-10-07 160/week @ 2024-10-14 12/week @ 2024-10-21 104/week @ 2024-10-28 173/week @ 2024-11-04

450 downloads per month

BSD-3-Clause

11KB
85 lines

All the same!

If you ever had code that looks like this:

use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncWrite;
use tokio::net::{TcpStream, UnixStream};

enum Stream {
    Tcp(TcpStream),
    Unix(UnixStream),
    Custom(Box<dyn AsyncWrite + Unpin + 'static>),
}

impl AsyncWrite for Stream {
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<Result<usize, io::Error>> {
        match self.get_mut() {
            Stream::Tcp(s) => Pin::new(s).poll_write(cx, buf),
            Stream::Unix(s) => Pin::new(s).poll_write(cx, buf),
            Stream::Custom(s) => Pin::new(s).poll_write(cx, buf),
        }
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), io::Error>> {
        match self.get_mut() {
            Stream::Tcp(s) => Pin::new(s).poll_shutdown(cx),
            Stream::Unix(s) => Pin::new(s).poll_shutdown(cx),
            Stream::Custom(s) => Pin::new(s).poll_shutdown(cx),
        }
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
        match self.get_mut() {
            Stream::Tcp(s) => Pin::new(s).poll_flush(cx),
            Stream::Unix(s) => Pin::new(s).poll_flush(cx),
            Stream::Custom(s) => Pin::new(s).poll_flush(cx),
        }
    }
}

with the help of the macro you can now replace it with:

use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::AsyncWrite;
use tokio::net::{TcpStream, UnixStream};
use all_the_same::all_the_same;

enum Stream {
    Tcp(TcpStream),
    Unix(UnixStream),
    Custom(Box<dyn AsyncWrite + Unpin + 'static>),
}

impl AsyncWrite for Stream {
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<Result<usize, io::Error>> {
        all_the_same!(match self.get_mut() {
            Stream::[Tcp, Unix, Custom](s) => Pin::new(s).poll_write(cx, buf)
        })
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), io::Error>> {
        all_the_same!(match self.get_mut() {
            Stream::[Tcp, Unix, Custom](s) => Pin::new(s).poll_shutdown(cx)
        })
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
        all_the_same!(match self.get_mut() {
            Stream::[Tcp, Unix, Custom](s) => Pin::new(s).poll_flush(cx)
        })
    }
}

Feature gated enum variants, etc.

Btw, you can add attributes that will be applied to the match arms, to deal with feature-gated enum variants:

use all_the_same::all_the_same;

enum Variants {
    Foo(String),

    #[cfg(test)]
    Bar(String)
}

impl Variants {
    pub fn value(&self) -> &str {
        all_the_same!(match self {
            Variants::[Foo, #[cfg(test)]Bar](v) => v
        })
    }
}

Dependencies

~1.5MB
~37K SLoC