daml_codegen/generator/
separate.rs

1use crate::generator::module_matcher::ModuleMatcher;
2use crate::generator::RenderMethod;
3use crate::renderer::{quote_all_data, to_module_path, to_rust_identifier, RenderContext, RenderFilterMode};
4use daml_lf::element::{DamlArchive, DamlModule, DamlPackage};
5use itertools::Itertools;
6use std::fs::File;
7use std::io::{Error, Write};
8use std::path::Path;
9use std::path::PathBuf;
10use std::{fs, io};
11
12const DISABLE_WARNINGS: &str = "#![allow(clippy::all, warnings)]";
13const USE_DAML_PRELUDE: &str = "use ::daml::prelude::*;";
14
15pub fn generate_archive_separate(
16    archive: &DamlArchive<'_>,
17    output_path: &Path,
18    module_matcher: &ModuleMatcher,
19    render_method: &RenderMethod,
20) -> Result<(), Error> {
21    let ctx = RenderContext::with_archive(archive, RenderFilterMode::default());
22    for package in archive.packages() {
23        generate_package_source(&ctx, package, output_path, module_matcher, render_method)?;
24    }
25    Ok(())
26}
27
28fn generate_package_source(
29    ctx: &RenderContext<'_>,
30    package: &DamlPackage<'_>,
31    output_path: &Path,
32    module_matcher: &ModuleMatcher,
33    render_method: &RenderMethod,
34) -> Result<(), Error> {
35    let root_modules: Vec<_> =
36        package.root_module().child_modules().filter(|&m| is_interesting_module(m, module_matcher)).collect();
37    let module_decl = root_modules.iter().map(|m| make_pub_mod_declaration(m.local_name())).join("\n");
38    let package_body = format!("{}\n{}", DISABLE_WARNINGS, module_decl);
39    let mut package_file = create_file(&PathBuf::from(output_path), &make_package_filename(package.name()))?;
40    package_file.write_all(package_body.as_bytes())?;
41    let package_dir_path = PathBuf::from(output_path).join(&to_rust_identifier(package.name()));
42    for module in root_modules {
43        generate_module_source(ctx, module, &package_dir_path, module_matcher, render_method)?;
44    }
45    Ok(())
46}
47
48fn generate_module_source(
49    ctx: &RenderContext<'_>,
50    module: &DamlModule<'_>,
51    package_dir_path: &Path,
52    module_matcher: &ModuleMatcher,
53    render_method: &RenderMethod,
54) -> Result<(), Error> {
55    let sub_modules: Vec<_> = module.child_modules().filter(|&m| is_interesting_module(m, module_matcher)).collect();
56    let sub_module_decl: String = sub_modules.iter().map(|&m| make_pub_mod_declaration(m.local_name())).join("\n");
57    let module_types_text = quote_module_data_types(ctx, module, render_method);
58    let module_body = format!("{}\n{}{}", USE_DAML_PRELUDE, sub_module_decl, module_types_text);
59    let module_path: Vec<_> = module.path().collect();
60    let module_dir_path = module_path[..module_path.len() - 1].iter().map(to_rust_identifier).join("/");
61    let package_module_dir_path = PathBuf::from(package_dir_path).join(module_dir_path);
62    let mut module_file = create_file(&package_module_dir_path, &make_module_filename(module.local_name()))?;
63    module_file.write_all(module_body.as_bytes())?;
64    for child_module in sub_modules {
65        generate_module_source(ctx, child_module, package_dir_path, module_matcher, render_method)?;
66    }
67    Ok(())
68}
69
70/// A module is interesting (and should therefore be rendered) if its path is matched by the [`ModuleMatcher`] and if it
71/// contains any data types.  Additionally, a module is considered interesting if any of decedent modules are
72/// interesting.
73fn is_interesting_module(module: &DamlModule<'_>, module_matcher: &ModuleMatcher) -> bool {
74    (module.data_types().next().is_some() && module_matcher.matches(&to_module_path(module.path())))
75        || module.child_modules().any(|m| is_interesting_module(m, module_matcher))
76}
77
78fn quote_module_data_types(ctx: &RenderContext<'_>, module: &DamlModule<'_>, render_method: &RenderMethod) -> String {
79    quote_all_data(ctx, module.data_types().collect::<Vec<_>>().as_slice(), render_method).to_string()
80}
81
82fn create_file(base_dir: &Path, filename: &str) -> io::Result<File> {
83    fs::create_dir_all(&base_dir)?;
84    File::create(base_dir.join(filename))
85}
86
87fn make_package_filename(name: &str) -> String {
88    format!("{}.rs", to_rust_identifier(name))
89}
90
91fn make_module_filename(name: &str) -> String {
92    format!("{}.rs", to_rust_identifier(name))
93}
94
95fn make_pub_mod_declaration(module_name: &str) -> String {
96    format!("pub mod {};", to_rust_identifier(module_name))
97}