Aleph-Alpha/ts-rs

Feature request: using inventory to build custom bindings

Opened this issue · 0 comments

Is your feature request related to a problem? Please describe.
sometimes, I want to customize the way this crate generates bindings.

Describe alternatives you've considered
One of the alternatives is CLI issue #304, and I like it, but this is more versatile and is easier to implement.

Note
if this is something you would like to merge I can start working on it

Describe the solution you'd like
This feature relies on the inventory crate. The idea is that the derive macro submit inventory item describes how a given type can be converted to typescript binding; then, I can add a custom executable in the bin folder that collects these inventory items and generate these binding or run arbitrary code.

This is what I will add to the ts-rs crate:

pub trait DynTS { ... }

// this trait closely mimics TS trait but allows for dynamic behavior
impl<T> DynTS for PhantomData<T> where T: TS { ... }

pub struct Submittable {
    init: fn () -> Box<dyn DynTS>
}

inventory::collect!{ Submittable }

This is what derive macro will generate:

use ts_rs::*;

#[derive(TS)] 
struct MyType { ... }

impl ts_rs::TS for MyType { ... }

inventory::submit! { Submittable {
    init: || Box::new(PhantomData::<MyType>)
}}

Here is an example of bin/generate_ts.rs:

fn strategy_1() { 
    // Create the `binding` folder if it does not exist 
    let path = Path::new("binding"); 
    if !path.exists() { 
        fs::create_dir(path).expect("Failed to create `binding` directory"); } 
        // Iterate through all `Submitable` items 
        for submit in inventory::iter::<Submittable> { 
            // Initialize the DynTS object 
            let dyn_ts: Box<dyn DynTS> = (submit.init)(); 

            // Generate TypeScript binding 
            let ts_binding = dyn_ts.declare(); 

            // Determine file name 
            let type_name = ts_binding.split_whitespace().next().unwrap_or("unknown"); 
            
            // Extract type name 
            let file_name = format!("binding/type_{}.ts", type_name); 

            // Write the TS binding to the file 
            let mut file = File::create(&file_name).expect("Failed to create file"); 
            writeln!(file, "{}", ts_binding).expect("Failed to write to file"); 
        } 
}
fn main() {
    strategy_1()
}

The good thing here is that you can add this strategy to cli, or other people can copy it and customize it to their need.