orbiton/commands/
build.rs1use anyhow::{Context, Result};
4use clap::Args;
5use console::style;
6use log::info;
7use std::path::{Path, PathBuf};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum BuildTarget {
12 Web,
13 Desktop,
14 Embedded,
15}
16
17impl From<&str> for BuildTarget {
18 fn from(value: &str) -> Self {
19 match value.to_lowercase().as_str() {
20 "web" => BuildTarget::Web,
21 "desktop" => BuildTarget::Desktop,
22 "embedded" => BuildTarget::Embedded,
23 _ => BuildTarget::Web, }
25 }
26}
27
28impl std::fmt::Display for BuildTarget {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 BuildTarget::Web => write!(f, "web"),
32 BuildTarget::Desktop => write!(f, "desktop"),
33 BuildTarget::Embedded => write!(f, "embedded"),
34 }
35 }
36}
37
38#[derive(Args)]
39pub struct BuildArgs {
40 #[arg(short, long)]
42 dir: Option<PathBuf>,
43
44 #[arg(short, long, default_value = "web")]
46 target: String,
47
48 #[arg(short, long)]
50 output: Option<PathBuf>,
51
52 #[arg(short, long)]
54 release: bool,
55}
56
57pub fn execute(args: BuildArgs) -> Result<()> {
58 let project_dir = match args.dir {
60 Some(dir) => dir,
61 None => std::env::current_dir()?,
62 };
63
64 if !project_dir.exists() {
66 return Err(anyhow::anyhow!(
67 "Project directory does not exist: {project_dir:?}"
68 ));
69 }
70
71 let target = BuildTarget::from(args.target.as_str());
73
74 let output_dir = match args.output {
76 Some(dir) => dir,
77 None => {
78 let mut dir = project_dir.clone();
79 dir.push("build");
80 dir.push(target.to_string());
81 dir
82 }
83 };
84
85 println!(
86 "{} project for target {}",
87 style("Building").bold().green(),
88 style(&target).bold()
89 );
90
91 if !output_dir.exists() {
93 std::fs::create_dir_all(&output_dir)
94 .with_context(|| format!("Failed to create output directory: {output_dir:?}"))?;
95 }
96
97 match target {
99 BuildTarget::Web => {
100 build_for_web(project_dir.as_path(), output_dir.as_path(), args.release)?
101 }
102 BuildTarget::Desktop => {
103 build_for_desktop(project_dir.as_path(), output_dir.as_path(), args.release)?
104 }
105 BuildTarget::Embedded => {
106 build_for_embedded(project_dir.as_path(), output_dir.as_path(), args.release)?
107 }
108 }
109
110 println!(
111 "\n{} successful. Output at {output_dir:?}",
112 style("Build").bold().green()
113 );
114
115 Ok(())
116}
117
118struct BuildProgress {
119 progress_bar: indicatif::ProgressBar,
120}
121
122impl BuildProgress {
123 fn new(steps: u64, target: &BuildTarget) -> Self {
124 let progress_bar = indicatif::ProgressBar::new(steps);
125 progress_bar.set_style(
126 indicatif::ProgressStyle::default_bar()
127 .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {msg} ({eta})")
128 .expect("Failed to set progress bar style")
129 .progress_chars("#>-"),
130 );
131 progress_bar.set_message(format!("Building for {target}"));
132 Self { progress_bar }
133 }
134
135 fn step(&self, msg: &str) {
136 self.progress_bar.inc(1);
137 self.progress_bar.set_message(msg.to_string());
138 }
139
140 fn finish(&self, msg: &str) {
141 self.progress_bar.finish_with_message(msg.to_string());
142 }
143}
144
145fn build_for_web(project_dir: &Path, output_dir: &Path, release: bool) -> Result<()> {
146 info!("Starting Web build process");
147 let progress = BuildProgress::new(4, &BuildTarget::Web);
148
149 progress.step("Parsing .orbit files");
151 let orbit_files = find_orbit_files(project_dir)?;
152
153 progress.step("Generating Rust code");
155 generate_rust_code(&orbit_files, output_dir)?;
156
157 progress.step("Compiling to WASM");
159 compile_to_wasm(output_dir, release)?;
160
161 progress.step("Generating HTML/JS/CSS wrappers");
163 generate_web_wrappers(output_dir)?;
164
165 progress.finish("Web build completed successfully");
166 Ok(())
167}
168
169fn build_for_desktop(project_dir: &Path, output_dir: &Path, release: bool) -> Result<()> {
170 info!("Starting Desktop build process");
171 let progress = BuildProgress::new(3, &BuildTarget::Desktop);
172
173 progress.step("Parsing .orbit files");
175 let orbit_files = find_orbit_files(project_dir)?;
176
177 progress.step("Generating Rust code");
179 generate_rust_code(&orbit_files, output_dir)?;
180
181 progress.step("Compiling native binary");
183 compile_native_binary(output_dir, release)?;
184
185 progress.finish("Desktop build completed successfully");
186 Ok(())
187}
188
189fn build_for_embedded(project_dir: &Path, output_dir: &Path, release: bool) -> Result<()> {
190 info!("Starting Embedded build process");
191 let progress = BuildProgress::new(4, &BuildTarget::Embedded);
192
193 progress.step("Parsing .orbit files");
195 let orbit_files = find_orbit_files(project_dir)?;
196
197 progress.step("Generating Rust code");
199 generate_rust_code(&orbit_files, output_dir)?;
200
201 progress.step("Optimizing for embedded target");
203 optimize_for_embedded(output_dir)?;
204
205 progress.step("Creating firmware package");
207 create_firmware_package(output_dir, release)?;
208
209 progress.finish("Embedded build completed successfully");
210 Ok(())
211}
212
213fn find_orbit_files(dir: &Path) -> Result<Vec<PathBuf>> {
214 let mut files = Vec::new();
215 for entry in walkdir::WalkDir::new(dir) {
216 let entry = entry.context("Failed to read directory entry")?;
217 if entry.path().extension().is_some_and(|ext| ext == "orbit") {
218 files.push(entry.path().to_path_buf());
219 }
220 }
221 Ok(files)
222}
223
224fn generate_rust_code(_orbit_files: &[PathBuf], _output_dir: &Path) -> Result<()> {
225 std::thread::sleep(std::time::Duration::from_millis(500));
230 Ok(())
231}
232
233fn compile_to_wasm(_output_dir: &Path, _release: bool) -> Result<()> {
234 std::thread::sleep(std::time::Duration::from_millis(1000));
239 Ok(())
240}
241
242fn generate_web_wrappers(output_dir: &Path) -> Result<()> {
243 let _ = output_dir; std::thread::sleep(std::time::Duration::from_millis(300));
249 Ok(())
250}
251
252fn compile_native_binary(output_dir: &Path, release: bool) -> Result<()> {
253 let _ = (output_dir, release); std::thread::sleep(std::time::Duration::from_millis(1500));
259 Ok(())
260}
261
262fn optimize_for_embedded(output_dir: &Path) -> Result<()> {
263 let _ = output_dir; std::thread::sleep(std::time::Duration::from_millis(800));
269 Ok(())
270}
271
272fn create_firmware_package(output_dir: &Path, release: bool) -> Result<()> {
273 let _ = (output_dir, release); std::thread::sleep(std::time::Duration::from_millis(500));
279 Ok(())
280}