1#![feature(exit_status_error)]
2
3use std::path::Path;
4use std::path::PathBuf;
5
6use owo_colors::OwoColorize;
7use rootcause::prelude::ResultExt;
8
9const ZELLIJ_PLUGINS_PATH: &[&str] = &[".config", "zellij", "plugins"];
10const ZELLIJ_LAYOUTS_PATH: &[&str] = &[".config", "zellij", "layouts"];
11const WASM_TARGET: &str = "wasm32-wasip1";
12
13pub struct PluginInstallSpec {
14 pub dir_name: &'static str,
15 pub wasm_name: &'static str,
16}
17
18pub fn build_and_install_plugin(spec: &PluginInstallSpec, is_debug: bool) -> rootcause::Result<()> {
23 build_and_install_plugin_copies(spec, &[spec.wasm_name], is_debug)
24}
25
26pub fn build_and_install_plugin_copies(
31 spec: &PluginInstallSpec,
32 install_names: &[&str],
33 is_debug: bool,
34) -> rootcause::Result<()> {
35 let wasm_path = build_wasm(spec, is_debug)
36 .context("failed to build wasm plugin")
37 .attach_with(|| format!("plugin={}", spec.dir_name))?;
38 for install_name in install_names {
39 install_wasm_plugin(&wasm_path, install_name)
40 .context("failed to install wasm plugin")
41 .attach_with(|| format!("plugin={}", spec.dir_name))
42 .attach_with(|| format!("install_name={install_name}"))?;
43 }
44 Ok(())
45}
46
47pub fn install_layout_file(source: &Path, layout_name: &str) -> rootcause::Result<()> {
52 let install_dir = ytil_sys::dir::build_home_path(ZELLIJ_LAYOUTS_PATH)
53 .context("failed to determine zellij layouts directory")
54 .attach_with(|| format!("layouts_path={ZELLIJ_LAYOUTS_PATH:?}"))?;
55
56 std::fs::create_dir_all(&install_dir)
57 .context("failed to create zellij layouts directory")
58 .attach_with(|| format!("install_dir={}", install_dir.display()))?;
59
60 let dest = install_dir.join(layout_name);
61 ytil_sys::file::atomic_cp(source, &dest)
62 .context("failed to copy layout to install location")
63 .attach_with(|| format!("from={}", source.display()))
64 .attach_with(|| format!("to={}", dest.display()))?;
65
66 println!("{} {} to {}", "Copied".green().bold(), source.display(), dest.display());
67 Ok(())
68}
69
70fn build_wasm(spec: &PluginInstallSpec, is_debug: bool) -> rootcause::Result<PathBuf> {
71 let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
72 let plugin_dir = manifest_dir
73 .parent()
74 .ok_or_else(|| rootcause::report!("cannot resolve zj dir from CARGO_MANIFEST_DIR"))?
75 .join("plugins")
76 .join(spec.dir_name);
77 let workspace_target = manifest_dir
78 .parent()
79 .and_then(Path::parent)
80 .and_then(Path::parent)
81 .ok_or_else(|| rootcause::report!("cannot resolve workspace target from CARGO_MANIFEST_DIR"))?
82 .join("target");
83
84 build_wasm_plugin(&plugin_dir, &workspace_target, spec.wasm_name, is_debug)
85}
86
87fn build_wasm_plugin(
88 plugin_dir: &Path,
89 workspace_target_root: &Path,
90 artifact_name: &str,
91 is_debug: bool,
92) -> rootcause::Result<PathBuf> {
93 ytil_cmd::silent_cmd("rustup")
94 .args(["target", "add", WASM_TARGET])
95 .status()
96 .context("failed to spawn rustup command")
97 .attach_with(|| format!("target={WASM_TARGET}"))?
98 .exit_ok()
99 .context("failed to add wasm32-wasip1 target")
100 .attach_with(|| format!("target={WASM_TARGET}"))?;
101
102 let wasm_target = workspace_target_root.join("wasm-plugins");
103 let mut cmd = ytil_cmd::silent_cmd("cargo");
104 cmd.args(["build", "--target", WASM_TARGET]);
105 cmd.current_dir(plugin_dir);
106 cmd.env("CARGO_TARGET_DIR", &wasm_target);
107 if !is_debug {
108 cmd.arg("--release");
109 }
110 cmd.status()
111 .context("failed to spawn cargo build command")
112 .attach_with(|| format!("target={WASM_TARGET}"))?
113 .exit_ok()
114 .context("failed to build wasm plugin")
115 .attach_with(|| format!("target={WASM_TARGET}"))
116 .attach_with(|| format!("plugin_dir={}", plugin_dir.display()))?;
117
118 let profile = if is_debug { "debug" } else { "release" };
119 Ok(wasm_target.join(WASM_TARGET).join(profile).join(artifact_name))
120}
121
122fn install_wasm_plugin(built: &Path, install_name: &str) -> rootcause::Result<()> {
123 let install_dir = ytil_sys::dir::build_home_path(ZELLIJ_PLUGINS_PATH)
124 .context("failed to determine zellij plugins directory")
125 .attach_with(|| format!("plugins_path={ZELLIJ_PLUGINS_PATH:?}"))?;
126
127 std::fs::create_dir_all(&install_dir)
128 .context("failed to create install directory")
129 .attach_with(|| format!("install_dir={}", install_dir.display()))?;
130
131 let dest = install_dir.join(install_name);
132 ytil_sys::file::atomic_cp(built, &dest)
133 .context("failed to copy wasm plugin to install location")
134 .attach_with(|| format!("from={}", built.display()))
135 .attach_with(|| format!("to={}", dest.display()))?;
136
137 println!("{} {} to {}", "Copied".green().bold(), built.display(), dest.display());
138 Ok(())
139}