summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cc/src/vs_instances.rs
blob: 31d3dd1470fa99ca8aa9ec7e0cff4bf05657cc36 (plain)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use std::borrow::Cow;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::io::BufRead;
use std::path::PathBuf;

use crate::setup_config::{EnumSetupInstances, SetupInstance};

pub enum VsInstance {
    Com(SetupInstance),
    Vswhere(VswhereInstance),
}

impl VsInstance {
    pub fn installation_name(&self) -> Option<Cow<str>> {
        match self {
            VsInstance::Com(s) => s
                .installation_name()
                .ok()
                .and_then(|s| s.into_string().ok())
                .map(Cow::from),
            VsInstance::Vswhere(v) => v.map.get("installationName").map(Cow::from),
        }
    }

    pub fn installation_path(&self) -> Option<PathBuf> {
        match self {
            VsInstance::Com(s) => s.installation_path().ok().map(PathBuf::from),
            VsInstance::Vswhere(v) => v.map.get("installationPath").map(PathBuf::from),
        }
    }

    pub fn installation_version(&self) -> Option<Cow<str>> {
        match self {
            VsInstance::Com(s) => s
                .installation_version()
                .ok()
                .and_then(|s| s.into_string().ok())
                .map(Cow::from),
            VsInstance::Vswhere(v) => v.map.get("installationVersion").map(Cow::from),
        }
    }
}

pub enum VsInstances {
    ComBased(EnumSetupInstances),
    VswhereBased(VswhereInstance),
}

impl IntoIterator for VsInstances {
    type Item = VsInstance;
    #[allow(bare_trait_objects)]
    type IntoIter = Box<Iterator<Item = Self::Item>>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            VsInstances::ComBased(e) => {
                Box::new(e.into_iter().filter_map(Result::ok).map(VsInstance::Com))
            }
            VsInstances::VswhereBased(v) => Box::new(std::iter::once(VsInstance::Vswhere(v))),
        }
    }
}

#[derive(Debug)]
pub struct VswhereInstance {
    map: HashMap<String, String>,
}

impl TryFrom<&Vec<u8>> for VswhereInstance {
    type Error = &'static str;

    fn try_from(output: &Vec<u8>) -> Result<Self, Self::Error> {
        let map: HashMap<_, _> = output
            .lines()
            .filter_map(Result::ok)
            .filter_map(|s| {
                let mut splitn = s.splitn(2, ": ");
                Some((splitn.next()?.to_owned(), splitn.next()?.to_owned()))
            })
            .collect();

        if !map.contains_key("installationName")
            || !map.contains_key("installationPath")
            || !map.contains_key("installationVersion")
        {
            return Err("required properties not found");
        }

        Ok(Self { map })
    }
}

#[cfg(test)]
mod tests_ {
    use std::borrow::Cow;
    use std::convert::TryFrom;
    use std::path::PathBuf;

    #[test]
    fn it_parses_vswhere_output_correctly() {
        let output = br"instanceId: 58104422
installDate: 21/02/2021 21:50:33
installationName: VisualStudio/16.9.2+31112.23
installationPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools
installationVersion: 16.9.31112.23
productId: Microsoft.VisualStudio.Product.BuildTools
productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
state: 4294967295
isComplete: 1
isLaunchable: 1
isPrerelease: 0
isRebootRequired: 0
displayName: Visual Studio Build Tools 2019
description: The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support.
channelId: VisualStudio.16.Release
channelUri: https://aka.ms/vs/16/release/channel
enginePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service
releaseNotes: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-v16.9#16.9.2
thirdPartyNotices: https://go.microsoft.com/fwlink/?LinkId=660909
updateDate: 2021-03-17T21:16:46.5963702Z
catalog_buildBranch: d16.9
catalog_buildVersion: 16.9.31112.23
catalog_id: VisualStudio/16.9.2+31112.23
catalog_localBuild: build-lab
catalog_manifestName: VisualStudio
catalog_manifestType: installer
catalog_productDisplayVersion: 16.9.2
catalog_productLine: Dev16
catalog_productLineVersion: 2019
catalog_productMilestone: RTW
catalog_productMilestoneIsPreRelease: False
catalog_productName: Visual Studio
catalog_productPatchVersion: 2
catalog_productPreReleaseMilestoneSuffix: 1.0
catalog_productSemanticVersion: 16.9.2+31112.23
catalog_requiredEngineVersion: 2.9.3365.38425
properties_campaignId: 156063665.1613940062
properties_channelManifestId: VisualStudio.16.Release/16.9.2+31112.23
properties_nickname: 
properties_setupEngineFilePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installershell.exe
"
        .to_vec();

        let vswhere_instance = super::VswhereInstance::try_from(&output);
        assert!(vswhere_instance.is_ok());

        let vs_instance = super::VsInstance::Vswhere(vswhere_instance.unwrap());
        assert_eq!(
            vs_instance.installation_name(),
            Some(Cow::from("VisualStudio/16.9.2+31112.23"))
        );
        assert_eq!(
            vs_instance.installation_path(),
            Some(PathBuf::from(
                r"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools"
            ))
        );
        assert_eq!(
            vs_instance.installation_version(),
            Some(Cow::from("16.9.31112.23"))
        );
    }

    #[test]
    fn it_returns_an_error_for_empty_output() {
        let output = b"".to_vec();

        let vswhere_instance = super::VswhereInstance::try_from(&output);

        assert!(vswhere_instance.is_err());
    }

    #[test]
    fn it_returns_an_error_for_output_consisting_of_empty_lines() {
        let output = br"

"
        .to_vec();

        let vswhere_instance = super::VswhereInstance::try_from(&output);

        assert!(vswhere_instance.is_err());
    }

    #[test]
    fn it_returns_an_error_for_output_without_required_properties() {
        let output = br"instanceId: 58104422
installDate: 21/02/2021 21:50:33
productId: Microsoft.VisualStudio.Product.BuildTools
productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
"
        .to_vec();

        let vswhere_instance = super::VswhereInstance::try_from(&output);

        assert!(vswhere_instance.is_err());
    }
}