sails_idl_gen/
lib.rs

1pub use errors::*;
2use handlebars::{Handlebars, handlebars_helper};
3use meta::ExpandedProgramMeta;
4pub use program::*;
5use scale_info::{Field, PortableType, Variant, form::PortableForm};
6use serde::Serialize;
7use std::{fs, io::Write, path::Path};
8
9mod errors;
10mod meta;
11mod type_names;
12
13const IDL_TEMPLATE: &str = include_str!("../hbs/idl.hbs");
14const COMPOSITE_TEMPLATE: &str = include_str!("../hbs/composite.hbs");
15const VARIANT_TEMPLATE: &str = include_str!("../hbs/variant.hbs");
16
17pub mod program {
18    use super::*;
19    use sails_idl_meta::ProgramMeta;
20
21    pub fn generate_idl<P: ProgramMeta>(idl_writer: impl Write) -> Result<()> {
22        render_idl(
23            &ExpandedProgramMeta::new(Some(P::constructors()), P::services())?,
24            idl_writer,
25        )
26    }
27
28    pub fn generate_idl_to_file<P: ProgramMeta>(path: impl AsRef<Path>) -> Result<()> {
29        let mut idl_new_content = Vec::new();
30        generate_idl::<P>(&mut idl_new_content)?;
31        if let Ok(idl_old_content) = fs::read(&path)
32            && idl_new_content == idl_old_content
33        {
34            return Ok(());
35        }
36        if let Some(dir_path) = path.as_ref().parent() {
37            fs::create_dir_all(dir_path)?;
38        }
39        Ok(fs::write(&path, idl_new_content)?)
40    }
41}
42
43pub mod service {
44    use super::*;
45    use sails_idl_meta::{AnyServiceMeta, ServiceMeta};
46
47    pub fn generate_idl<S: ServiceMeta>(idl_writer: impl Write) -> Result<()> {
48        render_idl(
49            &ExpandedProgramMeta::new(None, vec![("", AnyServiceMeta::new::<S>())].into_iter())?,
50            idl_writer,
51        )
52    }
53
54    pub fn generate_idl_to_file<S: ServiceMeta>(path: impl AsRef<Path>) -> Result<()> {
55        let mut idl_new_content = Vec::new();
56        generate_idl::<S>(&mut idl_new_content)?;
57        if let Ok(idl_old_content) = fs::read(&path)
58            && idl_new_content == idl_old_content
59        {
60            return Ok(());
61        }
62        Ok(fs::write(&path, idl_new_content)?)
63    }
64}
65
66fn render_idl(program_meta: &ExpandedProgramMeta, idl_writer: impl Write) -> Result<()> {
67    let program_idl_data = ProgramIdlData {
68        type_names: program_meta.type_names()?.collect(),
69        types: program_meta.types().collect(),
70        ctors: program_meta.ctors().collect(),
71        services: program_meta
72            .services()
73            .map(|s| ServiceIdlData {
74                name: s.name(),
75                commands: s.commands().collect(),
76                queries: s.queries().collect(),
77                events: s.events().collect(),
78            })
79            .collect(),
80    };
81
82    let mut handlebars = Handlebars::new();
83    handlebars
84        .register_template_string("idl", IDL_TEMPLATE)
85        .map_err(Box::new)?;
86    handlebars
87        .register_template_string("composite", COMPOSITE_TEMPLATE)
88        .map_err(Box::new)?;
89    handlebars
90        .register_template_string("variant", VARIANT_TEMPLATE)
91        .map_err(Box::new)?;
92    handlebars.register_helper("deref", Box::new(deref));
93
94    handlebars
95        .render_to_write("idl", &program_idl_data, idl_writer)
96        .map_err(Box::new)?;
97
98    Ok(())
99}
100
101type CtorIdlData<'a> = (&'a str, &'a Vec<Field<PortableForm>>, &'a Vec<String>);
102type FuncIdlData<'a> = (&'a str, &'a Vec<Field<PortableForm>>, u32, &'a Vec<String>);
103
104#[derive(Serialize)]
105struct ProgramIdlData<'a> {
106    type_names: Vec<String>,
107    types: Vec<&'a PortableType>,
108    ctors: Vec<CtorIdlData<'a>>,
109    services: Vec<ServiceIdlData<'a>>,
110}
111
112#[derive(Serialize)]
113struct ServiceIdlData<'a> {
114    name: &'a str,
115    commands: Vec<FuncIdlData<'a>>,
116    queries: Vec<FuncIdlData<'a>>,
117    events: Vec<&'a Variant<PortableForm>>,
118}
119
120handlebars_helper!(deref: |v: String| { v });