1use crate::utils::Result;
4use anyhow::anyhow;
5use cargo_metadata::{Metadata, MetadataCommand, Package};
6use etc::{Etc, FileSystem};
7use std::{fs, path::PathBuf, process::Command};
8
9pub struct WasmBuilder {
11 metadata: Metadata,
12 package: Package,
13 output: Option<PathBuf>,
14 out_dir: Option<PathBuf>,
15}
16
17impl WasmBuilder {
18 pub fn new(path: impl Into<PathBuf>) -> Result<Self> {
20 let mut metadata_command = MetadataCommand::new();
21 let path = path.into();
22
23 tracing::trace!("parsing cargo metadata from: {path:?}");
24 let metadata = if path.is_dir() {
25 metadata_command.current_dir(&path)
26 } else {
27 metadata_command.manifest_path(&path)
28 }
29 .exec()?;
30
31 let manifest = Etc::from(path).find("Cargo.toml")?;
32 tracing::trace!("expected manifest: {manifest:?}");
33 let package = metadata
34 .packages
35 .iter()
36 .find(|p| p.manifest_path.ends_with(&manifest))
37 .ok_or(anyhow!("package {manifest:?} not found"))?
38 .clone();
39
40 Ok(Self {
41 metadata,
42 package,
43 output: None,
44 out_dir: None,
45 })
46 }
47
48 pub fn output(&self) -> Result<PathBuf> {
50 let out_dir = self.out_dir()?;
51 let output = if let Some(output) = self.output.as_ref() {
52 output.into()
53 } else {
54 out_dir
55 .join(self.package.name.as_str())
56 .with_extension("wasm")
57 };
58
59 Ok(output)
60 }
61
62 pub fn with_output(&mut self, output: impl Into<PathBuf>) -> &mut Self {
64 self.output = Some(output.into());
65 self
66 }
67
68 pub fn out_dir(&self) -> Result<PathBuf> {
70 let out_dir: PathBuf = if let Some(out_dir) = self.out_dir.as_ref() {
71 out_dir.into()
72 } else {
73 let out_dir = self.metadata.target_directory.join("zink");
74 if !out_dir.exists() {
75 fs::create_dir_all(&out_dir)?;
76 }
77 out_dir.into()
78 };
79
80 Ok(out_dir)
81 }
82
83 pub fn with_out_dir(&mut self, out_dir: impl Into<PathBuf>) -> &mut Self {
85 self.out_dir = Some(out_dir.into());
86 self
87 }
88
89 pub fn build(&self) -> Result<()> {
91 self.compile()?;
92 self.post()?;
93 Ok(())
94 }
95
96 fn compile(&self) -> Result<()> {
98 let args = vec![
99 "build",
100 "--manifest-path",
101 self.package.manifest_path.as_str(),
102 "--target",
103 "wasm32-unknown-unknown",
104 "--release",
105 ];
106
107 Command::new("cargo").args(&args).status()?;
108 Ok(())
109 }
110
111 fn post(&self) -> Result<()> {
113 let src = self
114 .metadata
115 .target_directory
116 .join("wasm32-unknown-unknown")
117 .join("release")
118 .join(self.package.name.as_str())
119 .with_extension("wasm");
120
121 zinkc::utils::wasm_opt(src, self.output()?)?;
123
124 Ok(())
125 }
126}