1use heck::CamelCase;
2use std::fs;
3use std::path::Path;
4use std::process::Command;
5use std::{env, path::PathBuf};
6use tempfile::NamedTempFile;
7
8pub fn build_dsp(dsp_file: &str) {
9 let out_dir = env::var_os("OUT_DIR").unwrap();
10 let dest_path = Path::new(&out_dir).join("dsp.rs");
11 FaustBuilder::new(dsp_file, dest_path.to_str().unwrap()).build()
12}
13
14pub fn build_dsp_to_destination(dsp_file: &str, dest_path: &str) {
15 FaustBuilder::new(dsp_file, dest_path).build()
16}
17
18pub struct FaustBuilder {
19 faust_path: Option<String>,
20 in_file: String,
21 out_file: String,
22 arch_file: Option<String>,
23 module_name: String,
25 struct_name: Option<String>,
27 use_double: bool,
28 faust_args: Vec<String>,
29}
30
31impl Default for FaustBuilder {
32 fn default() -> Self {
33 Self {
34 faust_path: None,
35 in_file: "".into(),
36 out_file: "".into(),
37 arch_file: None,
38 struct_name: None,
39 module_name: "dsp".into(),
40 use_double: false,
41 faust_args: vec![],
42 }
43 }
44}
45
46impl FaustBuilder {
47 pub fn new(in_file: &str, out_file: &str) -> Self {
48 Self {
49 in_file: in_file.to_string(),
50 out_file: out_file.to_string(),
51 ..Default::default()
52 }
53 }
54
55 pub fn set_struct_name(mut self, struct_name: &str) -> Self {
56 self.struct_name = Some(struct_name.to_string());
57 self
58 }
59 pub fn set_module_name(mut self, module_name: &str) -> Self {
60 self.module_name = module_name.to_string();
61 self
62 }
63
64 pub fn set_use_double(mut self, use_double: bool) -> Self {
65 self.use_double = use_double;
66 self
67 }
68
69 pub fn set_faust_path(mut self, faust_path: &str) -> Self {
70 self.faust_path = Some(faust_path.to_string());
71 self
72 }
73 pub fn set_arch_file(mut self, arch_file: &str) -> Self {
74 self.arch_file = Some(arch_file.to_string());
75 self
76 }
77 pub fn faust_arg(mut self, arg: &str) -> Self {
79 self.faust_args.push(arg.to_string());
80 self
81 }
82
83 pub fn build(&self) {
84 eprintln!("cargo:rerun-if-changed={}", self.in_file);
85
86 let dest_path = PathBuf::from(&self.out_file);
87
88 let default_arch = NamedTempFile::new().expect("failed creating temporary file");
89 let template_code = include_str!("../faust-template.rs");
90 fs::write(default_arch.path(), template_code).expect("failed writing temporary file");
91 let default_template = &default_arch.path().to_str().unwrap().to_owned();
92
93 let template_file = match &self.arch_file {
94 Some(arch_file) => arch_file,
95 None => default_template,
96 };
97
98 let target_file = NamedTempFile::new().expect("failed creating temporary file");
99
100 let faust = self.faust_path.clone().unwrap_or("faust".to_owned());
102 let mut output = Command::new(faust);
103
104 let struct_name = match &self.struct_name {
105 Some(struct_name) => struct_name.clone(),
106 None => {
107 let dsp_path = PathBuf::from(&self.in_file);
108 dsp_path
109 .file_stem()
110 .unwrap()
111 .to_string_lossy()
112 .to_string()
113 .to_camel_case()
114 }
115 };
116
117 output
118 .arg("-a")
119 .arg(template_file)
120 .arg("-lang")
121 .arg("rust")
122 .arg("-t")
123 .arg("0")
124 .arg("-cn")
125 .arg(&struct_name);
126
127 if self.use_double {
128 output.arg("-double");
129 }
130
131 for arg in self.faust_args.clone() {
132 output.arg(arg);
133 }
134
135 output.arg(&self.in_file).arg("-o").arg(target_file.path());
136
137 let output = output.output().expect("Failed to execute command");
138 if !output.status.success() {
143 panic!(
144 "faust compilation failed: {}",
145 String::from_utf8(output.stderr).unwrap()
146 );
147 }
148
149 let dsp_code = fs::read(target_file).unwrap();
150 let dsp_code = String::from_utf8(dsp_code).unwrap();
151 let dsp_code = dsp_code.replace("<<moduleName>>", &self.module_name);
152 let dsp_code = dsp_code.replace("<<structName>>", &struct_name);
153
154 fs::write(&dest_path, dsp_code).expect("failed to write to destination path");
155
156 eprintln!("Wrote module:\n{}", dest_path.to_str().unwrap());
157 }
158 pub fn build_xml(&self, out_dir: &str) {
159 eprintln!("cargo:rerun-if-changed={}", self.in_file);
160 let mut output = Command::new(self.faust_path.clone().unwrap_or("faust".to_owned()));
161
162 let struct_name = match &self.struct_name {
163 Some(struct_name) => struct_name.clone(),
164 None => {
165 let dsp_path = PathBuf::from(&self.in_file);
166 dsp_path
167 .file_stem()
168 .unwrap()
169 .to_string_lossy()
170 .to_string()
171 .to_camel_case()
172 }
173 };
174
175 output
176 .arg("-lang")
177 .arg("rust")
178 .arg("-xml")
179 .arg("--output-dir")
180 .arg(out_dir)
181 .arg("-t")
182 .arg("0")
183 .arg("-cn")
184 .arg(&struct_name);
185
186 if self.use_double {
187 output.arg("-double");
188 }
189
190 for arg in self.faust_args.clone() {
191 output.arg(arg);
192 }
193 let output = output
194 .arg(&self.in_file)
195 .output()
196 .expect("Failed to execute command");
197 if !output.status.success() {
202 panic!(
203 "faust compilation failed: {}",
204 String::from_utf8(output.stderr).unwrap()
205 );
206 }
207 }
208}