How to inject code based on a manifest setting?

Viewed 74

I apologize if I'm not asking this question properly. I feel like part of the issue is that I'm having hard time trying to idiomatically describe what I'm trying to do.

I am currently trying to figure out if I can register handler functions defined in lib.rs into a service, based only on reading a YAML manifest file.

I have a manifest file config.yaml

event: count
handler: count_handler

I have a lib.rs with the following definitions:

// This is somewhat pseudo-code (has not been tested, but gives you the idea)
fn count_handler(d: Vec<u8>) -> Vec<u8> {
    println!("count_handler was called!");
    Vec::new()
}

type HandlerFn = fn(Vec<u8>) -> Vec<u8>;

struct Service {
  handlers: HashMap<String, HandlerFn>,
}

impl Service {
   fn new() -> Self {
     let handlers: HashMap::new();
     Self { handlers }
   }

   fn register_handler(&mut self, name: String, handler: HandlerFn) {
       self.handlers.insert(name, handler);
   }

   fn start() {
       println!("I'm doing some things with these handlers: {:?}", self.handlers);
   }
}

What I'm trying to figure out is:

  • Is there any way to automagically register my the count_handler in the Service using only the manifest file?
    • Update: There seems to be no way to do this using only the manifest file, so let me clarify further
  • Is there any way to do this using some type of macro? (proc macro?)
  • I basically need whatever mapping is generated to happen without the user manually registering the handlers.

Ideally, I would be able to do the following:

fn main() {
  let manifest: Manifest = Manifest::from_file("config.yaml");
  // assert_eq!(manifest.event, "count".to_owned());
  // assert_eq!(manifest.handler, "count_handler".to_owned());

  let service = Service::new();
  service.start();
  // "I'm doing some things with these handlers: HashMap{ "count": Function(count_handler) }"
}

Some constrants and clarifications:

  • Assume the handler defined in the manifest, is always present in lib.rs
  • The count_handler could be any function that satisfies HandlerFn
  • The handler in the manifest, will always be indexed in the Service by the event
    • In this case, <"count", count_handler()> is registered in the Service

I don't think this is possible in Rust, but just wanted to get some clarity.

1 Answers

If you want to handle this at runtime, that is, not re-compile your application when you change your YAML file, then the answer is: no, it's no possible (without further effort). This is because Rust has no concept of runtime reflection, and as a result the name of your function is erased at compile time. There's simply no way to look it up.

As user EvilTak points out in the comments, you could build a supporting structure such that you end up with some sort of map from your handler function name (e.g. count_handler) to the reference to the function you can invoke at runtime, so e.g. HashMap<String, fn(Vec<u8>) -> Vec<u8>>.

However, this does not seem to meet your criteria of "using only the manifest file".


That being said, if you are okay with re-compiling your application every time you change the YAML file, then this is very much possible. The idea would then be to read your YAML file during compilation, in your macro, and generate the code that you would write yourself if you were manually wiring things up.

Related