cargo_e/
e_installer.rs

1use crate::e_prompts::yesno;
2use anyhow::{bail, Context, Result};
3use std::error::Error;
4use std::path::{Path, PathBuf};
5use std::process::{Command, Stdio};
6use which::which;
7
8// https://github.com/ahaoboy/is-admin
9#[cfg(windows)]
10pub fn is_admin() -> bool {
11    let shell = "[bool]([System.Security.Principal.WindowsPrincipal][System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)";
12    let output = std::process::Command::new("powershell")
13        .args(["-c", shell])
14        .output()
15        .expect("Failed to execute PowerShell command");
16    String::from_utf8(output.stdout).unwrap_or_default().trim() == "True"
17}
18
19
20// https://github.com/ahaoboy/is-admin
21#[cfg(unix)]
22pub fn is_admin() -> bool {
23    use libc::{geteuid, getuid};
24    unsafe { getuid() == 0 || geteuid() == 0 }
25}
26
27/// Check if the program is running as an administrator.
28/// Returns an error if the program is not running with administrative privileges.
29pub fn ensure_admin_privileges() -> Result<()> {
30    if !is_admin() {
31        return Err(anyhow::anyhow!(
32            "This program must be run as an administrator. Please restart it with administrative privileges."
33        ));
34    }
35    Ok(())
36}
37/// Ensure `npm` is on PATH.  
38/// Ensures Node.js is installed first.  
39/// Returns the full path to the `npm` executable, or an error.
40pub fn ensure_npm() -> Result<PathBuf> {
41    // Ensure Node.js is installed
42    ensure_node()?;
43    which("npm").context("`npm` not found in PATH. Please install Node.js and npm.")
44}
45
46/// Ensure the `napi` CLI is on PATH (provided by `@napi-rs/cli`).  
47/// If missing, prompts the user and installs it globally via `npm install -g @napi-rs/cli`.  
48/// Returns the full path to the `napi` executable.
49pub fn ensure_napi_cli() -> Result<PathBuf, Box<dyn Error>> {
50    // 1) Already installed?
51    if let Ok(path) = which("napi") {
52        return Ok(path);
53    }
54
55    // 2) Prompt the user to install it via npm
56    println!("`napi` CLI not found. Install it globally now?");
57    match yesno(
58        "Do you want to install `@napi-rs/cli` globally via npm?",
59        Some(true),
60    ) {
61        Ok(Some(true)) => {
62            let npm = ensure_npm()?;
63            println!("Installing `@napi-rs/cli` via `npm install -g @napi-rs/cli`…");
64            let mut child = Command::new(npm)
65                .args(&["install", "-g", "@napi-rs/cli"])
66                .stdin(Stdio::null())
67                .stdout(Stdio::inherit())
68                .stderr(Stdio::inherit())
69                .spawn()
70                .map_err(|e| format!("Failed to spawn install command: {}", e))?;
71
72            child
73                .wait()
74                .map_err(|e| format!("Error while waiting for installation: {}", e))?;
75        }
76        Ok(Some(false)) => return Err("User skipped installing `@napi-rs/cli`".into()),
77        Ok(None) => return Err("Installation of `@napi-rs/cli` cancelled (timeout)".into()),
78        Err(e) => return Err(format!("Error during prompt: {}", e).into()),
79    }
80
81    // 3) Retry locating `napi`
82    which("napi").map_err(|_| "`napi` still not found after installation".into())
83}
84
85/// Ensure `cross-env` is on PATH.  
86/// If it’s missing, prompts the user and installs it globally via `npm install -g cross-env`.  
87/// Returns the full path to the `cross-env` executable.
88pub fn ensure_cross_env() -> Result<PathBuf, Box<dyn Error>> {
89    // 1) Already installed?
90    if let Ok(path) = which("cross-env") {
91        return Ok(path);
92    }
93
94    // 2) Prompt the user to install it via npm
95    println!("`cross-env` is not installed. Install it globally now?");
96    match yesno(
97        "Do you want to install `cross-env` globally via npm?",
98        Some(true),
99    ) {
100        Ok(Some(true)) => {
101            // Make sure npm is available
102            let npm = ensure_npm()?;
103            println!("Installing `cross-env` via `npm install -g cross-env`…");
104            let mut child = Command::new(npm)
105                .args(&["install", "-g", "cross-env"])
106                .stdin(Stdio::null())
107                .stdout(Stdio::inherit())
108                .stderr(Stdio::inherit())
109                .spawn()
110                .map_err(|e| format!("Failed to spawn install command: {}", e))?;
111
112            // Wait for the installation to finish
113            child
114                .wait()
115                .map_err(|e| format!("Error while waiting for installation: {}", e))?;
116        }
117        Ok(Some(false)) => return Err("User skipped installing `cross-env`".into()),
118        Ok(None) => return Err("Installation of `cross-env` cancelled (timeout)".into()),
119        Err(e) => return Err(format!("Error during prompt: {}", e).into()),
120    }
121
122    // 3) Retry locating `cross-env`
123    which("cross-env").map_err(|_| "`cross-env` still not found after installation".into())
124}
125/// Ensure `pnpm` is on PATH.  
126/// Ensures Node.js is installed first.  
127/// If it’s missing, will use `npm` (via `ensure_npm`) to install `pnpm` globally.  
128/// Returns the full path to the `pnpm` executable.
129pub fn ensure_pnpm() -> Result<PathBuf> {
130    // Ensure Node.js is installed
131    ensure_node()?;
132
133    // Check if `pnpm` is already installed
134    if let Ok(path) = which("pnpm") {
135        return Ok(path);
136    }
137
138    // Otherwise, prompt the user to install it via npm
139    println!("`pnpm` is not installed. Install it now?");
140    match yesno(
141        "Do you want to install `pnpm` globally via npm?",
142        Some(true),
143    ) {
144        Ok(Some(true)) => {
145            // Make sure we have npm
146            let npm_path = ensure_npm()?;
147            println!("Installing `pnpm` via `npm install -g pnpm`…");
148
149            let mut child = Command::new(npm_path)
150                .args(&["install", "-g", "pnpm"])
151                .stdin(Stdio::null())
152                .stdout(Stdio::inherit())
153                .stderr(Stdio::inherit())
154                .spawn()
155                .context("failed to spawn `npm install -g pnpm`")?;
156
157            child
158                .wait()
159                .context("error while waiting for `npm install -g pnpm` to finish")?;
160        }
161        Ok(Some(false)) => bail!("user skipped installing `pnpm`"),
162        Ok(None) => bail!("installation of `pnpm` cancelled (timeout)"),
163        Err(e) => bail!("error during prompt: {}", e),
164    }
165
166    // Retry locating `pnpm`
167    which("pnpm").context("`pnpm` still not found in PATH after installation")
168}
169
170/// Ensure the `dx` CLI (the Dioxus helper) is on PATH.
171/// If missing, prompts the user to install the Dioxus CLI via `cargo install dioxus-cli`.
172/// Returns the full path to the `dx` executable.
173pub fn ensure_dx() -> Result<PathBuf> {
174    // 1) Check if `dx` is already on PATH
175    if let Ok(path) = which("dx") {
176        return Ok(path);
177    }
178
179    // 2) Prompt the user to install it
180    println!("`dx` CLI not found. Install the Dioxus CLI now?");
181    match yesno(
182        "Do you want to install the Dioxus CLI via `cargo install dioxus-cli`?",
183        Some(true),
184    ) {
185        Ok(Some(true)) => {
186            println!("Installing `dioxus-cli` via `cargo install dioxus-cli`…");
187            let mut child = Command::new("cargo")
188                .args(&["install", "dioxus-cli"])
189                .stdin(Stdio::null())
190                .stdout(Stdio::inherit())
191                .stderr(Stdio::inherit())
192                .spawn()
193                .context("failed to spawn `cargo install dioxus-cli`")?;
194
195            child
196                .wait()
197                .context("error while waiting for `cargo install dioxus-cli` to finish")?;
198        }
199        Ok(Some(false)) => bail!("user skipped installing the Dioxus CLI"),
200        Ok(None) => bail!("installation of the Dioxus CLI cancelled (timeout)"),
201        Err(e) => bail!("error during prompt: {}", e),
202    }
203
204    // 3) Retry locating `dx`
205    which("dx").context("`dx` still not found in PATH after installation")
206}
207
208/// Ensure `trunk` is on PATH.  
209/// Returns the full path to the `trunk` executable, or an error.
210pub fn ensure_trunk() -> Result<PathBuf> {
211    // 1) First try to locate `trunk`
212    if let Ok(path) = which("trunk") {
213        return Ok(path);
214    }
215
216    // 2) Prompt the user to install it
217    println!("`trunk` is not installed. Install it now?");
218    match yesno("Do you want to install `trunk`?", Some(true)) {
219        Ok(Some(true)) => {
220            println!("Installing `trunk` via `cargo install trunk`…");
221            let mut child = Command::new("cargo")
222                .args(&["install", "trunk"])
223                .stdin(Stdio::null())
224                .stdout(Stdio::inherit())
225                .stderr(Stdio::inherit())
226                .spawn()
227                .context("failed to spawn `cargo install trunk`")?;
228
229            child
230                .wait()
231                .context("failed while waiting for `cargo install trunk` to finish")?;
232        }
233        Ok(Some(false)) => {
234            anyhow::bail!("user skipped installing `trunk`");
235        }
236        Ok(None) => {
237            anyhow::bail!("installation of `trunk` cancelled (timeout)");
238        }
239        Err(e) => {
240            anyhow::bail!("error during prompt: {}", e);
241        }
242    }
243
244    // 3) Re‐try locating `trunk`
245    which("trunk").context("`trunk` still not found in PATH after installation")
246}
247
248/// Ensure `rust-script` is on PATH.  
249/// Returns the full path to the `rust-script` executable, or an error.
250pub fn ensure_rust_script() -> Result<PathBuf> {
251    // 1) First try to locate `trunk`
252    if let Ok(path) = which("rust-script") {
253        return Ok(path);
254    }
255
256    // 2) Prompt the user to install it
257    println!("`rust-script` is not installed. Install it now?");
258    match yesno("Do you want to install `rust-script`?", Some(true)) {
259        Ok(Some(true)) => {
260            println!("Installing `rust-script` via `cargo install rust-script`…");
261            let mut child = Command::new("cargo")
262                .args(&["install", "rust-script"])
263                .stdin(Stdio::null())
264                .stdout(Stdio::inherit())
265                .stderr(Stdio::inherit())
266                .spawn()
267                .context("failed to spawn `cargo install rust-script`")?;
268
269            child
270                .wait()
271                .context("failed while waiting for `cargo install rust-script` to finish")?;
272        }
273        Ok(Some(false)) => {
274            anyhow::bail!("user skipped installing `rust-script`");
275        }
276        Ok(None) => {
277            anyhow::bail!("installation of `rust-script` cancelled (timeout)");
278        }
279        Err(e) => {
280            anyhow::bail!("error during prompt: {}", e);
281        }
282    }
283    which("rust-script").context("`rust-script` still not found in PATH after installation")
284}
285// Helper function to check for package.json and run npm install if needed
286pub fn check_npm_and_install(workspace_parent: &Path) -> Result<(), Box<dyn Error>> {
287    if workspace_parent.join("pnpm-workspace.yaml").exists() {
288        // If this is a pnpm workspace, skip npm checks
289        println!("Skipping npm checks for pnpm workspace.");
290        return Ok(());
291    }
292    // Check if package.json exists at the workspace parent level
293    println!(
294        "Checking for package.json in: {}",
295        workspace_parent.display()
296    );
297    if workspace_parent.join("package.json").exists() {
298        println!("package.json found in: {}", workspace_parent.display());
299        // Get the path to npm using `which`.
300        match which("npm") {
301            Ok(npm_path) => {
302                println!("Found npm at: {}", npm_path.display());
303
304                // Run `npm ls --depth=1` in the specified directory
305                let output = Command::new(npm_path.clone())
306                    .arg("ls")
307                    .arg("--depth=1")
308                    .current_dir(workspace_parent)
309                    .output()
310                    .map_err(|e| eprintln!("Failed to execute npm ls: {}", e))
311                    .ok();
312
313                if let Some(output) = output {
314                    println!("npm ls output: {}", String::from_utf8_lossy(&output.stdout));
315                    if !output.status.success() {
316                        // Print the npm error output for debugging.
317                        eprintln!(
318                            "npm ls failed for directory: {}",
319                            workspace_parent.display()
320                        );
321                        eprintln!("{}", String::from_utf8_lossy(&output.stderr));
322
323                        // Run `npm install` to fix the missing dependencies
324                        println!(
325                            "Running npm install in directory: {}",
326                            workspace_parent.display()
327                        );
328                        let install_output = Command::new(npm_path)
329                            .arg("install")
330                            .current_dir(workspace_parent)
331                            .output()
332                            .map_err(|e| eprintln!("Failed to execute npm install: {}", e))
333                            .ok();
334
335                        if let Some(install_output) = install_output {
336                            println!(
337                                "npm install output: {}",
338                                String::from_utf8_lossy(&install_output.stdout)
339                            );
340                            if install_output.status.success() {
341                                println!(
342                                    "npm install completed successfully in: {}",
343                                    workspace_parent.display()
344                                );
345                            } else {
346                                eprintln!(
347                                    "npm install failed in directory: {}",
348                                    workspace_parent.display()
349                                );
350                                eprintln!("{}", String::from_utf8_lossy(&install_output.stderr));
351                            }
352                        }
353                    }
354                }
355            }
356            Err(_) => {
357                eprintln!("npm is not installed or not in the system PATH.");
358                return Err("npm not found".into());
359            }
360        }
361    }
362    Ok(())
363}
364
365/// Check for a pnpm workspace and, if found, run `pnpm install`.  
366/// Returns the full path to the `pnpm` executable.
367pub fn check_pnpm_and_install(workspace_parent: &Path) -> Result<PathBuf> {
368    // if this is a pnpm workspace, install deps
369    let workspace_yaml = workspace_parent.join("pnpm-workspace.yaml");
370    if workspace_yaml.exists() {
371        // ensure pnpm is available (and install it if necessary)
372        let pnpm = ensure_pnpm()?;
373        println!(
374            "Found pnpm-workspace.yaml in: {}",
375            workspace_parent.display()
376        );
377        println!("Running `pnpm install`…");
378
379        let status = Command::new(&pnpm)
380            .arg("install")
381            .current_dir(workspace_parent)
382            .stdin(Stdio::null())
383            .stdout(Stdio::inherit())
384            .stderr(Stdio::inherit())
385            .status()
386            .context("failed to execute `pnpm install`")?;
387
388        if !status.success() {
389            bail!("`pnpm install` failed with exit code {}", status);
390        }
391        //         if cfg!( target_os = "windows" ) {
392        //             #[cfg(windows)]
393        // use std::os::windows::process::CommandExt;
394        //             // WinAPI flag for “create a new console window”
395        // #[cfg(windows)]
396        // const CREATE_NEW_CONSOLE: u32 = 0x0000_0010;
397        //             println!("Running `pnpm run build:debug windows");
398        //                 // Build the command
399        //     let mut cmd = Command::new("cmd");
400        //     cmd.args(&["/C", "pnpm run build:debug"])
401        //        .current_dir(workspace_parent);
402        //     //    .stdin(Stdio::null())
403        //     //    .stdout(Stdio::inherit())
404        //     //    .stderr(Stdio::inherit());
405
406        //     // On Windows, ask for a new console window
407        //     #[cfg(windows)]
408        //     {
409        //         cmd.creation_flags(CREATE_NEW_CONSOLE);
410        //     }
411        //                 let status =
412        //     cmd.status()?;
413        // if !status.success() {
414        //     anyhow::bail!("`pnpm run build:debug` failed with {}", status);
415        // }
416        //         } else {
417        // ensure_napi_cli().ok();
418        // ensure_cross_env().ok();
419        Command::new(&pnpm)
420            .args(&["run", "build:debug"])
421            .current_dir(workspace_parent)
422            .env("CARGO", "cargo")
423            .status()?;
424        // };
425
426        println!("✅ pnpm install succeeded");
427        return Ok(pnpm);
428    } else {
429        println!(
430            "No pnpm-workspace.yaml found in {}, skipping `pnpm install`.",
431            workspace_parent.display()
432        );
433    }
434    Ok(PathBuf::new())
435}
436
437/// Ensure `node` is on PATH.  
438/// If missing, attempts to install Node.js using `nvm` (automated for Windows, manual prompt otherwise).  
439/// Returns the full path to the `node` executable.
440pub fn ensure_node() -> Result<PathBuf> {
441    // Check if `node` is already installed
442    if let Ok(path) = which("node") {
443        return Ok(path);
444    }
445
446    #[cfg(target_os = "windows")]
447    {
448        // On Windows, use Chocolatey to install NVM and set Node.js to LTS
449        println!("`node` is not installed.");
450        match yesno(
451            "Do you want to install Node.js using NVM (via Chocolatey)?",
452            Some(true),
453        ) {
454            Ok(Some(true)) => {
455                println!("Installing NVM via Chocolatey...");
456                let choco = ensure_choco()?;
457                let mut child = Command::new(choco)
458                    .args(&["install", "nvm"]) //, "-y"])
459                    .stdin(Stdio::null())
460                    .stdout(Stdio::inherit())
461                    .stderr(Stdio::inherit())
462                    .spawn()
463                    .context("Failed to spawn `choco install nvm`")?;
464
465                child
466                    .wait()
467                    .context("Error while waiting for `choco install nvm` to finish")?;
468
469                // Use NVM to install and use the latest LTS version of Node.js
470                let nvm = which("nvm").context("`nvm` not found in PATH after installation.")?;
471                let mut child = Command::new(&nvm)
472                    .args(&["install", "lts"])
473                    .stdin(Stdio::null())
474                    .stdout(Stdio::inherit())
475                    .stderr(Stdio::inherit())
476                    .spawn()
477                    .context("Failed to spawn `nvm install lts`")?;
478
479                child
480                    .wait()
481                    .context("Error while waiting for `nvm install lts` to finish")?;
482
483                let mut child = Command::new(&nvm)
484                    .args(&["use", "lts"])
485                    .stdin(Stdio::null())
486                    .stdout(Stdio::inherit())
487                    .stderr(Stdio::inherit())
488                    .spawn()
489                    .context("Failed to spawn `nvm use lts`")?;
490
491                child
492                    .wait()
493                    .context("Error while waiting for `nvm use lts` to finish")?;
494            }
495            Ok(Some(false)) => {
496                anyhow::bail!("User declined to install Node.js.");
497            }
498            Ok(None) => {
499                anyhow::bail!("Installation of Node.js cancelled (timeout).");
500            }
501            Err(e) => {
502                anyhow::bail!("Error during prompt: {}", e);
503            }
504        }
505    }
506
507    #[cfg(not(target_os = "windows"))]
508    {
509        // On non-Windows systems, prompt the user to install Node.js manually
510        println!("`node` is not installed. Please install Node.js manually.");
511        anyhow::bail!("Node.js installation is not automated for this platform.");
512    }
513
514    // Retry locating `node`
515    which("node").context("`node` still not found after installation")
516}
517
518/// Ensure the GitHub CLI (`gh`) is on PATH.  
519/// If missing, installs it using Chocolatey on Windows.  
520/// Returns the full path to the `gh` executable.
521pub fn ensure_github_gh() -> Result<PathBuf> {
522    // Check if `gh` is already installed
523    if let Ok(path) = which("gh") {
524        return Ok(path);
525    }
526    // Check if `gh.exe` exists in the default installation path
527    let default_path = Path::new("C:\\Program Files\\GitHub CLI\\gh.exe");
528    if default_path.exists() {
529        return Ok(default_path.to_path_buf());
530    }
531    #[cfg(target_os = "windows")]
532    {
533        // Ensure Chocolatey is installed
534        let choco = ensure_choco()?;
535
536        // Install GitHub CLI using Chocolatey
537        println!("Installing GitHub CLI (`gh`) via Chocolatey...");
538        if let Err(e) = ensure_admin_privileges() {
539            eprintln!("Error: {}", e);
540            return Err(e);
541        }
542        let mut child = Command::new(choco)
543            .args(&["install", "gh", "y"])
544            .stdin(Stdio::null())
545            .stdout(Stdio::inherit())
546            .stderr(Stdio::inherit())
547            .spawn()
548            .context("Failed to spawn `choco install gh`")?;
549
550        child
551            .wait()
552            .context("Error while waiting for `choco install gh` to finish")?;
553        if default_path.exists() {
554            return Ok(default_path.to_path_buf());
555        }
556    }
557
558    #[cfg(not(target_os = "windows"))]
559    {
560        anyhow::bail!("GitHub CLI installation is only automated on Windows.");
561    }
562
563    // Retry locating `gh`
564    which("gh").context("`gh` still not found after installation")
565}
566
567/// Ensure `choco` (Chocolatey) is on PATH.  
568/// If missing, prompts the user to install Chocolatey manually.  
569/// Returns the full path to the `choco` executable.
570pub fn ensure_choco() -> Result<PathBuf> {
571    // Check if `choco` is already installed
572    if let Ok(path) = which("choco") {
573        return Ok(path);
574    }
575
576    #[cfg(target_os = "windows")]
577    {
578        // On Windows, prompt the user to install Chocolatey manually
579        println!("`choco` (Chocolatey) is not installed.");
580        println!("It is required to proceed. Do you want to install it manually?");
581        match yesno(
582            "Do you want to install Chocolatey manually by following the instructions?",
583            Some(true),
584        ) {
585            Ok(Some(true)) => {
586                println!("Please run the following command in PowerShell to install Chocolatey:");
587                println!("Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))");
588                anyhow::bail!(
589                    "Chocolatey installation is not automated. Please install it manually."
590                );
591            }
592            Ok(Some(false)) => {
593                anyhow::bail!("User declined to install Chocolatey.");
594            }
595            Ok(None) => {
596                anyhow::bail!("Installation of Chocolatey cancelled (timeout).");
597            }
598            Err(e) => {
599                anyhow::bail!("Error during prompt: {}", e);
600            }
601        }
602    }
603
604    #[cfg(not(target_os = "windows"))]
605    {
606        anyhow::bail!("Chocolatey is only supported on Windows.");
607    }
608}
609
610/// Ensure the `cargo-leptos` CLI is on PATH.  
611/// If missing, prompts the user to install it via `cargo install cargo-leptos`.  
612/// Returns the full path to the `cargo-leptos` executable.
613pub fn ensure_leptos() -> Result<PathBuf> {
614    // 1) Check if `cargo-leptos` is already on PATH
615    if let Ok(path) = which("cargo-leptos") {
616        return Ok(path);
617    }
618
619    // 2) Prompt the user to install it
620    println!("`cargo-leptos` CLI not found. Install it now?");
621    match yesno(
622        "Do you want to install the `cargo-leptos` CLI via `cargo install cargo-leptos`?",
623        Some(true),
624    ) {
625        Ok(Some(true)) => {
626            // Check if `perl` is available
627            if which("perl").is_err() {
628                println!("`perl` is not installed or not found in PATH.");
629                println!("OpenSSL requires `perl` for installation unless OpenSSL is already configured in your environment.");
630                println!("It is recommended to have a working `perl` distribution installed for openssl.");
631                ensure_perl();
632            }
633
634            println!("Installing `cargo-leptos` via `cargo install cargo-leptos`…");
635            let mut child = Command::new("cargo")
636                .args(&["install", "cargo-leptos"])
637                .stdin(Stdio::null())
638                .stdout(Stdio::inherit())
639                .stderr(Stdio::inherit())
640                .spawn()
641                .context("Failed to spawn `cargo install cargo-leptos`")?;
642
643            child
644                .wait()
645                .context("Error while waiting for `cargo install cargo-leptos` to finish")?;
646        }
647        Ok(Some(false)) => bail!("User skipped installing `cargo-leptos`."),
648        Ok(None) => bail!("Installation of `cargo-leptos` cancelled (timeout)."),
649        Err(e) => bail!("Error during prompt: {}", e),
650    }
651
652    // 3) Retry locating `cargo-leptos`
653    which("cargo-leptos").context("`cargo-leptos` still not found after installation")
654}
655
656#[cfg(target_os = "windows")]
657pub fn ensure_perl() {
658    use std::process::Command;
659    use which::which;
660
661    // Check if choco is installed
662    if which("choco").is_err() {
663        eprintln!("Chocolatey (choco) is not installed.");
664        println!("Please install Chocolatey from https://chocolatey.org/install to proceed with Perl installation.");
665        return;
666    }
667
668    println!("Perl is missing. You can install Strawberry Perl using Chocolatey (choco).");
669    println!("Suggestion: choco install strawberryperl");
670
671    match crate::e_prompts::yesno(
672        "Do you want to install Strawberry Perl using choco?",
673        Some(true), // Default to yes
674    ) {
675        Ok(Some(true)) => {
676            println!("Installing Strawberry Perl...");
677            match Command::new("choco")
678                .args(["install", "strawberryperl", "-y"])
679                .spawn()
680            {
681                Ok(mut child) => {
682                    child.wait().ok(); // Wait for installation to complete
683                    println!("Strawberry Perl installation completed.");
684                }
685                Err(e) => {
686                    eprintln!("Error installing Strawberry Perl via choco: {}", e);
687                }
688            }
689        }
690        Ok(Some(false)) => {
691            println!("Strawberry Perl installation skipped.");
692        }
693        Ok(None) => {
694            println!("Installation cancelled (timeout or invalid input).");
695        }
696        Err(e) => {
697            eprintln!("Error during prompt: {}", e);
698        }
699    }
700}
701
702#[cfg(not(target_os = "windows"))]
703pub fn ensure_perl() {
704    println!("auto_sense_perl is only supported on Windows with Chocolatey.");
705}