telchar_codegen/
lib.rs

1//! This module provides functions to work with blueprints and schemas, including
2//! parsing JSON, reading from files, and generating templates using Handlebars.
3
4use std::fs;
5use serde_json;
6use heck::ToTitleCase;
7use schema::TypeName;
8use handlebars::Handlebars;
9
10pub mod blueprint;
11pub mod schema;
12pub mod template;
13
14pub struct Codegen<'a> {
15    handlebars: Handlebars<'a>,
16}
17
18impl Codegen<'_> {
19    pub fn new() -> Codegen<'static> {
20        Codegen {
21            handlebars: template::init_handlebars(),
22        }
23    }
24
25    fn get_schema_name(&self, key: String) -> String {
26        return key
27            .replace("#/definitions/", "")
28            .replace("~1", " ")
29            .replace("/", " ")
30            .replace("_", " ")
31            .replace("$", " ")
32            .to_title_case()
33            .replace(" ", "");
34    }
35
36    /// Parses a JSON string into a `Blueprint`.
37    ///
38    /// # Arguments
39    ///
40    /// * `json` - The JSON data from a `plutus.json` file
41    ///
42    /// # Returns
43    ///
44    /// A `Blueprint` instance.
45    pub fn get_blueprint_from_json(&self, json: String) -> blueprint::Blueprint {
46        serde_json::from_str(&json).expect("Unable to parse")
47    }
48
49    /// Reads a JSON file from a specified path and parses it into a `Blueprint`.
50    ///
51    /// # Arguments
52    ///
53    /// * `path` - The `plutus.json` file path in the filesystem
54    ///
55    /// # Returns
56    ///
57    /// A `Blueprint` instance.
58    pub fn get_blueprint_from_path(&self, path: String) -> blueprint::Blueprint {
59        let json = fs::read_to_string(path).expect("Unable to read file");
60        self.get_blueprint_from_json(json)
61    }
62
63    /// Obtains the list of schemas from a given `Blueprint`.
64    ///
65    /// # Arguments
66    ///
67    /// * `blueprint` - A `Blueprint` from which to obtain the schemas.
68    ///
69    /// # Returns
70    ///
71    /// A vector of `Schema` from the blueprint.
72    pub fn get_schemas_from_blueprint(&self, blueprint: blueprint::Blueprint) -> Vec<schema::Schema> {
73        let mut schemas: Vec<schema::Schema> = vec![];
74        if blueprint.definitions.is_some() {
75            for definition in blueprint.definitions.unwrap().inner.iter() {
76                let definition_name = self.get_schema_name(definition.0.clone());
77                let definition_json = serde_json::to_string(&definition.1).unwrap();
78                if definition.1.data_type.is_some() {
79                    match definition.1.data_type.unwrap() {
80                        blueprint::DataType::Integer => {
81                            schemas.push(schema::Schema::new_integer(definition_name.clone(), definition_json.clone()));
82                        }
83                        blueprint::DataType::Bytes => {
84                            schemas.push(schema::Schema::new_bytes(definition_name.clone(), definition_json.clone()));
85                        }
86                        blueprint::DataType::List => {
87                            if definition.1.items.is_some() {
88                                match definition.1.items.as_ref().unwrap() {
89                                    blueprint::ReferencesArray::Single(reference) => {
90                                        if reference.reference.is_some() {
91                                            schemas.push(schema::Schema::new_list(
92                                                definition_name.clone(),
93                                                schema::Reference{
94                                                    name: None,
95                                                    schema_name: self.get_schema_name(reference.reference.as_ref().unwrap().clone())
96                                                },
97                                                definition_json.clone()
98                                            ));
99                                        }
100                                    }
101                                    blueprint::ReferencesArray::Array(references) => {
102                                        let mut properties: Vec<schema::Reference> = vec![];
103                                        for reference in references {
104                                            if reference.reference.is_some() {
105                                                properties.push(schema::Reference{
106                                                    name: None,
107                                                    schema_name: self.get_schema_name(reference.reference.as_ref().unwrap().clone())
108                                                });
109                                            }
110                                        }
111                                        schemas.push(schema::Schema::new_tuple(definition_name.clone(), properties, definition_json.clone()));
112                                    }  
113                                }
114                            }
115                        }
116                        _ => {}
117                    }
118                }
119                if definition.1.title.is_some() {
120                    if definition.1.title.as_ref().unwrap() == "Data" && definition_name == "Data" {
121                        schemas.push(schema::Schema::new_anydata(definition_json.clone()));
122                    }
123                }
124                if definition.1.any_of.is_some() {
125                    let mut internal_schemas: Vec<schema::Schema> = vec![];
126                    for (index, parameter) in definition.1.any_of.as_ref().unwrap().iter().enumerate() {
127                        match parameter.data_type {
128                            blueprint::DataType::Constructor => {
129                                let schema_name = format!("{}{}", definition_name, parameter.title.clone().unwrap_or((index+1).to_string()));
130                                let mut properties: Vec<schema::Reference> = vec![];
131                                for property in &parameter.fields {
132                                    let mut schema_name = self.get_schema_name(property.reference.clone());
133                                    if schema_name == "Data" {
134                                        schema_name = "AnyData".to_string();
135                                    }
136                                    properties.push(schema::Reference{
137                                        name: property.title.clone(),
138                                        schema_name,
139                                    });
140                                }
141                                let schema: schema::Schema;
142                                if properties.len().gt(&0) || parameter.title.is_none() {
143                                    if properties.iter().any(|p| p.name.is_none()) {
144                                        schema = schema::Schema::new_tuple(schema_name, properties, definition_json.clone());
145                                    } else {
146                                        schema = schema::Schema::new_object(schema_name, properties, definition_json.clone());
147                                    }
148                                } else {
149                                    schema = schema::Schema::new_literal(schema_name, parameter.title.clone().unwrap(), definition_json.clone());
150                                }
151                                internal_schemas.push(schema);
152                            }
153                            _ => {}
154                        }
155                    }
156                    if internal_schemas.len().eq(&1) {
157                        let mut schema = internal_schemas.first().unwrap().clone();
158                        schema.name = definition_name.clone();
159                        schemas.push(schema);
160                    }
161                    if internal_schemas.len().gt(&1) {
162                        if internal_schemas.len().eq(&2)
163                        && internal_schemas.iter().any(|s| s.type_name.eq(&TypeName::Literal))
164                        && !internal_schemas.iter().all(|s| s.type_name.eq(&TypeName::Literal))
165                        {
166                            let reference = internal_schemas.iter().find(|s| s.type_name.ne(&TypeName::Literal));
167                            schemas.push(reference.unwrap().clone());
168                            schemas.push(schema::Schema::new_nullable(
169                                definition_name.clone(),
170                                reference.unwrap().name.clone(),
171                                definition_json.clone()
172                            ));
173                        } else {
174                            for schema in &internal_schemas {
175                                schemas.push(schema.clone());
176                            }
177                            schemas.push(schema::Schema::new_enum(definition_name.clone(), &internal_schemas, definition_json.clone()));
178                        }
179                    }
180                }
181            }
182        }
183        
184        schemas
185    }
186
187    /// Obtains the list of validators from a given `Blueprint`.
188    ///
189    /// # Arguments
190    ///
191    /// * `blueprint` - A `Blueprint` from which to obtain the validators.
192    ///
193    /// # Returns
194    ///
195    /// A vector of `Validator` from the blueprint.
196    pub fn get_validators_from_blueprint(&self, blueprint: blueprint::Blueprint) -> Vec<schema::Validator> {
197        let mut validators: Vec<schema::Validator> = vec![];
198        for validator in blueprint.validators.iter() {
199            let mut datum: Option<schema::Reference> = None;
200            if validator.datum.is_some() && validator.datum.as_ref().unwrap().schema.reference.is_some() {
201                datum = Some(schema::Reference {
202                    name: validator.datum.as_ref().unwrap().title.clone(),
203                    schema_name: self.get_schema_name(validator.datum.as_ref().unwrap().schema.reference.as_ref().unwrap().clone()),
204                });
205            }
206            let mut redeemer: Option<schema::Reference> = None;
207            if validator.redeemer.is_some() && validator.redeemer.as_ref().unwrap().schema.reference.is_some() {
208                redeemer = Some(schema::Reference {
209                    name: validator.redeemer.as_ref().unwrap().title.clone(),
210                    schema_name: self.get_schema_name(validator.redeemer.as_ref().unwrap().schema.reference.as_ref().unwrap().clone()),
211                });
212            }
213            let mut parameters: Vec<schema::Reference> = vec![];
214            if let Some(p) = &validator.parameters {
215                for parameter in p {
216                    if parameter.schema.reference.is_some() {
217                        parameters.push(
218                            schema::Reference {
219                                name: parameter.title.clone(),
220                                schema_name: self.get_schema_name(parameter.schema.reference.as_ref().unwrap().clone()),
221                            }
222                        )
223                    }
224                }
225            }
226            validators.push(
227                schema::Validator {
228                    name: validator.title.clone(),
229                    datum: datum,
230                    redeemer: redeemer,
231                    parameters: parameters,
232                }
233            );
234        }
235        validators
236    }
237
238    /// Generates the boilerplate code for a given `Blueprint` for the given `Template`.
239    ///
240    /// # Arguments
241    ///
242    /// * `blueprint` - A `Blueprint` from which to create the boilerplate code.
243    /// * `template` - A `Template` from which to create the boilerplate code.
244    ///
245    /// # Returns
246    ///
247    /// A string containing the boilerplate code.
248    pub fn get_template_from_blueprint(&self, blueprint: blueprint::Blueprint, template: template::Template) -> String {
249        let schemas = self.get_schemas_from_blueprint(blueprint);
250        self.handlebars.render(&template.to_string(), &schemas).unwrap()
251    }
252}