winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 49c0ddbd9bccb579292ec0da723911ac2310e4a0
parent c82ca612537f6dcecd210985ebbc58eda81b6e54
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Mon, 20 Apr 2020 17:08:53 -0700

Trim individual multi-vals in manifest (#89)


Diffstat:
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 3+++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 3+++
Msrc/AppInstallerCLITests/Strings.cpp | 14++++++++++++++
Asrc/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/YamlManifest.cpp | 16++++++++++++++++
Msrc/AppInstallerCommonCore/AppInstallerStrings.cpp | 26+++++++++++++++++++++++++-
Msrc/AppInstallerCommonCore/Public/AppInstallerStrings.h | 3+++
Msrc/AppInstallerRepositoryCore/Manifest/Manifest.cpp | 13+++++++------
8 files changed, 125 insertions(+), 7 deletions(-)

diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -358,6 +358,9 @@ <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AppInstallerCLICore\AppInstallerCLICore.vcxproj"> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -255,5 +255,8 @@ <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp @@ -36,3 +36,17 @@ TEST_CASE("NormalizedString", "[strings]") std::string_view input2 = u8"\xFB01"; REQUIRE(NormalizedString(input2) == u8"fi"); } + +TEST_CASE("Trim", "[strings]") +{ + std::string str; + REQUIRE(Trim(str.assign("")) == ""); + REQUIRE(Trim(str.assign(" ")) == ""); + REQUIRE(Trim(str.assign(" \t ")) == ""); + REQUIRE(Trim(str.assign(" a")) == "a"); + REQUIRE(Trim(str.assign("bght ")) == "bght"); + REQUIRE(Trim(str.assign("\tStuff\f")) == "Stuff"); + REQUIRE(Trim(str.assign("Multiple words")) == "Multiple words"); + REQUIRE(Trim(str.assign(" Multiple words")) == "Multiple words"); + REQUIRE(Trim(str.assign("Much after is taken \f\n\r\t\v\v\t\r\n\f ")) == "Much after is taken"); +} diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml @@ -0,0 +1,53 @@ +Id: microsoft.msixsdk +Name: " MSIX SDK" +AppMoniker: "msixsdk" +Version: 1.7.32 +Publisher: Microsoft +Channel: release +Author: Microsoft +License: MIT License +LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE +MinOSVersion: 0.0.0.0 +Description: The MSIX SDK project is an effort to enable developers +Homepage: https://github.com/microsoft/msix-packaging +Tags: "msix, appx" +Commands: "makemsix ,makeappx" +Protocols: "protocol1,protocol2" +FileExtensions: "appx,appxbundle,msix,msixbundle" +# InstallerType and Switches CAN have a "default" value +# on the root. An installer can override them. +InstallerType: Zip +Switches: + Custom: /custom + SilentWithProgress: /silentwithprogress + Silent: /silence + Interactive: /interactive + Language: /en-us + Log: /log=<LOGPATH> + InstallLocation: /dir=<INSTALLPATH> +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip + Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + Language: en-US + InstallerType: Zip + Scope: user + Switches: + Custom: /c + SilentWithProgress: /sp + Silent: /s + Interactive: /i + Language: /en + Log: /l=<LOGPATH> + InstallLocation: /d=<INSTALLPATH> + - Arch: x64 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip + Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000 + Language: en-US + InstallerType: Zip + Scope: user +Localization: + - Language: es-MX + Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes + Homepage: https://github.com/microsoft/msix-packaging/es-MX + LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX+ \ No newline at end of file diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp @@ -106,6 +106,22 @@ TEST_CASE("ReadGoodManifestAndVerifyContents", "[ManifestValidation]") REQUIRE(localization1.LicenseUrl == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX"); } +TEST_CASE("ReadGoodManifestWithSpaces", "[ManifestValidation]") +{ + Manifest manifest = Manifest::CreateFromPath(TestDataFile("Manifest-Good-Spaces.yaml")); + + REQUIRE(manifest.Id == "microsoft.msixsdk"); + REQUIRE(manifest.Name == "MSIX SDK"); + REQUIRE(manifest.AppMoniker == "msixsdk"); + REQUIRE(manifest.Version == "1.7.32"); + REQUIRE(manifest.Channel == "release"); + REQUIRE(manifest.MinOSVersion == "0.0.0.0"); + REQUIRE(manifest.Tags == MultiValue{ "msix", "appx" }); + REQUIRE(manifest.Commands == MultiValue{ "makemsix", "makeappx" }); + REQUIRE(manifest.Protocols == MultiValue{ "protocol1", "protocol2" }); + REQUIRE(manifest.FileExtensions == MultiValue{ "appx", "appxbundle", "msix", "msixbundle" }); +} + void TestManifest(const std::filesystem::path& manifestPath, const std::string& expectedError = {}) { if (expectedError.empty()) diff --git a/src/AppInstallerCommonCore/AppInstallerStrings.cpp b/src/AppInstallerCommonCore/AppInstallerStrings.cpp @@ -5,6 +5,13 @@ namespace AppInstaller::Utility { + // Same as std::isspace(char) +#define AICLI_SPACE_CHARS " \f\n\r\t\v"sv + + using namespace std::string_view_literals; + constexpr std::string_view s_SpaceChars = AICLI_SPACE_CHARS; + constexpr std::wstring_view s_WideSpaceChars = L"" AICLI_SPACE_CHARS; + bool CaseInsensitiveEquals(std::string_view a, std::string_view b) { // TODO: When we bring in ICU, do this correctly. @@ -118,7 +125,7 @@ namespace AppInstaller::Utility } std::wstring inputAsWStr(str.data()); - bool nonWhitespaceNotFound = inputAsWStr.find_last_not_of(L" \t\v\f") == std::wstring::npos; + bool nonWhitespaceNotFound = inputAsWStr.find_last_not_of(s_WideSpaceChars) == std::wstring::npos; return nonWhitespaceNotFound; } @@ -136,6 +143,23 @@ namespace AppInstaller::Utility return result; } + std::string& Trim(std::string& str) + { + size_t begin = str.find_first_not_of(s_SpaceChars); + size_t end = str.find_last_not_of(s_SpaceChars); + + if (begin == std::string_view::npos || end == std::string_view::npos) + { + str.clear(); + } + else + { + str = str.substr(begin, (end - begin) + 1); + } + + return str; + } + std::string ReadEntireStream(std::istream& stream) { std::streampos currentPos = stream.tellg(); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -86,6 +86,9 @@ namespace AppInstaller::Utility // Returns a value indicating whether a replacement occurred. bool FindAndReplace(std::string& inputStr, std::string_view token, std::string_view value); + // Removes whitespace from the beginning and end of the string. + std::string& Trim(std::string& str); + // Reads the entire stream into a string. std::string ReadEntireStream(std::istream& stream); } diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp b/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp @@ -25,6 +25,7 @@ namespace AppInstaller::Manifest } std::string splitVal = input.substr(currentPos, splitPos - currentPos); + Utility::Trim(splitVal); if (!splitVal.empty()) { result.emplace_back(std::move(splitVal)); @@ -44,16 +45,16 @@ namespace AppInstaller::Manifest const std::vector<ManifestFieldInfo> FieldInfos = { - { "Id", [this](const YAML::Node& value) { Id = value.as<std::string>(); }, true, "^[\\S]+\\.[\\S]+$" }, - { "Name", [this](const YAML::Node& value) { Name = value.as<std::string>(); }, true }, - { "Version", [this](const YAML::Node& value) { Version = value.as<std::string>(); }, true, + { "Id", [this](const YAML::Node& value) { Id = value.as<std::string>(); Utility::Trim(Id); }, true, "^[\\S]+\\.[\\S]+$" }, + { "Name", [this](const YAML::Node& value) { Name = value.as<std::string>(); Utility::Trim(Name); }, true }, + { "Version", [this](const YAML::Node& value) { Version = value.as<std::string>(); Utility::Trim(Version); }, true, "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, { "Publisher", [this](const YAML::Node& value) { Publisher = value.as<std::string>(); }, true }, - { "AppMoniker", [this](const YAML::Node& value) { AppMoniker = value.as<std::string>(); } }, - { "Channel", [this](const YAML::Node& value) { Channel = value.as<std::string>(); } }, + { "AppMoniker", [this](const YAML::Node& value) { AppMoniker = value.as<std::string>(); Utility::Trim(AppMoniker); } }, + { "Channel", [this](const YAML::Node& value) { Channel = value.as<std::string>(); Utility::Trim(Channel); } }, { "Author", [this](const YAML::Node& value) { Author = value.as<std::string>(); } }, { "License", [this](const YAML::Node& value) { License = value.as<std::string>(); } }, - { "MinOSVersion", [this](const YAML::Node& value) { MinOSVersion = value.as<std::string>(); }, false, + { "MinOSVersion", [this](const YAML::Node& value) { MinOSVersion = value.as<std::string>(); Utility::Trim(MinOSVersion); }, false, "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, { "Tags", [this](const YAML::Node& value) { Tags = SplitMultiValueField(value.as<std::string>()); } }, { "Commands", [this](const YAML::Node& value) { Commands = SplitMultiValueField(value.as<std::string>()); } },