cargo_lambda_build/
zig.rs1use crate::error::BuildError;
2use cargo_lambda_interactive::{
3 choose_option, command::silent_command, is_stdin_tty, progress::Progress,
4};
5use cargo_zigbuild::Zig;
6use miette::{IntoDiagnostic, Result};
7
8pub fn print_install_options(options: &[InstallOption]) {
10 println!("Zig is not installed in your system.");
11 if is_stdin_tty() {
12 println!("Run `cargo lambda system --install-zig` to install Zig.")
13 }
14
15 if !options.is_empty() {
16 println!("You can use any of the following options to install it:");
17 for option in options {
18 println!("\t* {}: `{}`", option, option.usage());
19 }
20 }
21 println!(
22 "\t* Download Zig 0.9.1 or newer from https://ziglang.org/download/ and add it to your PATH"
23 );
24}
25
26pub async fn install_zig(options: Vec<InstallOption>) -> Result<()> {
28 let choice = choose_option(
29 "Zig is not installed in your system.\nHow do you want to install Zig?",
30 options,
31 );
32
33 match choice {
34 Ok(choice) => choice.install().await.map(|_| ()),
35 Err(err) => Err(err).into_diagnostic(),
36 }
37}
38
39pub async fn check_installation() -> Result<()> {
40 if Zig::find_zig().is_ok() {
41 return Ok(());
42 }
43
44 let options = install_options();
45
46 if !is_stdin_tty() || options.is_empty() {
47 print_install_options(&options);
48 return Err(BuildError::ZigMissing.into());
49 }
50
51 install_zig(options).await
52}
53
54#[derive(Debug)]
55pub enum InstallOption {
56 #[cfg(not(windows))]
57 Brew,
58 #[cfg(windows)]
59 Choco,
60 #[cfg(not(windows))]
61 Npm,
62 Pip3,
63 #[cfg(windows)]
64 Scoop,
65}
66
67impl serde::Serialize for InstallOption {
68 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69 where
70 S: serde::Serializer,
71 {
72 serializer.serialize_str(self.usage())
73 }
74}
75
76impl std::fmt::Display for InstallOption {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 #[cfg(not(windows))]
80 InstallOption::Brew => write!(f, "Install with Homebrew"),
81 #[cfg(windows)]
82 InstallOption::Choco => write!(f, "Install with Chocolatey"),
83 #[cfg(not(windows))]
84 InstallOption::Npm => write!(f, "Install with NPM"),
85 InstallOption::Pip3 => write!(f, "Install with Pip3 (Python 3)"),
86 #[cfg(windows)]
87 InstallOption::Scoop => write!(f, "Install with Scoop"),
88 }
89 }
90}
91
92impl InstallOption {
93 pub fn usage(&self) -> &'static str {
94 match self {
95 #[cfg(not(windows))]
96 InstallOption::Brew => "brew install zig",
97 #[cfg(windows)]
98 InstallOption::Choco => "choco install zig",
99 #[cfg(not(windows))]
100 InstallOption::Npm => "npm install -g @ziglang/cli",
101 InstallOption::Pip3 => "pip3 install ziglang",
102 #[cfg(windows)]
103 InstallOption::Scoop => "scoop install zig",
104 }
105 }
106
107 pub async fn install(self) -> Result<()> {
108 let pb = Progress::start("Installing Zig...");
109 let usage = self.usage().split(' ').collect::<Vec<_>>();
110 let usage = usage.as_slice();
111 let result = silent_command(usage[0], &usage[1..usage.len()]).await;
112
113 let finish = if result.is_ok() {
114 "Zig installed"
115 } else {
116 "Failed to install Zig"
117 };
118 pb.finish(finish);
119
120 result
121 }
122}
123
124pub fn install_options() -> Vec<InstallOption> {
125 let mut options = Vec::new();
126
127 #[cfg(not(windows))]
128 if which::which("brew").is_ok() {
129 options.push(InstallOption::Brew);
130 }
131
132 #[cfg(windows)]
133 if which::which("choco").is_ok() {
134 options.push(InstallOption::Choco);
135 }
136
137 #[cfg(windows)]
138 if which::which("scoop").is_ok() {
139 options.push(InstallOption::Scoop);
140 }
141
142 if which::which("pip3").is_ok() {
143 options.push(InstallOption::Pip3);
144 }
145
146 #[cfg(not(windows))]
147 if which::which("npm").is_ok() {
148 options.push(InstallOption::Npm);
149 }
150 options
151}