elm_client_gen_cli/
lib.rs1use std::fmt::Write as _;
10use std::path::{Path, PathBuf};
11
12use anyhow::{Context, Result};
13use elm_client_gen_builder::{
14 build_merged_module, group_by_module, DefaultStrategy, MaybeEncoderRef, NameMap, TypeOverrides,
15};
16use elm_client_gen_core::ElmTypeInfo;
17
18pub struct CodegenOptions<'a> {
20 pub output: PathBuf,
23 pub filter_names: &'a [String],
25 pub dry_run: bool,
28}
29
30#[derive(Debug)]
32pub enum CodegenOutcome {
33 Wrote { module_count: usize, root: PathBuf },
36 DryRun(String),
39}
40
41pub fn run_codegen(types: Vec<ElmTypeInfo>, options: CodegenOptions<'_>) -> Result<CodegenOutcome> {
45 let overrides = TypeOverrides::new();
46 let types: Vec<ElmTypeInfo> = types.into_iter().map(|t| overrides.apply(t)).collect();
47 let mut types = types;
48 types.sort_by(|a, b| {
49 a.module_path
50 .cmp(&b.module_path)
51 .then_with(|| a.type_name.cmp(b.type_name))
52 });
53
54 let names = NameMap::from_types(&types);
55
56 let targets: Vec<ElmTypeInfo> = if options.filter_names.is_empty() {
57 types
58 } else {
59 types
60 .into_iter()
61 .filter(|t| options.filter_names.iter().any(|n| n == t.type_name))
62 .collect()
63 };
64
65 if targets.is_empty() {
66 anyhow::bail!("No types matched. Did you `use my_schema_crate as _;` in main.rs?");
67 }
68
69 let strategy = DefaultStrategy;
70 let maybe = MaybeEncoderRef::new(vec!["Json", "Encode", "Extra"], "maybe");
71 let groups = group_by_module(&targets);
72
73 if options.dry_run {
74 let mut buffer = String::new();
75 for (module_path, group) in &groups {
76 let module = build_merged_module(module_path, group, &names, &strategy, &maybe);
77 writeln!(
78 &mut buffer,
79 "-- {}.elm --\n{}\n",
80 module_path.join("."),
81 elm_ast::pretty_print(&module)
82 )?;
83 }
84 return Ok(CodegenOutcome::DryRun(buffer));
85 }
86
87 for (module_path, group) in &groups {
88 let module = build_merged_module(module_path, group, &names, &strategy, &maybe);
89 write_module(&options.output, module_path, &module)?;
90 }
91 Ok(CodegenOutcome::Wrote {
92 module_count: groups.len(),
93 root: options.output,
94 })
95}
96
97fn write_module(
98 output_dir: &Path,
99 module_path: &[&str],
100 module: &elm_ast::file::ElmModule,
101) -> Result<()> {
102 let mut file_path = output_dir.to_path_buf();
103 for segment in module_path {
104 file_path.push(segment);
105 }
106 file_path.set_extension("elm");
107
108 let parent = file_path
109 .parent()
110 .with_context(|| format!("no parent directory for {}", file_path.display()))?;
111 std::fs::create_dir_all(parent)
112 .with_context(|| format!("creating directory {}", parent.display()))?;
113 std::fs::write(&file_path, elm_ast::pretty_print(module))
114 .with_context(|| format!("writing {}", file_path.display()))?;
115 Ok(())
116}