How to make plugin like API for renderers in mdBook?


I have been thinking of rewriting mdBook for a while now and I have a pretty good idea of how I want it to look like from the users point of view, but I am less certain on how I could implement it.

I would like to support multiple renderers that can be enabled / disabled to be able to render to html, pdf, epub, … so in the configuration file you would be able to put:


html = { destination = "book/" }
pdf = { destination = "pdf/mdBook.pdf" }

This should create two renderers that will be run when the book is build.

Ideally I would like to support multiple renderers of the same type with different configurations.

html = { destination = "book/" }
html2 = { renderer = "html", destination = "book2/" }

So in this case 2 renderers of type html will be created with names html and html2 to be able to differentiate the two.

Finally I don’t want to exclude the possibility of providing a way to add third-party-renderers as plug-ins, but this seems particularly tricky at the moment as I heard plugins should only use the C abi because Rust is not stable for that purpose?

Now the problem is, I have absolutely no clue how to implement this more or less efficiently in my code… I have experimented with the idea of having a Renderer trait and having all renderers implement that. I have to register all renderers before I can create instances of them to use. For now the way I have it is that the Renderer trait has a fn identifier(&self) -> &'static str method. Registering is just making an instance and when I want to create a new renderer I iterate over the registered renderers and clone the one with matching identifier.

I am interested to hear how others would solve this problem, or if there is a better design for this. :slight_smile:


Hm, and can the renderer be just a separate process?


It could I guess, but I would like to avoid that if there is a way to do it in the same process :wink:
I would have to feed it with the content and configuration options and for that purpose I think it would be easier if I could just call the appropriate functions and pass a reference to the data.

… and I don’t have any experience with inter-process communication