metapac/backends/
mise.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use color_eyre::Result;
4use color_eyre::eyre::eyre;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::cmd::{run_command, run_command_for_stdout};
9use crate::prelude::*;
10
11#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
12pub struct Mise;
13
14#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct MiseOptions {
17    #[serde(default)]
18    version: Option<String>,
19}
20
21#[derive(Debug, Serialize, Deserialize, Default)]
22#[serde(deny_unknown_fields)]
23pub struct MiseConfig {}
24
25impl Backend for Mise {
26    type Options = MiseOptions;
27    type Config = MiseConfig;
28
29    fn invalid_package_help_text() -> String {
30        String::new()
31    }
32
33    fn is_valid_package_name(_: &str) -> Option<bool> {
34        None
35    }
36
37    fn get_all(_: &Self::Config) -> Result<BTreeSet<String>> {
38        let search = run_command_for_stdout(
39            ["mise", "search", "--no-headers", "--quiet"],
40            Perms::Same,
41            true,
42        )?;
43
44        Ok(search
45            .lines()
46            .map(|line| {
47                line.split_whitespace()
48                    .next()
49                    .expect("mise search lines should not be empty")
50                    .to_string()
51            })
52            .collect())
53    }
54
55    fn get_installed(config: &Self::Config) -> Result<BTreeMap<String, Self::Options>> {
56        if Self::version(config).is_err() {
57            return Ok(BTreeMap::new());
58        }
59
60        let packages = run_command_for_stdout(
61            [
62                "mise",
63                "ls",
64                "--current",
65                "--installed",
66                "--global",
67                "--json",
68                "--quiet",
69            ],
70            Perms::Same,
71            true,
72        )?;
73
74        let packages_json = match serde_json::from_str(&packages)? {
75            Value::Object(x) => x,
76            _ => return Err(eyre!("json should be an object")),
77        };
78
79        let mut packages = BTreeMap::new();
80        for (key, value) in packages_json {
81            // Each package maps to an array of version objects
82            let versions = value
83                .as_array()
84                .ok_or(eyre!("mise package {key:?} should be an array"))?;
85
86            // Only one version in the array (the one in use)
87            if let Some(first_version) = versions.first() {
88                packages.insert(
89                    key.clone(),
90                    MiseOptions {
91                        version: first_version
92                            .get("requested_version")
93                            .and_then(|x| x.as_str())
94                            .map(|x| x.to_string()),
95                    },
96                );
97            }
98        }
99
100        Ok(packages)
101    }
102
103    fn install(
104        packages: &BTreeMap<String, Self::Options>,
105        no_confirm: bool,
106        _: &Self::Config,
107    ) -> Result<()> {
108        for (package, options) in packages {
109            let package = format!("{package}@{}", options.version.as_deref().unwrap_or(""));
110            run_command(
111                ["mise", "use", "--global"]
112                    .into_iter()
113                    .chain(Some("--yes").filter(|_| no_confirm))
114                    .chain(std::iter::once(package.as_str())),
115                Perms::Same,
116            )?;
117        }
118
119        Ok(())
120    }
121
122    fn uninstall(packages: &BTreeSet<String>, _: bool, _: &Self::Config) -> Result<()> {
123        for package in packages {
124            run_command(["mise", "uninstall", package], Perms::Same)?;
125        }
126
127        Ok(())
128    }
129
130    fn update(packages: &BTreeSet<String>, _: bool, _: &Self::Config) -> Result<()> {
131        for package in packages {
132            run_command(["mise", "upgrade", package], Perms::Same)?;
133        }
134
135        Ok(())
136    }
137
138    fn update_all(_: bool, _: &Self::Config) -> Result<()> {
139        run_command(["mise", "upgrade"], Perms::Same)
140    }
141
142    fn clean_cache(_: &Self::Config) -> Result<()> {
143        Ok(())
144    }
145
146    fn version(_: &Self::Config) -> Result<String> {
147        run_command_for_stdout(["mise", "--version"], Perms::Same, false)
148    }
149}