devalang_wasm/services/build/
pipeline.rs1#![cfg(feature = "cli")]
2
3use std::path::{Path, PathBuf};
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6
7use anyhow::Result;
8
9use crate::engine::audio::settings::{AudioBitDepth, AudioChannels, AudioFormat, ResampleQuality};
10use crate::language::syntax::ast::Statement;
11use crate::language::syntax::parser::driver::SimpleParser;
12use crate::tools::logger::Logger;
13
14use super::outputs::ast::AstBuilder;
15use super::outputs::audio::AudioBuilder;
16use super::outputs::logs::LogWriter;
17
18#[derive(Debug, Clone)]
19pub struct BuildRequest {
20 pub entry_path: PathBuf,
21 pub output_root: PathBuf,
22 pub audio_formats: Vec<AudioFormat>,
23 pub bit_depth: AudioBitDepth,
24 pub channels: AudioChannels,
25 pub resample_quality: ResampleQuality,
26 pub sample_rate: u32,
27 pub bpm: f32,
28}
29
30#[derive(Debug, Clone)]
31pub struct BuildArtifacts {
32 pub primary_format: AudioFormat,
33 pub exported_formats: Vec<(AudioFormat, PathBuf)>,
34 pub bit_depth: AudioBitDepth,
35 pub channels: AudioChannels,
36 pub resample_quality: ResampleQuality,
37 pub sample_rate: u32,
38 pub module_name: String,
39 pub statements: Vec<Statement>,
40 pub ast_path: PathBuf,
41 pub primary_audio_path: PathBuf,
42 pub rms: f32,
43 pub audio_render_time: Duration,
44 pub audio_length: Duration,
45 pub total_duration: Duration,
46}
47
48#[derive(Clone)]
49pub struct ProjectBuilder {
50 logger: Arc<Logger>,
51 ast_builder: AstBuilder,
52 audio_builder: AudioBuilder,
53 log_writer: LogWriter,
54}
55
56impl ProjectBuilder {
57 pub fn new(logger: Arc<Logger>) -> Self {
58 let log_writer = LogWriter::new();
59 let audio_logger = logger.clone();
60 Self {
61 logger,
62 ast_builder: AstBuilder::new(),
63 audio_builder: AudioBuilder::new(log_writer, audio_logger),
64 log_writer,
65 }
66 }
67
68 pub fn build(&self, request: &BuildRequest) -> Result<BuildArtifacts> {
69 let build_start = Instant::now();
70 self.logger.action(format!(
71 "Building module from {}",
72 request.entry_path.display()
73 ));
74
75 let statements = self.parse(&request.entry_path)?;
76 let module_name = module_name_from_path(&request.entry_path);
77
78 let ast_path = self
79 .ast_builder
80 .write(&statements, &request.output_root, &module_name)?;
81
82 use super::outputs::audio::MultiFormatRenderSummary;
84 let MultiFormatRenderSummary {
85 primary_path,
86 primary_format,
87 exported_formats,
88 bit_depth,
89 rms,
90 render_time: audio_render_time,
91 audio_length,
92 } = self.audio_builder.render_all_formats(
93 &statements,
94 &request.entry_path,
95 &request.output_root,
96 &module_name,
97 &request.audio_formats,
98 request.bit_depth,
99 request.channels,
100 request.sample_rate,
101 request.resample_quality,
102 request.bpm,
103 )?;
104
105 self.log_writer.clear(&request.output_root)?;
107
108 let formats_str = exported_formats
110 .iter()
111 .map(|(fmt, _)| format!("{:?}", fmt))
112 .collect::<Vec<_>>()
113 .join(", ");
114
115 self.log_writer.append(
116 &request.output_root,
117 &format!(
118 "Module '{}' built with {} statement(s); exported formats: [{}] ({} bits, {} ch, {:?})",
119 module_name,
120 statements.len(),
121 formats_str,
122 bit_depth.bits(),
123 request.channels.count(),
124 request.resample_quality
125 ),
126 )?;
127
128 let total_duration = build_start.elapsed();
129 self.logger.watch(format!(
130 "Build complete in {:.1} ms (audio regen {:.1} ms)",
131 total_duration.as_secs_f64() * 1000.0,
132 audio_render_time.as_secs_f64() * 1000.0
133 ));
134
135 Ok(BuildArtifacts {
136 primary_format,
137 exported_formats,
138 bit_depth,
139 channels: request.channels,
140 resample_quality: request.resample_quality,
141 sample_rate: request.sample_rate,
142 module_name,
143 statements,
144 ast_path,
145 primary_audio_path: primary_path,
146 rms,
147 audio_render_time,
148 audio_length,
149 total_duration,
150 })
151 }
152
153 fn parse(&self, entry: impl AsRef<Path>) -> Result<Vec<Statement>> {
154 SimpleParser::parse_file(entry)
155 }
156}
157
158fn module_name_from_path(path: &Path) -> String {
159 path.file_stem()
160 .map(|stem| stem.to_string_lossy().to_string())
161 .unwrap_or_else(|| "module".to_string())
162}