tools for building gleam projects with nix
at javascript 91 lines 2.7 kB view raw
1// SPDX-FileCopyrightText: 2025 Ruby Iris Juric <ruby@srxl.me> 2// 3// SPDX-License-Identifier: 0BSD 4 5use std::{fs, path::Path}; 6 7use miette::{Context, IntoDiagnostic, Result, miette}; 8 9use crate::gleam_toml::{self, GleamToml}; 10 11pub struct AppSpec { 12 pub name: String, 13 pub contents: String, 14} 15 16fn get_applications(otp_dependencies: &[String], gleam_toml: &GleamToml) -> Vec<String> { 17 let extra_applications = gleam_toml 18 .erlang 19 .as_ref() 20 .and_then(|e| e.extra_applications.clone()) 21 .unwrap_or_default(); 22 let mut applications = [otp_dependencies, &extra_applications].concat(); 23 applications.sort(); 24 applications.dedup(); 25 26 applications 27} 28 29fn get_modules(out_dir: &str) -> Result<Vec<String>> { 30 std::fs::read_dir(out_dir) 31 .into_diagnostic()? 32 .collect::<Result<Vec<_>, _>>() 33 .into_diagnostic() 34 .wrap_err(format!("While getting files in \"{out_dir}\":"))? 35 .iter() 36 .filter(|path| { 37 path.path() 38 .extension() 39 .map(|ext| ext == "beam") 40 .unwrap_or(false) 41 }) 42 .map(|path| { 43 path.path() 44 .file_stem() 45 .and_then(|p| p.to_str()) 46 .map(|p| p.to_string()) 47 .ok_or(miette!("Non UTF-8 filename in directory")) 48 }) 49 .collect::<Result<Vec<_>, _>>() 50} 51 52fn generate_app_spec(otp_dependencies: &[String], out_dir: &str) -> Result<AppSpec> { 53 let gleam_toml = gleam_toml::parse("gleam.toml")?; 54 55 let applications = get_applications(otp_dependencies, &gleam_toml).join(","); 56 let modules = get_modules(out_dir)?.join(","); 57 let start_module = gleam_toml 58 .erlang 59 .and_then(|e| e.application_start_module) 60 .map(|module| format!("{{mod, {{'{module}', []}}}},\n")) 61 .unwrap_or_default(); 62 63 Ok(AppSpec { 64 name: format!("{}.app", gleam_toml.name), 65 contents: format!( 66 r#"{{application, {0}, [ 67 {{vsn, "{1}"}}, 68 {start_module}{{applications, [{applications}]}}, 69 {{description, "{2}"}}, 70 {{modules, [{modules}]}}, 71 {{registered, []}} 72]}}."#, 73 gleam_toml.name, 74 gleam_toml.version, 75 gleam_toml.description.unwrap_or_default() 76 ), 77 }) 78} 79 80pub fn generate_app_file(args: &[String]) -> Result<()> { 81 let (out_path, otp_dependencies) = args 82 .split_first() 83 .ok_or(miette!("No output path specified"))?; 84 85 let app_spec = generate_app_spec(otp_dependencies, &out_path)?; 86 87 fs::write(Path::new(&out_path).join(&app_spec.name), app_spec.contents).into_diagnostic()?; 88 println!("Successfully generated {}", app_spec.name); 89 90 Ok(()) 91}