Bladeren bron

Use semester for JSON processing

default_compile_flags
vector-of-bool 4 jaren geleden
bovenliggende
commit
bb3c1b0bc1
8 gewijzigde bestanden met toevoegingen van 135 en 276 verwijderingen
  1. +1
    -0
      library.dds
  2. +2
    -1
      library.jsonc
  3. +3
    -2
      package.dds
  4. +2
    -1
      package.jsonc
  5. +53
    -19
      src/dds/library/manifest.cpp
  6. +71
    -51
      src/dds/package/manifest.cpp
  7. +0
    -199
      src/dds/util/json5_read.hpp
  8. +3
    -3
      tools/gcc-9.dds

+ 1
- 0
library.dds Bestand weergeven

@@ -7,5 +7,6 @@ Uses: nlohmann/json
Uses: neo/sqlite3
Uses: neo/fun
Uses: semver/semver
Uses: vob/semester
Uses: pubgrub/pubgrub
Uses: vob/json5

+ 2
- 1
library.jsonc Bestand weergeven

@@ -10,6 +10,7 @@
"neo/fun",
"semver/semver",
"pubgrub/pubgrub",
"vob/json5"
"vob/json5",
"vob/semester",
]
}

+ 3
- 2
package.dds Bestand weergeven

@@ -10,6 +10,7 @@ Depends: neo-sqlite3 0.2.2
Depends: neo-fun 0.1.0
Depends: semver 0.2.1
Depends: pubgrub 0.2.0
Depends: json5 0.1.2
Depends: vob-json5 0.1.5-dev
Depends: semester 0.1.0

Test-Driver: Catch-Main
Test-Driver: Catch-Main

+ 2
- 1
package.jsonc Bestand weergeven

@@ -12,7 +12,8 @@
"neo-fun": "0.1.0",
"semver": "0.2.1",
"pubgrub": "0.2.0",
"json5": "0.1.2"
"vob-json5": "0.1.5-dev",
"semester": "*"
},
"test_driver": "Catch-Main"
}

+ 53
- 19
src/dds/library/manifest.cpp Bestand weergeven

@@ -3,11 +3,11 @@
#include <dds/dym.hpp>
#include <dds/error/errors.hpp>
#include <dds/util/algo.hpp>
#include <dds/util/json5_read.hpp>
#include <libman/parse.hpp>

#include <json5/parse_data.hpp>
#include <range/v3/view/transform.hpp>
#include <semester/decomp.hpp>
#include <spdlog/spdlog.h>

using namespace dds;
@@ -42,24 +42,58 @@ library_manifest library_manifest::load_from_file(path_ref fpath) {
}

library_manifest lib;
using namespace json_read::ops;
json_read::decompose( //
data.as_object(),
object(key("name", require_string(put_into{lib.name}, "`name` must be a string")),
key("uses",
array_each{require_string(
[&](auto&& uses) {
lib.uses.push_back(lm::split_usage_string(uses.as_string()));
return json_read::accept_t{};
},
"All `uses` items must be strings")}),
key("links",
array_each{require_string(
[&](auto&& links) {
lib.links.push_back(lm::split_usage_string(links.as_string()));
return json_read::accept_t{};
},
"All `links` items must be strings")})));
using namespace semester::decompose_ops;
auto res = semester::decompose( //
data,
try_seq{require_type<json5::data::mapping_type>{
"The root of the library manifest must be an object (mapping)"},
mapping{
if_key{"name",
require_type<std::string>{"`name` must be a string"},
put_into{lib.name}},
if_key{"uses",
require_type<json5::data::array_type>{
"`uses` must be an array of usage requirements"},
for_each{
require_type<std::string>{"`uses` elements must be strings"},
[&](auto&& uses) {
lib.uses.push_back(lm::split_usage_string(uses.as_string()));
return semester::dc_accept;
},
}},
if_key{"links",
require_type<json5::data::array_type>{
"`links` must be an array of usage requirements"},
for_each{
require_type<std::string>{"`links` elements must be strings"},
[&](auto&& links) {
lib.links.push_back(lm::split_usage_string(links.as_string()));
return semester::dc_accept;
},
}},
}});
auto rej = std::get_if<semester::dc_reject_t>(&res);
if (rej) {
throw_user_error<errc::invalid_lib_manifest>(rej->message);
}
// using namespace json_read::ops;
// json_read::decompose( //
// data.as_object(),
// object(key("name", require_string(put_into{lib.name}, "`name` must be a string")),
// key("uses",
// array_each{require_string(
// [&](auto&& uses) {
// lib.uses.push_back(lm::split_usage_string(uses.as_string()));
// return json_read::accept_t{};
// },
// "All `uses` items must be strings")}),
// key("links",
// array_each{require_string(
// [&](auto&& links) {
// lib.links.push_back(lm::split_usage_string(links.as_string()));
// return json_read::accept_t{};
// },
// "All `links` items must be strings")})));

