I have a plugin trait that includes some heavy types that would be almost impossible to wrap into a single API. It looks like this:

pub struct PluginContext<'a> {
    pub query: &'a mut String,
    pub gl_window: &'a GlutinWindowContext,
    flow: PluginFlowControl,
    pub egui_ctx: &'a Context,
    disable_cursor: bool,
    error: Option<String>,
}
pub trait Plugin {
    fn configure(&mut self, builder: ConfigBuilder) -> Result<ConfigBuilder, ConfigError> {
        Ok(builder)
    }
    fn search(&mut self, ui: &mut Ui, ctx: &mut PluginContext<'_>);
    fn before_search(&mut self, _ctx: &mut PluginContext<'_>) {}
}

Here is what I considered:

  1. Keeping all plugins in-repo. This is what I do now, however I’d like to make a plugin that would just pollute the repository. So I need another option that would keep the plugins’ freedom as it is right now, but with the possibility to move the plugin out to a separate repository.
  2. I tried to look into dynamic loading, and since rust doesn’t have a stable ABI, I’m okay with restricting the rust versions for the plugin ecosystem. However, I don’t think it’s possible to compile this complex API into a dynamic lib and load it safely.
  3. I’m also ok with recompiling the app every time I need a new plugin, but I would like to load these plugins automatically, so I don’t want to change the code every time I need a new plugin. For example, I imagine loading all plugins from a folder. Unfortunately, I didn’t find an easy solution for this neither. I think I will write a build macro that checks the ~/.config/myapp/plugins and include all of them into the repo.

Do you have any better ideas, suggestions? Thanks in advance.

(For context, this the app I’m writing about: https://github.com/fxdave/vonal-rust)

  • @orclev
    link
    English
    12 months ago

    I’m not sure exactly how to solve your problem, but one thing that occurs to me is that lifetimes and references are really a compile time semantic. If you’re dynamically loading something then you can’t assert lifetimes, at least not safely. So for point 2 I feel like you’d need to create an unsafe layer in between the app and dynamically loaded plugins and then wrap that in a safe API.

    For point 3 I suspect you’d need to look at the macro system. Some kind of like load_plugins!("~/.config/myapp/plugins") macro stuck in an appropriate place in the code that at compile time can check that folder and generate the appropriate glue code to register and use everything. One thing I’m not entirely sure about though is if you’d have permission to access folders outside of the project at compile time, there’s a chance the compiler would refuse to do so, but I don’t know enough about macros or the way they’re sandboxed (or not sandboxed) to say for sure.