1use super::gc;
2use std::fs;
3use std::io;
4use std::path::Path;
5use std::process::{Command, Stdio};
6use structopt::StructOpt;
7use toml_edit::{array, value, Document, Item, Table};
8
9fn worker_table() -> Table {
10 let mut table = Table::new();
11 table["name"] = value("worker");
12 table["path"] = value("src/bin/worker.rs");
13 table
14}
15
16const WASI_WORKER_VERSION: &str = "0.5";
17
18#[derive(Debug, StructOpt)]
22pub enum Cli {
23 Install,
27 Deploy,
29}
30
31impl Cli {
32 const WORKER_JS: &'static [u8] = include_bytes!("../js/dist/worker.js");
33 const WORKER_RS: &'static [u8] = include_bytes!("../worker/worker.rs");
34 pub fn exec(&self) -> io::Result<()> {
35 match self {
36 Self::Install => self.install(),
37 Self::Deploy => self.deploy(),
38 }
39 }
40 fn install(&self) -> io::Result<()> {
41 Self::install_worker()
42 }
43 fn deploy(&self) -> io::Result<()> {
44 println!("Building worker with release settings");
45 Self::build_worker()?;
46 println!("Output will go to ./dist");
47 fs::create_dir_all("dist")?;
48 println!("Copying target/wasm32-wasi/release/worker.wasm");
49 fs::copy("target/wasm32-wasi/release/worker.wasm", "dist/worker.wasm")?;
50 println!("Cleaning worker.wasm with wasm-gc");
51 gc("dist/worker.wasm")?;
52 println!("Deploying JavaScript glue code under dist/worker.js");
53 fs::write("dist/worker.js", Self::WORKER_JS)?;
54 Ok(())
55 }
56
57 fn install_worker() -> io::Result<()> {
58 if !Path::new("./Cargo.toml").exists() {
60 panic!("Current dir is not cargo package");
61 }
62 println!("Copying worker.rs template to src/bin/worker.rs");
63 fs::create_dir_all("src/bin")?;
64 fs::write("src/bin/worker.rs", Self::WORKER_RS)?;
65
66 println!("Checking Cargo.toml for bin worker target...");
67 let cargo_toml = fs::read_to_string("./Cargo.toml")?;
68 let mut toml = cargo_toml
69 .parse::<Document>()
70 .expect("Invalid Cargo.toml, bin target not installed but can be built");
71 let changed = match &mut toml["bin"] {
73 Item::ArrayOfTables(tables) => {
74 if tables
75 .iter()
76 .filter(|table| {
77 table["name"]
78 .as_str()
79 .filter(|val| val == &"worker")
80 .is_some()
81 })
82 .count()
83 == 0
84 {
85 tables.append(worker_table());
86 true
87 } else {
88 false
89 }
90 }
91 _ => {
92 toml["bin"] = array();
93 toml["bin"]
94 .as_array_of_tables_mut()
95 .map(|arr| arr.append(worker_table()));
96 true
97 }
98 };
99 toml["dependencies"]["wasi-worker"] = value(WASI_WORKER_VERSION);
100 if changed {
101 println!("Adding bin worker target to Cargo.toml");
103 fs::write("./Cargo.toml", toml.to_string())?;
104 }
105 Ok(())
106 }
107
108 fn build_worker() -> io::Result<()> {
109 if !Path::new("./Cargo.toml").exists() {
111 panic!("Current dir is not cargo package");
112 }
113
114 let mut cmd = Command::new("cargo");
115 cmd.args(&[
116 "build",
117 "--bin=worker",
118 "--release",
119 "--target=wasm32-wasi",
120 "--target-dir=./target",
121 ])
122 .stdout(Stdio::inherit())
123 .stderr(Stdio::inherit());
124 let output = cmd.output()?;
125
126 let status = output.status;
127 if !status.success() {
128 panic!(
129 "Building worker failed: exit code: {}",
130 status.code().unwrap()
131 );
132 }
133
134 Ok(())
135 }
136}