if (lib.name.empty()) {
throw_user_error<errc::invalid_lib_manifest>(

+ 71
- 51
src/dds/package/manifest.cpp Bestand weergeven

@@ -2,13 +2,13 @@

#include <dds/dym.hpp>
#include <dds/error/errors.hpp>
#include <dds/util/json5_read.hpp>
#include <dds/util/string.hpp>
#include <libman/parse.hpp>

#include <range/v3/view/split.hpp>
#include <range/v3/view/split_when.hpp>
#include <range/v3/view/transform.hpp>
#include <semester/decomp.hpp>
#include <spdlog/spdlog.h>

#include <json5/parse_data.hpp>
@@ -78,62 +78,82 @@ package_manifest package_manifest::load_from_file(const fs::path& fpath) {
throw_user_error<errc::invalid_pkg_manifest>("Root value must be an object");
}

const auto& obj = data.as_object();
package_manifest ret;

using namespace json_read::ops;
json_read::decompose(
obj,
object(
key("name", require_string(put_into{ret.pkg_id.name}, "`name` must be a string")),
key("namespace",
require_string(put_into{ret.namespace_}, "`namespace` must be a string")),
ignore_key{"$schema"},
key("version",
require_string(
using namespace semester::decompose_ops;
auto res = semester::decompose( //
data,
try_seq{
require_type<json5::data::mapping_type>{
"The root of a package manifest must be an object (mapping)"},
mapping{
if_key{"$schema", just_accept},
if_key{
"name",
require_type<std::string>{"`name` must be a string"},
put_into{ret.pkg_id.name},
},
if_key{
"namespace",
require_type<std::string>{"`namespace` must be a string"},
put_into{ret.namespace_},
},
if_key{
"version",
require_type<std::string>{"`version` must be a string"},
[&](auto&& version_str_) {
auto& version = version_str_.as_string();
ret.pkg_id.version = semver::version::parse(version);
return json_read::accept_t{};
return semester::dc_accept;
},
"`version` must be a string")),
key("depends", object([&](auto key, auto&& range_str_) {
auto pkg_name = std::string(key);
if (!range_str_.is_string()) {
throw_user_error<errc::invalid_pkg_manifest>(
"Dependency for '{}' must be a range string", pkg_name);
}
try {
auto rng = semver::range::parse_restricted(range_str_.as_string());
dependency dep{std::string(pkg_name), {rng.low(), rng.high()}};
ret.dependencies.push_back(std::move(dep));
} catch (const semver::invalid_range&) {
throw_user_error<errc::invalid_version_range_string>(
"Invalid version range string '{}' in dependency declaration for '{}'",
range_str_.as_string(),
pkg_name);
}
return json_read::accept_t{};
})),
key("test_driver",
require_string(
[&](auto&& test_driver_str_) {
auto& test_driver = test_driver_str_.as_string();
if (test_driver == "Catch-Main") {
ret.test_driver = test_lib::catch_main;
} else if (test_driver == "Catch") {
ret.test_driver = test_lib::catch_;
} else {
auto dym = *did_you_mean(test_driver, {"Catch-Main", "Catch"});
throw_user_error<errc::unknown_test_driver>(
"Unknown 'test_driver' '{}' (Did you mean '{}'?)",
test_driver,
dym);
},
if_key{
"depends",
require_type<json5::data::mapping_type>{
"`depends` must be a mapping between package names and version ranges"},
mapping{[&](auto pkg_name, auto&& range_str_) {
if (!range_str_.is_string()) {
throw_user_error<errc::invalid_pkg_manifest>(
"Dependency for '{}' must be a range string", pkg_name);
}
return json_read::accept_t{};
},
"`test_driver` must be a valid test driver name string")),
reject_key));
try {
auto rng = semver::range::parse_restricted(range_str_.as_string());
dependency dep{std::string(pkg_name), {rng.low(), rng.high()}};
ret.dependencies.push_back(std::move(dep));
} catch (const semver::invalid_range&) {
throw_user_error<errc::invalid_version_range_string>(
"Invalid version range string '{}' in dependency declaration for "
"'{}'",
range_str_.as_string(),
pkg_name);
}
return semester::dc_accept;
}},
},
if_key{"test_driver",
require_type<std::string>{"`test_driver` must be a string"},
[&](auto&& test_driver_str_) {
auto& test_driver = test_driver_str_.as_string();
if (test_driver == "Catch-Main") {
ret.test_driver = test_lib::catch_main;
} else if (test_driver == "Catch") {
ret.test_driver = test_lib::catch_;
} else {
auto dym = *did_you_mean(test_driver, {"Catch-Main", "Catch"});
throw_user_error<errc::unknown_test_driver>(
"Unknown 'test_driver' '{}' (Did you mean '{}'?)",
test_driver,
dym);
}
return semester::dc_accept;
}},
[&](auto key, auto&&) {
return semester::dc_reject_t{
fmt::format("Unknown key `{}` in package manifest", key)};
}}});
auto rej = std::get_if<semester::dc_reject_t>(&res);
if (rej) {
throw_user_error<errc::invalid_pkg_manifest>(rej->message);
}

if (ret.pkg_id.name.empty()) {
throw_user_error<errc::invalid_pkg_manifest>("The 'name' field is required.");

+ 0
- 199
src/dds/util/json5_read.hpp Bestand weergeven

@@ -1,199 +0,0 @@
#pragma once

#include <json5/data.hpp>

#include <tuple>
#include <variant>

namespace dds {

namespace json_read {

struct reject_t {
std::string message;
};

struct accept_t {};
struct pass_t {};

using result_var = std::variant<reject_t, accept_t, pass_t>;

inline namespace ops {

struct reject {
std::string_view message;

result_var operator()(const json5::data&) const noexcept {
return reject_t{std::string(message)};
}
};

template <typename... Handlers>
struct then {
std::tuple<Handlers...> _hs;

explicit then(Handlers... hs)
: _hs(std::move(hs)...) {}

result_var _handle(const json5::data&) noexcept { return pass_t{}; }

template <typename Head, typename... Tail>
result_var _handle(const json5::data& dat, Head&& h, Tail&&... tail) {
result_var res = h(dat);
if (!std::holds_alternative<pass_t>(res)) {
return res;
}
return _handle(dat, tail...);
}

result_var operator()(const json5::data& dat) {
return std::apply([&](auto&&... hs) { return _handle(dat, hs...); }, _hs);
}
};

template <typename... KeyHandlers>
struct object {
std::tuple<KeyHandlers...> _keys;

explicit object(KeyHandlers... ks)
: _keys(ks...) {}

result_var _handle(std::string_view, const json5::data&) { return pass_t{}; }

template <typename Head, typename... Tail>
result_var _handle(std::string_view key, const json5::data& dat, Head cur, Tail... ts) {
result_var current = cur(key, dat);
if (std::holds_alternative<pass_t>(current)) {
return _handle(key, dat, ts...);
}
return current;
}

result_var operator()(const json5::data& dat) {
if (!dat.is_object()) {
return pass_t{};
}

for (const auto& [key, val] : dat.as_object()) {
result_var res
= std::apply([&](auto... ks) { return _handle(key, val, ks...); }, _keys);
if (std::holds_alternative<accept_t>(res)) {
continue;
}
if (std::holds_alternative<reject_t>(res)) {
return res;
}
}

return accept_t{};
}
};

template <typename Handler>
struct array_each {
Handler _hs;

result_var operator()(const json5::data& arr) {
if (!arr.is_array()) {
return pass_t{};
}
for (const auto& elem : arr.as_array()) {
result_var res = _hs(elem);
if (std::holds_alternative<reject_t>(res)) {
return res;
}
}
return accept_t{};
}
};

template <typename Handler>
array_each(Handler) -> array_each<Handler>;

template <typename Handler>
struct key {
std::string_view _key;
Handler _handle;

key(std::string_view k, Handler h)
: _key(k)
, _handle(h) {}

result_var operator()(std::string_view key, const json5::data& dat) {
if (key == _key) {
return _handle(dat);
}
return pass_t{};
}
};

inline struct reject_key_t {
result_var operator()(std::string_view key, const json5::data&) const noexcept {
return reject_t{"The key `" + std::string(key) + "` is invalid"};
}
} reject_key;

struct ignore_key {
std::string_view key;

result_var operator()(std::string_view key, const json5::data&) const noexcept {
if (key == this->key) {
return accept_t{};
}
return pass_t{};
}
};

template <typename T, typename Handler>
struct accept_type {
Handler _handle;

result_var operator()(const json5::data& d) {
if (!d.is<T>()) {
return pass_t{};
}
return _handle(d);
}
};

template <typename T, typename Handler>
auto accept(Handler h) {
return accept_type<T, Handler>{h};
}

template <typename H>
auto if_string(H h) {
return accept<std::string>(h);
}

template <typename H>
auto require_string(H h, std::string_view msg) {
return then(if_string(h), reject{msg});
}

template <typename T>
struct put_into {
T& _dest;

result_var operator()(const json5::data& d) {
_dest = d.as<T>();
return accept_t{};
}
};

template <typename T>
put_into(T) -> put_into<T>;

} // namespace ops

template <typename Handler>
auto decompose(const json5::data& dat, Handler&& h) {
result_var res = h(dat);
if (std::holds_alternative<reject_t>(res)) {
throw std::runtime_error(std::get<reject_t>(res).message);
}
}

} // namespace json_read

} // namespace dds

+ 3
- 3
tools/gcc-9.dds Bestand weergeven

@@ -5,7 +5,7 @@ C++-Compiler: g++-9
# Range-v3 0.10.0 contains an accidental conversion warning
Flags: -D SPDLOG_COMPILED_LIB -Werror=return-type -Wno-conversion
C++-Flags: -fconcepts
Link-Flags: -static-libgcc -static-libstdc++
# Debug: True
Optimize: True
# Link-Flags: -static-libgcc -static-libstdc++
Debug: True
#Optimize: True
Compiler-Launcher: ccache

Laden…
Annuleren
Opslaan