Combining attribute macros in impl

I'm trying to write a derive macro to implement the manager pattern in tokio (https://tokio.rs/tokio/tutorial/shared-state#strategies).

I'm envisioning something like this:

#[derive(Handle)]
pub struct MyData {
    x: isize
}

impl MyData {
    #[handle]
    fn increment(&mut self) {
        self.x += 1;
    }
    #[handle]
    fn decrement(&mut self) {
        self.x -= 1;
    }
    #[handle]
    fn value(&self) -> isize {
        self.x
    }
    fn do_something_private(&mut self) {
        self.x = self.x * self.x;
    }
}

that expands to something like this (barring any compilation errors... i haven't actually tried to compile this):

pub struct MyData {
    x: isize
}

pub enum MyDataMessage {
    Increment,
    Decrement,
    Value(tokio::sync::oneshot::Sender<isize>),
}

#[derive(Clone)]
pub struct MyDataHandle {
    tx: mpsc::Sender<MyDataMessage>
}

impl MyDataHandle {
    pub async fn increment(&mut self) {
        self.tx.send(MyDataMessage::Increment).await;
    }
    pub async fn decrement(&mut self) {
        self.tx.send(MyDataMessage::Increment).await;
    }
    pub async fn value(&mut self) -> Result<isize, Box<dyn std::error::Error>> {
        let (tx, mut rx) = oneshot::channel();
        self.tx.send(MyDataMessage::Value(tx)).await;

        rx.await
    }
}

impl MyData {
    fn increment(&mut self) {
        self.x += 1;
    }
    fn decrement(&mut self) {
        self.x -= 1;
    }
    fn value(&self) -> isize {
        self.x
    }
    fn do_something_private(&mut self) {
        self.x = self.x * self.x;
    }
}

To break this down, I need to:

  1. Generate a {name}Handle type from a derive -- OK
  2. Build an enum based on attribute macros on functions in an impl -- no idea
  3. Create methods on a generated type based on attribute macros -- no idea

How can I "collect" the items the attribute macro is used on to generate both the enum variants and methods on the Handle struct?

It isn't currently possible to access other parts of the code other than what is directly passed to the macro invocation.

I think you could use a function-like macro that gets passed all of that info in a single call like

derive_handle! {
pub struct MyData {
    x: isize
}

impl MyData {
    #[handle]
    fn increment(&mut self) {
        self.x += 1;
    }
    #[handle]
    fn decrement(&mut self) {
        self.x -= 1;
    }
    #[handle]
    fn value(&self) -> isize {
        self.x
    }
    fn do_something_private(&mut self) {
        self.x = self.x * self.x;
    }
}
}

This seems like it would work but might turn into a very complex macro. How would I handle the attribute macros inside the function-like macro?

I could fall back to a solution that uses manually specified attributes on the derive macro. That might be simplest, even if not the most ergonomic.

They wouldn't be macros, you'd just remove them before you output the final token stream

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.