Browse Source

library.json manifest format

default_compile_flags
vector-of-bool 4 years ago
parent
commit
2055f4a01c
10 changed files with 190 additions and 32 deletions
  1. +15
    -0
      library.jsonc
  2. +34
    -0
      res/library-schema.json
  3. +2
    -2
      res/package-schema.json
  4. +13
    -8
      src/dds/error/errors.cpp
  5. +1
    -0
      src/dds/error/errors.hpp
  6. +79
    -4
      src/dds/library/manifest.cpp
  7. +10
    -1
      src/dds/library/manifest.hpp
  8. +8
    -4
      src/dds/library/root.cpp
  9. +5
    -5
      src/dds/package/manifest.cpp
  10. +23
    -8
      src/dds/util/json5_read.hpp

+ 15
- 0
library.jsonc View File

{
"$schema": "./res/library-schema.json",
"name": "dds",
"uses": [
"spdlog/spdlog",
"Microsoft/wil",
"range-v3/range-v3",
"nlohmann/json",
"neo/sqlite3",
"neo/fun",
"semver/semver",
"pubgrub/pubgrub",
"vob/json5"
]
}

+ 34
- 0
res/library-schema.json View File

{
"type": "object",
"description": "DDS Library Manifest",
"additionalProperties": false,
"patternProperties": {
"^\\$": {}
},
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the library within the package.",
"pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$"
},
"uses": {
"type": "array",
"items": {
"type": "string",
"description": "A library that is used by this library. Should be of the form `namespace/name`.",
"pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*/[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$"
}
},
"links": {
"type": "array",
"items": {
"type": "string",
"description": "A library that is linked to this library. Should be of the form `namespace/name`.",
"pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*/[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$"
}
}
}
}

+ 2
- 2
res/package-schema.json View File

