Rework Paragraph parsing error handling. Add single paragraph functions
MODIFY
include/Paragraphs.h
include/Paragraphs.h
namespace vcpkg::Paragraphs
{
- std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path);
- std::vector<std::unordered_map<std::string, std::string>> parse_paragraphs(const std::string& str);
+ expected<std::unordered_map<std::string, std::string>> get_single_paragraph(const fs::path& control_path);
+ expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(const fs::path& control_path);
+ expected<std::unordered_map<std::string, std::string>> parse_single_paragraph(const std::string& str);
+ expected<std::vector<std::unordered_map<std::string, std::string>>> parse_paragraphs(const std::string& str);
expected<SourceParagraph> try_load_port(const fs::path& control_path);

ADD
include/paragraph_parse_result.h
+#pragma once
+#include <system_error>
+
+namespace vcpkg
+{
+ enum class paragraph_parse_result
+ {
+ SUCCESS = 0,
+ EXPECTED_ONE_PARAGRAPH
+ };
+
+ struct paragraph_parse_result_category_impl final : std::error_category
+ {
+ virtual const char* name() const noexcept override;
+
+ virtual std::string message(int ev) const noexcept override;
+ };
+
+ const std::error_category& paragraph_parse_result_category();
+
+ std::error_code make_error_code(paragraph_parse_result e);
+
+ paragraph_parse_result to_paragraph_parse_result(int i);
+
+ paragraph_parse_result to_paragraph_parse_result(std::error_code ec);
+}
+
+// Enable implicit conversion to std::error_code
+namespace std
+{
+ template <>
+ struct is_error_code_enum<vcpkg::paragraph_parse_result> : ::std::true_type
+ {
+ };
+}

MODIFY
src/Paragraphs.cpp
src/Paragraphs.cpp
#include "pch.h"
#include "Paragraphs.h"
#include "vcpkg_Files.h"
+#include "paragraph_parse_result.h"
namespace vcpkg::Paragraphs
{
@@ -150,7 +151,18 @@ namespace vcpkg::Paragraphs
}
};
- std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path)
+ expected<std::unordered_map<std::string, std::string>> get_single_paragraph(const fs::path& control_path)
+ {
+ const expected<std::string> contents = Files::read_contents(control_path);
+ if (auto spgh = contents.get())
+ {
+ return parse_single_paragraph(*spgh);
+ }
+
+ return contents.error_code();
+ }
+
+ expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(const fs::path& control_path)
{
const expected<std::string> contents = Files::read_contents(control_path);
if (auto spgh = contents.get())
@@ -158,44 +170,47 @@ namespace vcpkg::Paragraphs
return parse_paragraphs(*spgh);
}
- Checks::exit_with_message(VCPKG_LINE_INFO, "Error while reading %s: %s", control_path.generic_string(), contents.error_code().message());
+ return contents.error_code();
+ }
+
+ expected<std::unordered_map<std::string, std::string>> parse_single_paragraph(const std::string& str)
+ {
+ const std::vector<std::unordered_map<std::string, std::string>> p = Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
+
+ if (p.size() == 1)
+ {
+ return p.at(0);
}
- std::vector<std::unordered_map<std::string, std::string>> parse_paragraphs(const std::string& str)
+ return std::error_code(paragraph_parse_result::EXPECTED_ONE_PARAGRAPH);
+ }
+
+ expected<std::vector<std::unordered_map<std::string, std::string>>> parse_paragraphs(const std::string& str)
{
return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
}
expected<SourceParagraph> try_load_port(const fs::path& path)
{
- try
+ expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(path / "CONTROL");
+ if (auto p = pghs.get())
{
- auto pghs = get_paragraphs(path / "CONTROL");
- Checks::check_exit(VCPKG_LINE_INFO, pghs.size() == 1, "Invalid control file at %s\\CONTROL", path.string());
- return SourceParagraph(pghs[0]);
+ return SourceParagraph(*p);
}
- catch (std::runtime_error const&) {}
- return std::errc::no_such_file_or_directory;
+ return pghs.error_code();
}
expected<BinaryParagraph> try_load_cached_package(const vcpkg_paths& paths, const package_spec& spec)
{
- const fs::path path = paths.package_dir(spec) / "CONTROL";
+ expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(paths.package_dir(spec) / "CONTROL");
- auto control_contents_maybe = Files::read_contents(path);
- if (auto control_contents = control_contents_maybe.get())
- {
- std::vector<std::unordered_map<std::string, std::string>> pghs;
- try
+ if (auto p = pghs.get())
{
- pghs = parse_paragraphs(*control_contents);
+ return BinaryParagraph(*p);
}
- catch (std::runtime_error) {}
- Checks::check_exit(VCPKG_LINE_INFO, pghs.size() == 1, "Invalid control file at %s", path.string());
- return BinaryParagraph(pghs[0]);
- }
- return control_contents_maybe.error_code();
+
+ return pghs.error_code();
}
std::vector<SourceParagraph> load_all_ports(const fs::path& ports_dir)

