| use std::borrow::Cow; |
| use std::collections::HashMap; |
| use std::convert::TryFrom; |
| use std::io::BufRead; |
| use std::path::PathBuf; |
| |
| use crate::windows::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()); |
| } |
| } |