"properties": { "properties": {
"name": { "name": {
"type": "string", "type": "string",
"description": "The name of the package. Must be a valid Semantic Version string.",
"description": "The name of the package",
"pattern": "^[a-z][a-z0-9_]*((\\.|-)[a-z0-9_]+)*$" "pattern": "^[a-z][a-z0-9_]*((\\.|-)[a-z0-9_]+)*$"
}, },
"version": { "version": {
"type": "string", "type": "string",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$",
"description": "The version of the package. Required",
"description": "The version of the package. Must be a valid Semantic Version string.",
"default": "0.1.0" "default": "0.1.0"
}, },
"namespace": { "namespace": {

+ 13
- 8
src/dds/error/errors.cpp View File

return "sdist-ident-mismatch.html"; return "sdist-ident-mismatch.html";
case errc::corrupted_build_db: case errc::corrupted_build_db:
return "corrupted-build-db.html"; return "corrupted-build-db.html";
case errc::invalid_lib_manifest:
return "invalid-lib-manifest.html";
case errc::invalid_pkg_manifest: case errc::invalid_pkg_manifest:
return "invalid-pkg-manifest.html"; return "invalid-pkg-manifest.html";
case errc::invalid_version_range_string: case errc::invalid_version_range_string:
The catalog database schema doesn't match what dds expects. This indicates that The catalog database schema doesn't match what dds expects. This indicates that
the database file has been modified in a way that dds cannot automatically fix the database file has been modified in a way that dds cannot automatically fix
and handle. and handle.
)";
case errc::invalid_lib_manifest:
return R"(
A library manifest is malformed Refer to the documentation and above error
message for more details.
)"; )";
case errc::invalid_pkg_manifest: case errc::invalid_pkg_manifest:
return R"( return R"(
"that was expected of it"; "that was expected of it";
case errc::corrupted_build_db: case errc::corrupted_build_db:
return "The build database file is corrupted"; return "The build database file is corrupted";
case errc::invalid_lib_manifest:
return "The library manifest is invalid";
case errc::invalid_pkg_manifest: case errc::invalid_pkg_manifest:
return "The package manifest is invalid"; return "The package manifest is invalid";
case errc::invalid_version_range_string: case errc::invalid_version_range_string:
"`dds` bug. Please report it.)"; "`dds` bug. Please report it.)";
case errc::invalid_version_string: case errc::invalid_version_string:
return "Attempted to parse an invalid version string. <- (Seeing this text is a `dds` " return "Attempted to parse an invalid version string. <- (Seeing this text is a `dds` "
"bug. "
"Please report it.)";
"bug. Please report it.)";
case errc::invalid_config_key: case errc::invalid_config_key:
return "Found an invalid configuration key. <- (Seeing this text is a `dds` bug. " return "Found an invalid configuration key. <- (Seeing this text is a `dds` bug. "
"Please "
"report it.)";
"Please report it.)";
case errc::invalid_lib_filesystem: case errc::invalid_lib_filesystem:
case errc::invalid_pkg_filesystem: case errc::invalid_pkg_filesystem:
return "The filesystem structure of the package/library is invalid. <- (Seeing this " return "The filesystem structure of the package/library is invalid. <- (Seeing this "
"text "
"is a `dds` bug. Please report it.)";
"text is a `dds` bug. Please report it.)";
case errc::invalid_pkg_id: case errc::invalid_pkg_id:
return "A package identifier is invalid <- (Seeing this text is a `dds` bug. Please " return "A package identifier is invalid <- (Seeing this text is a `dds` bug. Please "
"report it.)"; "report it.)";
"it.)"; "it.)";
case errc::sdist_exists: case errc::sdist_exists:
return "The source ditsribution already exists at the destination <- (Seeing this " return "The source ditsribution already exists at the destination <- (Seeing this "
"text is "
"a `dds` bug. Please report it.)";
"text is a `dds` bug. Please report it.)";
case errc::unknown_test_driver: case errc::unknown_test_driver:
return "The specified Test-Driver is not known to `dds`"; return "The specified Test-Driver is not known to `dds`";
case errc::dependency_resolve_failure: case errc::dependency_resolve_failure:

+ 1
- 0
src/dds/error/errors.hpp View File



corrupted_build_db, corrupted_build_db,


invalid_lib_manifest,
invalid_pkg_manifest, invalid_pkg_manifest,
invalid_version_range_string, invalid_version_range_string,
invalid_version_string, invalid_version_string,

+ 79
- 4
src/dds/library/manifest.cpp View File

#include "./manifest.hpp" #include "./manifest.hpp"


#include <dds/dym.hpp> #include <dds/dym.hpp>
#include <dds/error/errors.hpp>
#include <dds/util/algo.hpp> #include <dds/util/algo.hpp>
#include <range/v3/view/transform.hpp>

#include <dds/util/json5_read.hpp>
#include <libman/parse.hpp> #include <libman/parse.hpp>


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


using namespace dds; using namespace dds;


library_manifest library_manifest::load_from_file(const fs::path& fpath) {
library_manifest library_manifest::load_from_dds_file(path_ref fpath) {
spdlog::warn(
"Using deprecated library.dds parsing (on file {}). This will be removed soon. Migrate!",
fpath.string());
auto kvs = lm::parse_file(fpath); auto kvs = lm::parse_file(fpath);
library_manifest ret; library_manifest ret;
ret.name = fpath.parent_path().filename().string(); ret.name = fpath.parent_path().filename().string();
extend(ret.links, ranges::views::transform(links_strings, lm::split_usage_string)); extend(ret.links, ranges::views::transform(links_strings, lm::split_usage_string));
return ret; return ret;
} }

library_manifest library_manifest::load_from_file(path_ref fpath) {
auto content = slurp_file(fpath);
auto data = json5::parse_data(content);

if (!data.is_object()) {
throw_user_error<errc::invalid_lib_manifest>("Root value must be an object");
}

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")})));

if (lib.name.empty()) {
throw_user_error<errc::invalid_lib_manifest>(
"The 'name' field is required (Reading library manifest [{}])", fpath.string());
}

return lib;
}

std::optional<fs::path> library_manifest::find_in_directory(path_ref dirpath) {
auto fnames = {
"library.json5",
"library.jsonc",
"library.json",
};
for (auto c : fnames) {
auto cand = dirpath / c;
if (fs::is_regular_file(cand)) {
return cand;
}
}

auto dds_file = dirpath / "library.dds";
if (fs::is_regular_file(dds_file)) {
return dds_file;
}

return std::nullopt;
}

std::optional<library_manifest> library_manifest::load_from_directory(path_ref dirpath) {
auto found = find_in_directory(dirpath);
if (!found.has_value()) {
return std::nullopt;
}

if (found->extension() == ".dds") {
return load_from_dds_file(*found);
} else {
return load_from_file(*found);
}
}

+ 10
- 1
src/dds/library/manifest.hpp View File

/** /**
* Load the library manifest from an existing file * Load the library manifest from an existing file
*/ */
static library_manifest load_from_file(const fs::path&);
static library_manifest load_from_file(path_ref);
static library_manifest load_from_dds_file(path_ref);

/**
* Find a library manifest within a directory. This will search for a few
* file candidates and return the result from the first matching. If none
* match, it will return nullopt.
*/
static std::optional<fs::path> find_in_directory(path_ref);
static std::optional<library_manifest> load_from_directory(path_ref);
}; };


} // namespace dds } // namespace dds

