metapac/backends/
rustup.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::cmd::command_found;
use crate::cmd::run_command;
use crate::cmd::run_command_for_stdout;
use crate::prelude::*;
use color_eyre::Result;
use serde::Deserialize;
use serde::Serialize;
use serde_inline_default::serde_inline_default;
use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)]
pub struct Rustup;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RustupQueryInfo {
    pub components: Vec<String>,
}

#[serde_inline_default]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct RustupInstallOptions {
    #[serde_inline_default(RustupInstallOptions::default().components)]
    pub components: Vec<String>,
}

impl Backend for Rustup {
    type QueryInfo = RustupQueryInfo;
    type InstallOptions = RustupInstallOptions;

    fn map_managed_packages(
        packages: BTreeMap<String, Self::InstallOptions>,
        _: &Config,
    ) -> Result<BTreeMap<String, Self::InstallOptions>> {
        Ok(packages)
    }

    fn query_installed_packages(_: &Config) -> Result<BTreeMap<String, Self::QueryInfo>> {
        if !command_found("rustup") {
            return Ok(BTreeMap::new());
        }

        let mut packages = BTreeMap::new();

        let toolchains_stdout =
            run_command_for_stdout(["rustup", "toolchain", "list"], Perms::Same)?;
        let toolchains = toolchains_stdout.lines().map(|x| {
            x.split(' ')
                .next()
                .expect("output shouldn't contain empty lines")
                .to_string()
        });

        for toolchain in toolchains {
            //due to https://github.com/rust-lang/rustup/issues/1570
            //we sometimes must interpret a failed command as no
            //components for custom toolchains
            if let Ok(components_stdout) = run_command_for_stdout(
                [
                    "rustup",
                    "component",
                    "list",
                    "--installed",
                    "--toolchain",
                    toolchain.as_str(),
                ],
                Perms::Same,
            ) {
                packages.insert(
                    toolchain,
                    RustupQueryInfo {
                        components: components_stdout.lines().map(|x| x.to_string()).collect(),
                    },
                );
            }
        }

        Ok(packages)
    }

    fn install_packages(
        packages: &BTreeMap<String, Self::InstallOptions>,
        _: bool,
        _: &Config,
    ) -> Result<()> {
        for (toolchain, rustup_install_options) in packages.iter() {
            run_command(
                ["rustup", "toolchain", "install", toolchain.as_str()],
                Perms::Same,
            )?;

            if !rustup_install_options.components.is_empty() {
                run_command(
                    [
                        "rustup",
                        "component",
                        "add",
                        "--toolchain",
                        toolchain.as_str(),
                    ]
                    .into_iter()
                    .chain(rustup_install_options.components.iter().map(String::as_str)),
                    Perms::Same,
                )?;
            }
        }

        Ok(())
    }

    fn remove_packages(packages: &BTreeSet<String>, _: bool, _: &Config) -> Result<()> {
        if !packages.is_empty() {
            for toolchain in packages.iter() {
                run_command(
                    ["rustup", "toolchain", "remove", toolchain.as_str()],
                    Perms::Same,
                )?;
            }
        }

        Ok(())
    }
}