MODIFY
src/PostBuildLint_BuildInfo.cpp
src/PostBuildLint_BuildInfo.cpp
@@ -39,10 +39,8 @@ namespace vcpkg::PostBuildLint
BuildInfo read_build_info(const fs::path& filepath)
{
- const std::vector<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_paragraphs(filepath);
- Checks::check_exit(VCPKG_LINE_INFO, pghs.size() == 1, "Invalid BUILD_INFO file for package");
-
- return BuildInfo::create(pghs[0]);
+ const expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(filepath);
+ Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package");
+ return BuildInfo::create(*pghs.get());
}
-
}

MODIFY
src/commands_cache.cpp
src/commands_cache.cpp
@@ -13,24 +13,13 @@ namespace vcpkg::Commands::Cache
for (auto it = fs::directory_iterator(paths.packages); it != fs::directory_iterator(); ++it)
{
const fs::path& path = it->path();
-
- try
- {
- auto file_contents = Files::read_contents(path / "CONTROL");
- if (auto text = file_contents.get())
+ const expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(path / "CONTROL");
+ if (auto p = pghs.get())
{
- auto pghs = Paragraphs::parse_paragraphs(*text);
- if (pghs.size() != 1)
- continue;
-
- const BinaryParagraph binary_paragraph = BinaryParagraph(pghs[0]);
+ const BinaryParagraph binary_paragraph = BinaryParagraph(*p);
output.push_back(binary_paragraph);
}
}
- catch (std::runtime_error const&)
- {
- }
- }
return output;
}

MODIFY
src/commands_import.cpp
src/commands_import.cpp
@@ -72,11 +72,11 @@ namespace vcpkg::Commands::Import
const fs::path include_directory(args.command_arguments[1]);
const fs::path project_directory(args.command_arguments[2]);
- auto pghs = Paragraphs::get_paragraphs(control_file_path);
- Checks::check_exit(VCPKG_LINE_INFO, pghs.size() == 1, "Invalid control file %s for package", control_file_path.generic_string());
+ const expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(control_file_path);
+ Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid control file %s for package", control_file_path.generic_string());
StatusParagraph spgh;
- spgh.package = BinaryParagraph(pghs[0]);
+ spgh.package = BinaryParagraph(*pghs.get());
auto& control_file_data = spgh.package;
do_import(paths, include_directory, project_directory, control_file_data);

ADD
src/paragraph_parse_result.cpp
+#include "pch.h"
+#include "vcpkg_Checks.h"
+#include "paragraph_parse_result.h"
+
+namespace vcpkg
+{
+ const char* paragraph_parse_result_category_impl::name() const noexcept
+ {
+ return "paragraph_parse_result";
+ }
+
+ std::string paragraph_parse_result_category_impl::message(int ev) const noexcept
+ {
+ switch (static_cast<paragraph_parse_result>(ev))
+ {
+ case paragraph_parse_result::SUCCESS:
+ return "OK";
+ case paragraph_parse_result::EXPECTED_ONE_PARAGRAPH:
+ return "There should be exactly one paragraph";
+ default:
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ const std::error_category& paragraph_parse_result_category()
+ {
+ static paragraph_parse_result_category_impl instance;
+ return instance;
+ }
+
+ std::error_code make_error_code(paragraph_parse_result e)
+ {
+ return std::error_code(static_cast<int>(e), paragraph_parse_result_category());
+ }
+
+ paragraph_parse_result to_paragraph_parse_result(int i)
+ {
+ return static_cast<paragraph_parse_result>(i);
+ }
+
+ paragraph_parse_result to_paragraph_parse_result(std::error_code ec)
+ {
+ return to_paragraph_parse_result(ec.value());
+ }
+}

MODIFY
src/tests_paragraph.cpp
src/tests_paragraph.cpp
@@ -153,14 +153,14 @@ namespace UnitTest1
TEST_METHOD(parse_paragraphs_empty)
{
const char* str = "";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::IsTrue(pghs.empty());
}
TEST_METHOD(parse_paragraphs_one_field)
{
const char* str = "f1: v1";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(1), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -171,7 +171,7 @@ namespace UnitTest1
const char* str =
"f1: v1\n"
"f2: v2";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -186,7 +186,7 @@ namespace UnitTest1
"\n"
"f3: v3\n"
"f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(2), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -204,7 +204,7 @@ namespace UnitTest1
"F:\n"
"0:\n"
"F-2:\n";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(5), pghs[0].size());
}
@@ -218,7 +218,7 @@ namespace UnitTest1
"\n"
"f3: v3\n"
"f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(2), pghs.size());
}
@@ -227,7 +227,7 @@ namespace UnitTest1
const char* str =
"f1:\n"
"f2: ";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("", pghs[0]["f1"].c_str());
@@ -243,7 +243,7 @@ namespace UnitTest1
"f2:\r\n"
" f2\r\n"
" continue\r\n";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual("simple\n f1", pghs[0]["f1"].c_str());
Assert::AreEqual("\n f2\n continue", pghs[0]["f2"].c_str());
@@ -257,7 +257,7 @@ namespace UnitTest1
"\r\n"
"f3: v3\r\n"
"f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(2), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -277,7 +277,7 @@ namespace UnitTest1
{"Multi-Arch", "same"},
});
ss << pgh;
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str()).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(4), pghs[0].size());
Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
@@ -299,7 +299,7 @@ namespace UnitTest1
{"Multi-Arch", "same"},
});
ss << pgh;
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str()).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(7), pghs[0].size());
Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
@@ -321,7 +321,7 @@ namespace UnitTest1
{"Depends", "a, b, c"},
});
ss << pgh;
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str()).get_or_throw(VCPKG_LINE_INFO);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
}