+ 8
- 4
src/dds/library/root.cpp View File

auto sources = collect_pf_sources(lib_dir); auto sources = collect_pf_sources(lib_dir);


library_manifest man; library_manifest man;
man.name = lib_dir.filename().string();
auto man_path = lib_dir / "library.dds";
if (fs::is_regular_file(man_path)) {
man = library_manifest::load_from_file(man_path);
man.name = lib_dir.filename().string();
auto found = library_manifest::find_in_directory(lib_dir);
if (found) {
if (found->extension() == ".dds") {
man = library_manifest::load_from_dds_file(*found);
} else {
man = library_manifest::load_from_file(*found);
}
} }


auto lib = library_root(lib_dir, std::move(sources), std::move(man)); auto lib = library_root(lib_dir, std::move(sources), std::move(man));

+ 5
- 5
src/dds/package/manifest.cpp View File

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


using namespace j5_read::ops;
j5_read::destructure(
using namespace json_read::ops;
json_read::decompose(
obj, obj,
object( object(
key("name", require_string(put_into{ret.pkg_id.name}, "`name` must be a string")), key("name", require_string(put_into{ret.pkg_id.name}, "`name` must be a string")),
[&](auto&& version_str_) { [&](auto&& version_str_) {
auto& version = version_str_.as_string(); auto& version = version_str_.as_string();
ret.pkg_id.version = semver::version::parse(version); ret.pkg_id.version = semver::version::parse(version);
return j5_read::accept_t{};
return json_read::accept_t{};
}, },
"`version` must be a string")), "`version` must be a string")),
key("depends", object([&](auto key, auto&& range_str_) { key("depends", object([&](auto key, auto&& range_str_) {
range_str_.as_string(), range_str_.as_string(),
pkg_name); pkg_name);
} }
return j5_read::accept_t{};
return json_read::accept_t{};
})), })),
key("test_driver", key("test_driver",
require_string( require_string(
test_driver, test_driver,
dym); dym);
} }
return j5_read::accept_t{};
return json_read::accept_t{};
}, },
"`test_driver` must be a valid test driver name string")), "`test_driver` must be a valid test driver name string")),
reject_key)); reject_key));

+ 23
- 8
src/dds/util/json5_read.hpp View File



namespace dds { namespace dds {


namespace j5_read {
namespace json_read {


struct reject_t { struct reject_t {
std::string message; std::string message;
} }
}; };


template <typename... Hs>
then(Hs...) -> then<Hs...>;

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


template <typename... Ks>
object(Ks...) -> object<Ks...>;
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> template <typename Handler>
struct key { struct key {
} // namespace ops } // namespace ops


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


} // namespace j5_read
} // namespace json_read


} // namespace dds } // namespace dds

Loading…
Cancel
Save