Skip to main content

waterui_cli/toolchain/
meson.rs

1//! Toolchain support for `meson`.
2
3use std::path::PathBuf;
4
5use crate::{
6    brew::Brew,
7    toolchain::{Host, Installation, Toolchain, ToolchainError},
8    utils::CommandError,
9};
10
11/// Toolchain for `meson`.
12#[derive(Debug, Clone, Default)]
13pub struct Meson;
14
15impl Meson {
16    /// Get the path to the `meson` executable.
17    ///
18    /// # Errors
19    /// Returns an error if `meson` is not found in PATH.
20    pub async fn path(&self, host: &Host) -> Result<PathBuf, which::Error> {
21        host.which("meson").await
22    }
23}
24
25impl Toolchain for Meson {
26    type Installation = MesonInstallation;
27
28    async fn check(&self, host: &Host) -> Result<(), ToolchainError<Self::Installation>> {
29        if host.which("meson").await.is_ok() {
30            Ok(())
31        } else {
32            Err(ToolchainError::fixable(MesonInstallation))
33        }
34    }
35}
36
37/// Installation plan for `meson`.
38#[derive(Debug, Clone)]
39pub struct MesonInstallation;
40
41/// Errors that can occur during `meson` installation.
42#[derive(Debug, thiserror::Error)]
43pub enum FailToInstallMeson {
44    /// Homebrew not found error.
45    #[error("Homebrew not found. Please install Homebrew to proceed.")]
46    BrewNotFound,
47    /// The Homebrew installation command failed.
48    #[error("Failed to install meson via Homebrew: {0}")]
49    BrewInstall(#[from] CommandError),
50    /// Unsupported platform error.
51    #[error(
52        "Automatic installation of meson is not supported on this platform. Please install meson manually."
53    )]
54    UnsupportedPlatform,
55}
56
57impl Installation for MesonInstallation {
58    type Error = FailToInstallMeson;
59
60    async fn install(&self, host: &Host) -> Result<(), Self::Error> {
61        if cfg!(target_os = "macos") {
62            let brew = Brew::default();
63            brew.check(host)
64                .await
65                .map_err(|_| FailToInstallMeson::BrewNotFound)?;
66            brew.install(host, "meson").await?;
67            Ok(())
68        } else {
69            Err(FailToInstallMeson::UnsupportedPlatform)
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::{Meson, MesonInstallation};
77    use crate::toolchain::testing::TestMachine;
78    use crate::toolchain::{Installation, Toolchain, ToolchainError};
79
80    #[test]
81    fn ok_when_meson_on_path() {
82        let machine = TestMachine::new();
83        machine.install("meson");
84        let host = machine.host(Vec::<(String, String)>::new());
85        smol::block_on(Meson.check(&host)).expect("meson on PATH must be ok");
86    }
87
88    #[test]
89    fn missing_meson_is_fixable() {
90        let machine = TestMachine::new();
91        let host = machine.host(Vec::<(String, String)>::new());
92        let result = smol::block_on(Meson.check(&host));
93        assert!(
94            matches!(result, Err(ToolchainError::Fixable(_))),
95            "missing meson must always be fixable: {result:?}"
96        );
97    }
98
99    #[test]
100    #[cfg(target_os = "macos")]
101    fn install_runs_brew() {
102        let machine = TestMachine::new();
103        machine.install("brew");
104        let host = machine.host(Vec::<(String, String)>::new());
105        smol::block_on(MesonInstallation.install(&host))
106            .expect("brew install meson must succeed on a host that provides brew");
107    }
108
109    #[test]
110    #[cfg(not(target_os = "macos"))]
111    fn install_fails_off_macos() {
112        let machine = TestMachine::new();
113        let host = machine.host(Vec::<(String, String)>::new());
114        let result = smol::block_on(MesonInstallation.install(&host));
115        assert!(
116            matches!(result, Err(super::FailToInstallMeson::UnsupportedPlatform)),
117            "meson install must fail fast outside macOS: {result:?}"
118        );
119    }
120}