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 });