Beginner proc macro questions

I'm trying to wrap my head around some of the basic ways of thinking about proc macros.

This is what I envision (this is the application code):

#[derive(IfHandler)]
struct MyHandler {
}

impl MyHandler {
  #[handler(ordinal = 6)]
  fn notify(&self) {
    // Do things
  }
  #[handler(ordinal = 8)]
  fn abort(&self, id: u32) {
    // Do things
  }
}

The IfHandler derive should generate:

impl InterfaceHandler for MyHandler {
  fn do_work(ctx: Context, o: u32) {
    match o {
      6 => {
        self.notify();
      }
      8 => {
        let id = ctx.get_id();
        self.abort(id);
      }
      _ => {
      }
    }
  }
}

The problem, to my ignorant brain, is that the #[handler()]'s in impl MyHandler are used to generate the match and calls in impl InterfaceHandler for MyHandler, which means there's a dependency requirement and I don't see any means to "communicate" between the two proc macros (the derive and the handlers).

How should I be thinking here? Is there a way to guarantee that the handler macros are processed before the derive (so I can generate the list) and pass it to the derive macro in some way, or is my mental model completely wrong?

I can't help shake the feeling that philosophically one should treat each proc macro as completely disjoint from others, but I also can't help think that it's extremely useful to have dependency orders and shared data between them.

Indeed. This is by design.

You could do something like

struct MyHandler {
}

#[interface_handler]
impl MyHandler {
  #[handler(ordinal = 6)]
  fn notify(&self) {
    // Do things
  }
  #[handler(ordinal = 8)]
  fn abort(&self, id: u32) {
    // Do things
  }
}

and then have #[interface_handler] generate the impl InterfaceHandler for MyHandler based on #[handler] inert attributes in addition to emitting the impl MyHandler unchanged.

2 Likes