MODIFY
src/vcpkg.cpp
src/vcpkg.cpp
@@ -111,10 +111,8 @@ static void loadConfig()
try
{
- std::string config_contents = Files::read_contents(localappdata / "vcpkg" / "config").get_or_throw(VCPKG_LINE_INFO);
-
+ auto pghs = Paragraphs::get_paragraphs(localappdata / "vcpkg" / "config").get_or_throw(VCPKG_LINE_INFO);
std::unordered_map<std::string, std::string> keys;
- auto pghs = Paragraphs::parse_paragraphs(config_contents);
if (pghs.size() > 0)
keys = pghs[0];

MODIFY
src/vcpkglib.cpp
src/vcpkglib.cpp
@@ -22,8 +22,7 @@ namespace vcpkg
fs::rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file);
}
- auto text = Files::read_contents(vcpkg_dir_status_file).get_or_throw(VCPKG_LINE_INFO);
- auto pghs = Paragraphs::parse_paragraphs(text);
+ auto pghs = Paragraphs::get_paragraphs(vcpkg_dir_status_file).get_or_throw(VCPKG_LINE_INFO);
std::vector<std::unique_ptr<StatusParagraph>> status_pghs;
for (auto&& p : pghs)
@@ -65,8 +64,7 @@ namespace vcpkg
if (b->path().filename() == "incomplete")
continue;
- auto text = Files::read_contents(b->path()).get_or_throw(VCPKG_LINE_INFO);
- auto pghs = Paragraphs::parse_paragraphs(text);
+ auto pghs = Paragraphs::get_paragraphs(b->path()).get_or_throw(VCPKG_LINE_INFO);
for (auto&& p : pghs)
{
current_status_db.insert(std::make_unique<StatusParagraph>(p));

MODIFY
vcpkglib/vcpkglib.vcxproj
vcpkglib/vcpkglib.vcxproj
<ClInclude Include="..\include\BinaryParagraph.h" />
<ClInclude Include="..\include\lazy.h" />
<ClInclude Include="..\include\LineInfo.h" />
+ <ClInclude Include="..\include\paragraph_parse_result.h" />
<ClInclude Include="..\include\PostBuildLint_BuildInfo.h" />
<ClInclude Include="..\include\PostBuildLint_BuildPolicies.h" />
<ClInclude Include="..\include\coff_file_reader.h" />
<ClCompile Include="..\src\BinaryParagraph.cpp" />
<ClCompile Include="..\src\commands_ci.cpp" />
<ClCompile Include="..\src\LineInfo.cpp" />
+ <ClCompile Include="..\src\paragraph_parse_result.cpp" />
<ClCompile Include="..\src\PostBuildLint_BuildInfo.cpp" />
<ClCompile Include="..\src\PostBuildLint_BuildPolicies.cpp" />
<ClCompile Include="..\src\coff_file_reader.cpp" />

MODIFY
vcpkglib/vcpkglib.vcxproj.filters
vcpkglib/vcpkglib.vcxproj.filters
<ClCompile Include="..\src\LineInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\src\paragraph_parse_result.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\package_spec.h">
<ClInclude Include="..\include\LineInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\include\paragraph_parse_result.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file

Rework Paragraph parsing error handling. Add single paragraph functions