Mercurial > malikania
changeset 194:61dd98874d82
Common: remove util and import json_util, closes #938 @2h
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 28 Oct 2018 10:01:44 +0100 |
parents | 209bdaa13a49 |
children | ae3741be8c38 |
files | libmlk-client/malikania/client/loader.cpp libmlk-client/malikania/client/loader.hpp libmlk-server/malikania/server/net/auth_handler.cpp libmlk-server/malikania/server/server.cpp libmlk/CMakeLists.txt libmlk/malikania/json_util.cpp libmlk/malikania/json_util.hpp libmlk/malikania/loader.cpp libmlk/malikania/loader.hpp libmlk/malikania/util.cpp libmlk/malikania/util.hpp tests/libmlk/CMakeLists.txt tests/libmlk/util/CMakeLists.txt tests/libmlk/util/main.cpp tests/tools/CMakeLists.txt tools/tileset/main.cpp |
diffstat | 16 files changed, 585 insertions(+), 1338 deletions(-) [+] |
line wrap: on
line diff
--- a/libmlk-client/malikania/client/loader.cpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk-client/malikania/client/loader.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -1,5 +1,5 @@ /* - * client_resources_loader.cpp -- load shared resources files for the client + * loader.cpp -- load shared resources files for the client * * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> * @@ -20,7 +20,6 @@ #include <malikania/locator.hpp> #include <malikania/size.hpp> -#include <malikania/util.hpp> #include "animation.hpp" #include "font.hpp" @@ -28,7 +27,9 @@ #include "loader.hpp" #include "sprite.hpp" -using namespace nlohmann; +using nlohmann::json; + +using mlk::json_util::deserializer; namespace mlk::client { @@ -39,19 +40,21 @@ animation::frame_list frames; for (const auto& v : value) { + const deserializer doc(v); + if (!v.is_object()) throw std::runtime_error("not a JSON object"); - const auto cell = v.find("cell"); - const auto delay = v.find("delay"); + const auto cell = doc.get<unsigned>("cell"); + const auto delay = doc.get<unsigned>("delay"); - if (cell == v.end() || !cell->is_number_unsigned()) - throw std::runtime_error("invalid cell"); - if (delay == v.end() || !delay->is_number_unsigned()) + if (!cell) + throw std::runtime_error("invalid cell property in animation object"); + if (!delay) throw std::runtime_error("invalid delay"); // TODO: range check. - frames.push_back({cell->get<unsigned>(), delay->get<unsigned>()}); + frames.push_back({*cell, *delay}); } return frames; @@ -76,33 +79,47 @@ auto loader::load_sprite(std::string_view id) -> sprite { - auto value = json::parse(get_locator().read(id)); + const deserializer doc(json::parse(get_locator().read(id))); - if (!value.is_object()) + if (!doc.is_object()) throw std::runtime_error(": not a JSON object"); - return sprite( - load_image(util::json::require_string(value, "/image"_json_pointer)), - util::json::require_size(value, "/cell"_json_pointer), - util::json::get_size(value, "/margin"_json_pointer), - util::json::get_size(value, "/space"_json_pointer), - util::json::get_size(value, "/size"_json_pointer) - ); + const auto image = doc.get<std::string>("image"); + const auto cell = doc.get<size>("cell"); + const auto margin = doc.optional<size>("margin", size{}); + const auto space = doc.optional<size>("space", size{}); + const auto geom = doc.optional<size>("size", size{}); + + if (!image) + throw std::runtime_error("invalid image property in sprite object"); + if (!cell) + throw std::runtime_error("invalid cell property in sprite object"); + if (!margin) + throw std::runtime_error("invalid margin property in sprite object"); + if (!space) + throw std::runtime_error("invalid space property in sprite object"); + if (!geom) + throw std::runtime_error("invalid size property in sprite object"); + + return sprite{load_image(*image), *cell, *margin, *space, *geom}; } auto loader::load_animation(std::string_view id) -> animation { - const auto value = json::parse(get_locator().read(id)); + const deserializer doc(json::parse(get_locator().read(id))); - if (!value.is_object()) + if (!doc.is_object()) throw std::runtime_error("not a JSON object"); - const auto s = load_sprite(util::json::require_string(value, "/sprite"_json_pointer)); + const auto sprite = doc.get<std::string>("sprite"); + const auto frames = doc.find("frames"); - return { - load_sprite(util::json::require_string(value, "/sprite"_json_pointer)), - load_animation_frames(util::json::require_array(value, "/frames"_json_pointer)) - }; + if (!sprite) + throw std::runtime_error("invalid sprite property in animation object"); + if (!frames->is_array()) + throw std::runtime_error("invalid frames property in animation object"); + + return animation{load_sprite(*sprite), load_animation_frames(*frames)}; } } // !mlk::client
--- a/libmlk-client/malikania/client/loader.hpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk-client/malikania/client/loader.hpp Sun Oct 28 10:01:44 2018 +0100 @@ -1,5 +1,5 @@ /* - * client_resources_loader.hpp -- load shared resources files for the client + * loader.hpp -- load shared resources files for the client * * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> * @@ -26,13 +26,7 @@ #include <malikania/loader.hpp> -#include <string> - -namespace mlk { - -struct size; - -namespace client { +namespace mlk::client { struct animation; @@ -92,8 +86,6 @@ virtual auto load_animation(std::string_view id) -> animation; }; -} // !client - -} // !mlk +} // !mlk::client #endif // !MALIKANIA_CLIENT_LOADER_HPP
--- a/libmlk-server/malikania/server/net/auth_handler.cpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk-server/malikania/server/net/auth_handler.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <malikania/util.hpp> +#include <malikania/json_util.hpp> #include <malikania/error/auth_error.hpp> @@ -26,6 +26,8 @@ #include "client.hpp" #include "server.hpp" +using mlk::json_util::deserializer; + namespace mlk::server { namespace { @@ -47,10 +49,16 @@ void auth_handler::exec(server& server, client& clt, nlohmann::json object) noexcept { - auto ac = db::account::authenticate( - util::json::require_string(object, "/login"_json_pointer), - util::json::require_string(object, "/password"_json_pointer) - ); + const deserializer doc(std::move(object)); + const auto login = doc.get<std::string>("login"); + const auto password = doc.get<std::string>("password"); + + if (!login || !password) { + clt.fatal("auth", auth_error::invalid_credentials); + return; + } + + auto ac = db::account::authenticate(*login, *password); if (!ac) clt.fatal("auth", auth_error::invalid_credentials);
--- a/libmlk-server/malikania/server/server.cpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk-server/malikania/server/server.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -16,8 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <malikania/util.hpp> - #include "client.hpp" #include "server.hpp"
--- a/libmlk/CMakeLists.txt Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk/CMakeLists.txt Sun Oct 28 10:01:44 2018 +0100 @@ -29,13 +29,13 @@ ${CMAKE_CURRENT_SOURCE_DIR}/malikania/line.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/loader.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/locator.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/malikania/json_util.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/point.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/rectangle.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/size.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/socket.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/tileset.hpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/unicode.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/malikania/util.hpp ) set( @@ -43,9 +43,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/malikania/error/auth_error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/locator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/malikania/json_util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/socket.cpp ${CMAKE_CURRENT_SOURCE_DIR}/malikania/unicode.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/malikania/util.cpp ) malikania_define_library(
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk/malikania/json_util.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -0,0 +1,154 @@ +/* + * json_util.cpp -- utilities for JSON + * + * Copyright (c) 2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <limits> +#include <type_traits> + +#include "json_util.hpp" + +using nlohmann::json; + +namespace mlk::json_util { + +namespace { + +template <typename Int> +auto clampi(const json& value) noexcept -> std::optional<Int> +{ + static_assert(std::is_signed<Int>::value, "Int must be signed"); + + if (!value.is_number_integer()) + return std::nullopt; + + const auto ret = value.get<std::int64_t>(); + + if (ret < std::numeric_limits<Int>::min() || ret > std::numeric_limits<Int>::max()) + return std::nullopt; + + return static_cast<Int>(ret); +} + +template <typename Int> +auto clampu(const json& value) noexcept -> std::optional<Int> +{ + static_assert(std::is_unsigned<Int>::value, "Int must be unsigned"); + + if (!value.is_number_unsigned()) + return std::nullopt; + + const auto ret = value.get<std::uint64_t>(); + + if (ret > std::numeric_limits<Int>::max()) + return std::nullopt; + + return static_cast<Int>(ret); +} + +} // !namespace + +auto type_traits<bool>::get(const json& value) noexcept -> std::optional<bool> +{ + if (!value.is_boolean()) + return std::nullopt; + + return value.get<bool>(); +} + +auto type_traits<double>::get(const json& value) noexcept -> std::optional<double> +{ + if (!value.is_number_float()) + return std::nullopt; + + return value.get<double>(); +} + +auto type_traits<std::string>::get(const json& value) -> std::optional<std::string> +{ + if (!value.is_string()) + return std::nullopt; + + return value.get<std::string>(); +} + +auto type_traits<std::int8_t>::get(const json& value) -> std::optional<std::int8_t> +{ + return clampi<std::int8_t>(value); +} + +auto type_traits<std::int16_t>::get(const json& value) -> std::optional<std::int16_t> +{ + return clampi<std::int16_t>(value); +} + +auto type_traits<std::int32_t>::get(const json& value) -> std::optional<std::int32_t> +{ + return clampi<std::int32_t>(value); +} + +auto type_traits<std::int64_t>::get(const json& value) noexcept -> std::optional<std::int64_t> +{ + if (!value.is_number_integer()) + return std::nullopt; + + return value.get<std::int64_t>(); +} + +auto type_traits<std::uint8_t>::get(const json& value) -> std::optional<std::uint8_t> +{ + return clampu<std::uint8_t>(value); +} + +auto type_traits<std::uint16_t>::get(const json& value) -> std::optional<std::uint16_t> +{ + return clampu<std::uint16_t>(value); +} + +auto type_traits<std::uint32_t>::get(const json& value) -> std::optional<std::uint32_t> +{ + return clampu<std::uint32_t>(value); +} + +auto type_traits<std::uint64_t>::get(const json& value) noexcept -> std::optional<std::uint64_t> +{ + if (!value.is_number_unsigned()) + return std::nullopt; + + return value.get<std::uint64_t>(); +} + +auto pretty(const json& value, int indent) -> std::string +{ + switch (value.type()) { + case json::value_t::null: + return "null"; + case json::value_t::string: + return value.get<std::string>(); + case json::value_t::boolean: + return value.get<bool>() ? "true" : "false"; + case json::value_t::number_integer: + return std::to_string(value.get<std::int64_t>()); + case json::value_t::number_unsigned: + return std::to_string(value.get<std::uint64_t>()); + case json::value_t::number_float: + return std::to_string(value.get<double>()); + default: + return value.dump(indent); + } +} + +} // !mlk::json_util
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk/malikania/json_util.hpp Sun Oct 28 10:01:44 2018 +0100 @@ -0,0 +1,284 @@ +/* + * json_util.hpp -- utilities for JSON + * + * Copyright (c) 2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_JSON_UTIL_HPP +#define MALIKANIA_JSON_UTIL_HPP + +/** + * \file json_util.hpp + * \brief Utilities for JSON. + */ + +#include <cstdint> +#include <optional> +#include <string> + +#include <json.hpp> + +/** + * \brief Utilities for JSON. + */ +namespace mlk::json_util { + +/** + * \brief Describe how to convert a JSON value. + * + * This traits must be specialized for every type you want to convert from JSON + * to its native type. + * + * You only need to implement the get function with the following signature: + * + * ```cpp + * static std::optional<T> get(const nlohmann::json& value); + * ``` + * + * The implementation should not throw an exception but return a null optional + * instead. + * + * This traits is already specialized for the given types: + * + * - bool + * - double + * - std::uint(8, 16, 32, 64)_t + * - std::string + */ +template <typename T> +struct type_traits; + +/** + * \brief Specialization for `bool`. + */ +template <> +struct type_traits<bool> { + /** + * Convert the JSON value to bool. + * + * \param value the value + * \return the bool or empty if not a boolean type + */ + static auto get(const nlohmann::json& value) noexcept -> std::optional<bool>; +}; + +/** + * \brief Specialization for `double`. + */ +template <> +struct type_traits<double> { + /** + * Convert the JSON value to bool. + * + * \param value the value + * \return the double or empty if not a double type + */ + static auto get(const nlohmann::json& value) noexcept -> std::optional<double>; +}; + +/** + * \brief Specialization for `std::string`. + */ +template <> +struct type_traits<std::string> { + /** + * Convert the JSON value to std::string. + * + * \param value the value + * \return the string or empty if not a string type + */ + static auto get(const nlohmann::json& value) -> std::optional<std::string>; +}; + +/** + * \brief Specialization for `std::int8_t`. + */ +template <> +struct type_traits<std::int8_t> { + /** + * Convert the JSON value to std::int8_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::int8_t>; +}; + +/** + * \brief Specialization for `std::int16_t`. + */ +template <> +struct type_traits<std::int16_t> { + /** + * Convert the JSON value to std::int16_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::int16_t>; +}; + +/** + * \brief Specialization for `std::int32_t`. + */ +template <> +struct type_traits<std::int32_t> { + /** + * Convert the JSON value to std::int32_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::int32_t>; +}; + +/** + * \brief Specialization for `std::int64_t`. + */ +template <> +struct type_traits<std::int64_t> { + /** + * Convert the JSON value to std::int64_t. + * + * \param value the value + * \return the int or empty if not a int type + */ + static auto get(const nlohmann::json& value) noexcept -> std::optional<std::int64_t>; +}; + +/** + * \brief Specialization for `std::uint8_t`. + */ +template <> +struct type_traits<std::uint8_t> { + /** + * Convert the JSON value to std::uint8_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::uint8_t>; +}; + +/** + * \brief Specialization for `std::uint16_t`. + */ +template <> +struct type_traits<std::uint16_t> { + /** + * Convert the JSON value to std::uint16_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::uint16_t>; +}; + +/** + * \brief Specialization for `std::int32_t`. + */ +template <> +struct type_traits<std::uint32_t> { + /** + * Convert the JSON value to std::uint32_t. + * + * \param value the value + * \return the value or empty if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional<std::uint32_t>; +}; + +/** + * \brief Specialization for `std::uint64_t`. + */ +template <> +struct type_traits<std::uint64_t> { + /** + * Convert the JSON value to std::uint64_t. + * + * \param value the value + * \return the int or empty if not a int type + */ + static auto get(const nlohmann::json& value) noexcept -> std::optional<std::uint64_t>; +}; + +/** + * \brief Convenient JSON object parser + * + * This class helps destructuring insecure JSON input by returning optional + * values if they are not present or invalid. + */ +class deserializer : public nlohmann::json { +public: + /** + * Inherited constructor. + */ + deserializer(nlohmann::json value) noexcept + : nlohmann::json(std::move(value)) + { + } + + /** + * Get a value from the document object. + * + * \param key the property key + * \return the value or std::nullopt if not found or not convertible + */ + template <typename Type> + auto get(const std::string& key) const noexcept -> std::optional<Type> + { + const auto it = find(key); + + if (it == end()) + return std::nullopt; + + return type_traits<Type>::get(*it); + } + + /** + * Get an optional value from the document object. + * + * If the value is undefined, the default value is returned. Otherwise, if + * the value is not in the given type, std::nullopt is returned. + * + * \param key the property key + * \param def the default value if property is undefined + * \return the value, std::nullopt or def + */ + template <typename Type, typename DefaultValue> + auto optional(const std::string& key, DefaultValue&& def) const noexcept -> std::optional<Type> + { + const auto it = find(key); + + if (it == end()) + return std::optional<Type>(std::forward<DefaultValue>(def)); + + return type_traits<Type>::get(*it); + } +}; + +/** + * Print the value as human readable. + * + * \note This only works on flat objects. + * \param value the value + * \param indent the optional indent for objects/arrays + * \return the string + */ +auto pretty(const nlohmann::json& value, int indent = 4) -> std::string; + +} // !mlk::json_util + +#endif // !JSON_UTIL_HPP
--- a/libmlk/malikania/loader.cpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk/malikania/loader.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -17,16 +17,24 @@ */ #include "game.hpp" +#include "json_util.hpp" #include "loader.hpp" #include "locator.hpp" #include "tileset.hpp" -#include "util.hpp" + +using nlohmann::json; namespace mlk { +using json_util::deserializer; + namespace { -auto load_tileset_tile_properties(const nlohmann::json& json) -> tileset::properties_map +#if 0 + +// TODO: rework tileset. + +auto load_tileset_tile_properties(const json& json) -> tileset::properties_map { auto it = json.find("properties"); @@ -46,7 +54,7 @@ return props; } -auto load_tileset_tiles_properties(const nlohmann::json& json) -> tileset::tile_properties_map +auto load_tileset_tiles_properties(const json& json) -> tileset::tile_properties_map { auto tiles = json.find("tiles"); @@ -61,6 +69,8 @@ return props; } +#endif + } // !namespace loader::loader(mlk::locator& locator) @@ -75,34 +85,63 @@ auto loader::load_game() const -> game { - auto value = nlohmann::json::parse(locator_.read("game.json")); + const deserializer doc(json::parse(locator_.read("game.json"))); + + if (!doc.is_object()) + throw std::runtime_error("not a JSON object"); - if (!value.is_object()) - throw std::runtime_error("game.json: not a JSON object"); + const auto name = doc.get<std::string>("name"); + const auto version = doc.get<std::string>("version"); + const auto requires = doc.get<std::string>("requires"); + const auto license = doc.optional<std::string>("license", ""); + const auto author = doc.optional<std::string>("author", ""); - return game{ - util::json::require_string(value, "/name"_json_pointer), - util::json::require_string(value, "/version"_json_pointer), - util::json::require_string(value, "/requires"_json_pointer), - value.count("license") > 0 ? value["license"] : "", - value.count("author") > 0 ? value["author"] : "" - }; + if (!name) + throw std::runtime_error("invalid name property in game object"); + if (!version) + throw std::runtime_error("invalid version property in game object"); + if (!requires) + throw std::runtime_error("invalid requires property in game object"); + if (!license) + throw std::runtime_error("invalid license property in game object"); + if (!author) + throw std::runtime_error("invalid author property in game object"); + + return game{*name, *version, *requires, *license, *author}; } auto loader::load_tileset(std::string_view id) const -> tileset { - auto value = nlohmann::json::parse(locator_.read(id)); + const deserializer doc(json::parse(locator_.read(id))); - if (!value.is_object()) + if (!doc.is_object()) throw std::runtime_error("not a valid tileset"); - tileset tileset; + const auto image = doc.get<std::string>("image"); + const auto cell = doc.get<size>("cell"); - tileset.image = util::json::require_string(value, "/image"_json_pointer); - tileset.cell = util::json::require_size(value, "/cell"_json_pointer); - tileset.tile_properties = load_tileset_tiles_properties(value); + if (!image) + throw std::runtime_error("invalid image property in tileset object"); + if (!cell) + throw std::runtime_error("invalid cell property in tileset object"); - return tileset; + return tileset{*image, *cell, {}, {}}; } +namespace json_util { + +auto type_traits<size>::get(const json& value) noexcept -> std::optional<size> +{ + const deserializer doc(value); + const auto width = doc.get<unsigned>("width"); + const auto height = doc.get<unsigned>("height"); + + if (!width || !height) + return std::nullopt; + + return size({*width, *height}); +} + +} // !json_util + } // !mlk
--- a/libmlk/malikania/loader.hpp Sat Oct 27 07:16:28 2018 +0200 +++ b/libmlk/malikania/loader.hpp Sun Oct 28 10:01:44 2018 +0100 @@ -21,6 +21,10 @@ #include <string> +#include <malikania/json_util.hpp> + +#include "size.hpp" + namespace mlk { struct game; @@ -78,6 +82,21 @@ virtual auto load_tileset(std::string_view id) const -> tileset; }; +namespace json_util { + +template <> +struct type_traits<size> { + /** + * Convert the JSON value to std::uint64_t. + * + * \param value the value + * \return the int or empty if not a int type + */ + static auto get(const nlohmann::json& value) noexcept -> std::optional<size>; +}; + +} // !json_util + } // !mlk #endif // !MALIKANIA_LOADER_HPP
--- a/libmlk/malikania/util.cpp Sat Oct 27 07:16:28 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,443 +0,0 @@ -/* - * util.cpp -- malikania utilities - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cassert> -#include <climits> -#include <string> -#include <stdexcept> - -#if defined(__linux__) -# include <unistd.h> - -# include <cerrno> -# include <cstring> -# include <stdexcept> -#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) -# if defined(__NetBSD__) -# include <sys/param.h> -# else -# include <sys/types.h> -# endif - -# if defined(__OpenBSD__) -# include <unistd.h> -# endif - -# include <sys/sysctl.h> - -# include <array> -# include <cerrno> -# include <climits> -# include <cstddef> -# include <cstdlib> -# include <cstring> -# include <stdexcept> -#elif defined(__APPLE__) -# include <cerrno> -# include <cstring> -# include <libproc.h> -# include <unistd.h> -#elif defined(_WIN32) -# include <Windows.h> -#endif - -#include <boost/filesystem.hpp> - -#include "util.hpp" -#include "size.hpp" - -namespace { - -#if defined(__linux__) - -std::string executable_path() -{ - std::string result; - - result.resize(2048, '\0'); - - auto size = readlink("/proc/self/exe", &result[0], 2048); - - if (size < 0) { - throw std::runtime_error(std::strerror(errno)); - } - - result.resize(size, '\0'); - - return result; -} - -#elif defined(__FreeBSD__) || defined(__DragonFly__) - -std::string executable_path() -{ - std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } }; - std::string result; - std::size_t size = PATH_MAX + 1; - - result.resize(size, '\0'); - - if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) { - throw std::runtime_error(std::strerror(errno)); - } - - result.resize(size, '\0'); - - return result; -} - -#elif defined(__APPLE__) - -std::string executable_path() -{ - std::string result; - std::size_t size = PROC_PIDPATHINFO_MAXSIZE; - - result.resize(size, '\0'); - - if ((size = proc_pidpath(getpid(), &result[0], size)) == 0) { - throw std::runtime_error(std::strerror(errno)); - } - - result.resize(size, '\0'); - - return result; -} - -#elif defined(_WIN32) - -std::string executable_path() -{ - std::string result; - std::size_t size = PATH_MAX; - - result.resize(size, '\0'); - - if (!(size = GetModuleFileNameA(nullptr, &result[0], size))) { - throw std::runtime_error("GetModuleFileName error"); - } - - result.resize(size, '\0'); - - return result; -} - -#elif defined(__NetBSD__) - -std::string executable_path() -{ - std::string result; - -#if defined(KERN_PROC_PATHNAME) - std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; - std::size_t size = MAXPATHLEN; - - result.resize(size, '\0'); - - if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) { - throw std::runtime_error(std::strerror(errno)); - } - - result.resize(size, '\0'); -#else - result.resize(2048, '\0'); - - auto size = readlink("/proc/curproc/exe", &result[0], 2048); - - if (size < 0) { - throw std::runtime_error(std::strerror(errno)); - } - - result.resize(size, '\0'); -#endif - - return result; -} - -#elif defined(__OpenBSD__) - -std::string executable_path() -{ - char **paths, *path; - std::string result; - std::size_t len; - std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; - - if (sysctl(mib.data(), 4, nullptr, &len, nullptr, 0) == -1) { - throw std::runtime_error(std::strerror(errno)); - } - if ((paths = static_cast<char **>(malloc(len))) == nullptr) { - throw std::runtime_error(std::strerror(errno)); - } - - sysctl(mib.data(), 4, paths, &len, nullptr, 0); - - if ((path = static_cast<char *>(std::malloc(PATH_MAX + 1))) == nullptr) { - std::free(paths); - throw std::runtime_error(std::strerror(errno)); - } - - realpath(paths[0], path); - result = path; - - std::free(paths); - std::free(path); - - return result; -} - -#else - -std::string executable_path() -{ - // Not supported. - return ""; -} - -#endif - -} // !namespace - -namespace mlk { - -namespace util { - -std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max) -{ - std::vector<std::string> result; - std::size_t next = -1, current; - int count = 1; - bool finished = false; - - if (list.empty()) { - return result; - } - - do { - std::string val; - - current = next + 1; - next = list.find_first_of(delimiters, current); - - // split max, get until the end - if (max >= 0 && count++ >= max) { - val = list.substr(current, std::string::npos); - finished = true; - } else { - val = list.substr(current, next - current); - finished = next == std::string::npos; - } - - result.push_back(val); - } while (!finished); - - return result; -} - -std::string basedir() noexcept -{ - std::string result = "./"; - - try { - boost::filesystem::path tmp = executable_path(); - - tmp = tmp.parent_path(); // remove executable name - tmp = tmp.parent_path(); // remove "bin" - result = tmp.string(); - } catch (...) { - // TODO: add log. - } - - return result; -} - -/* - * json utilities - * ------------------------------------------------------------------ - */ - -namespace json { - -/* - * XXX: until json get a non-throwing function to check if a pointer exists, - * use this to check for a value at the given pointer or throw a - * property_error. - */ -nlohmann::json require(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - const nlohmann::json::value_t expected) -{ - assert(object.is_object()); - - nlohmann::json value; - - try { - value = object.at(pointer); - } catch (...) { - throw property_error(object, pointer, nlohmann::json::value_t::null, expected); - } - - if (value.type() != expected) { - throw property_error(object, pointer, value.type(), expected); - } - - return value; -} - -nlohmann::json get(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - const nlohmann::json::value_t expected, - const nlohmann::json def) -{ - assert(object.is_object()); - - nlohmann::json value; - - try { - value = object.at(pointer); - } catch (...) { - } - - if (value.type() != expected) - value = def; - - return value; -} - -nlohmann::json require_array(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - return require(object, pointer, nlohmann::json::value_t::array); -} - -bool require_bool(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - return require(object, pointer, nlohmann::json::value_t::boolean); -} - -int require_int(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - nlohmann::json v; - - try { - v = object.at(pointer); - - if (v.is_number_integer()) - return v.get<int>(); - else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX)) - return static_cast<int>(v.get<unsigned>()); - } catch (...) { - } - - throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_integer); -} - -nlohmann::json require_object(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - return require(object, pointer, nlohmann::json::value_t::object); -} - -std::string require_string(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - return require(object, pointer, nlohmann::json::value_t::string); -} - -unsigned require_uint(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer) -{ - nlohmann::json v; - - try { - v = object.at(pointer); - - if (v.is_number_unsigned()) - return v.get<unsigned>(); - else if (v.is_number_integer() && v.get<int>() >= 0) - return static_cast<unsigned>(v.get<int>()); - } catch (...) { - } - - throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_unsigned); -} - -mlk::size require_size(const nlohmann::json& object, - const nlohmann::json::json_pointer& index) -{ - using pointer = nlohmann::json::json_pointer; - - return { - require_uint(object, pointer(index.to_string() + "/width")), - require_uint(object, pointer(index.to_string() + "/height")) - }; -} - -int get_int(const nlohmann::json &object, - const nlohmann::json::json_pointer &pointer, - int value) noexcept -{ - try { - auto v = object.at(pointer); - - if (v.is_number_integer()) - value = v.get<int>(); - else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX)) - value = static_cast<int>(v.get<unsigned>()); - } catch (...) { - } - - return value; -} - -unsigned get_uint(const nlohmann::json &object, - const nlohmann::json::json_pointer& pointer, - unsigned value) noexcept -{ - try { - auto v = object.at(pointer); - - if (v.is_number_unsigned()) - value = v.get<unsigned>(); - else if (v.is_number_integer() && v.get<int>() >= 0) - value = static_cast<unsigned>(v.get<int>()); - } catch (...) { - } - - return value; -} - -mlk::size get_size(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - const mlk::size& def) noexcept -{ - using json_pointer = nlohmann::json::json_pointer; - - return { - get_uint(object, json_pointer(pointer.to_string() + "/width"), def.width), - get_uint(object, json_pointer(pointer.to_string() + "/height"), def.height) - }; -} - -} // !json - -} // !util - -} // !mlk
--- a/libmlk/malikania/util.hpp Sat Oct 27 07:16:28 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,333 +0,0 @@ -/* - * util.hpp -- malikania utilities - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_UTIL_HPP -#define MALIKANIA_UTIL_HPP - -/** - * \file util.hpp - * \brief Some utilities - */ - -#include <algorithm> -#include <exception> -#include <string> -#include <vector> - -#include "json.hpp" -#include "size.hpp" - -namespace mlk { - -/** - * \brief Utilities. - */ -namespace util { - -/** - * Split a line into a list of tokens. - * - * If max is less than 0, split as much as possible, otherwise split at most - * the number of specified tokens. - * - * \param line the line to split - * \param delim the list of delimiters allowed - * \param max the maximum number of tokens - * \return the list - */ -std::vector<std::string> split(const std::string& line, const std::string& delim, int max = -1); - -/** - * Get the base directory of the current executable. - * - * \return the base directory or empty if impossible to get - */ -std::string basedir() noexcept; - -/** - * Clamp the value between low and high. - * - * \param value the value - * \param low the minimum value - * \param high the maximum value - * \return the value between minimum and maximum - */ -template <typename T> -constexpr T clamp(T value, T low, T high) noexcept -{ - return (value < high) ? std::max(value, low) : std::min(value, high); -} - -/** - * \brief Utilities for networking. - */ -namespace net { - -/** - * Shortcut for util::split(line, " ", max); - * - * \param line the line - * \param max the maximum of tokens to split - * \return the list - */ -inline std::vector<std::string> split(const std::string& line, int max = -1) -{ - return util::split(line, " ", max); -} - -} // !net - -namespace json { - -/** - * \brief Indicates a property error in an object. - */ -class property_error : public std::exception { -private: - nlohmann::json m_object; - nlohmann::json::json_pointer m_pointer; - nlohmann::json::value_t m_type; - nlohmann::json::value_t m_expected; - std::string m_message; - -public: - /** - * Construct the property error. - * - * \param object the faulty object - * \param pointer the property path - * \param type the found type - * \param expected the expected type - */ - inline property_error(nlohmann::json object, - nlohmann::json::json_pointer pointer, - nlohmann::json::value_t type, - nlohmann::json::value_t expected) noexcept - : m_object(std::move(object)) - , m_pointer(std::move(pointer)) - , m_type(type) - , m_expected(expected) - { -#if 0 - m_message += "invalid '" + m_pointer.to_string() + "' property "; - m_message += "(" + nlohmann::json(expected).type_name(); - m_message += " expected, got "; - m_message += nlohmann::json(type).type_name() + ")"; -#endif - } - - /** - * Get the faulty object. - * - * return the object - */ - inline const nlohmann::json& object() const noexcept - { - return m_object; - } - - /** - * Get the pointer to the property. - * - * \return the pointer - */ - inline const nlohmann::json::json_pointer& pointer() const noexcept - { - return m_pointer; - } - - /** - * Get the actual type. - * - * \return the type - */ - inline nlohmann::json::value_t type() const noexcept - { - return m_type; - } - - /** - * Get the expected type. - * - * \return the type - */ - inline nlohmann::json::value_t expected() const noexcept - { - return m_expected; - } - - /** - * Get a human formatted message. - * - * \return the message - */ - const char* what() const noexcept - { - return m_message.c_str(); - } -}; - -/** - * Require a value at the given pointer, if the value does not match the - * expected type, an exception is thrown. - * - * If the property does not exists, a property_error with a null found type is - * thrown. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \param expected the expected type - * \throw property_error on errors - */ -nlohmann::json require(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - const nlohmann::json::value_t expected); - -/** - * Require an array at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -nlohmann::json require_array(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require a boolean at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -bool require_bool(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require an integer at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -int require_int(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require an object at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -nlohmann::json require_object(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require a string at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -std::string require_string(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require an unsigned integer at the given pointer. - * - * \pre object.is_object() - * \param object the object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -unsigned require_uint(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Require a size object at the given pointer. - * - * \pre object.is_object - * \param object the json object - * \param pointer the pointer to the property - * \return the value - * \throw property_error on errors - */ -mlk::size require_size(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer); - -/** - * Get an integer at the given pointer or default value if not present or - * invalid. - * - * \pre object.is_object - * \param object the json object - * \param pointer the pointer to the property - * \param def the default value - * \return an integer - */ -int get_int(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - int def = 0) noexcept; - -/** - * Get an unsigned integer at the given pointer or default value if not present - * or invalid. - * - * \pre object.is_object - * \param object the json object - * \param pointer the pointer to the property - * \param def the default value - * \return an integer - */ -unsigned get_uint(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - unsigned def = 0) noexcept; - -/** - * Get a size object at the given pointer or a default value. - * - * \pre object.is_object - * \param object the json object - * \param pointer the pointer to the property - * \param def the default value to return in case of error - * \return a size or a default value - */ -mlk::size get_size(const nlohmann::json& object, - const nlohmann::json::json_pointer& pointer, - const mlk::size& def = {}) noexcept; - -} // !json - -} // !util - -} // !mlk - -#endif // !MALIKANIA_UTIL_HPP
--- a/tests/libmlk/CMakeLists.txt Sat Oct 27 07:16:28 2018 +0200 +++ b/tests/libmlk/CMakeLists.txt Sun Oct 28 10:01:44 2018 +0100 @@ -20,7 +20,6 @@ add_subdirectory(point) add_subdirectory(rectangle) add_subdirectory(size) -add_subdirectory(util) add_subdirectory(socket) # JavaScript bindings
--- a/tests/libmlk/util/CMakeLists.txt Sat Oct 27 07:16:28 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for malikania -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -malikania_create_test( - NAME util - LIBRARIES libmlk - SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp -)
--- a/tests/libmlk/util/main.cpp Sat Oct 27 07:16:28 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,468 +0,0 @@ -/* - * main.cpp -- test util - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Util" -#include <boost/test/unit_test.hpp> - -#include <malikania/util.hpp> -#include <malikania/size.hpp> - -using namespace mlk; -using namespace nlohmann; - -namespace nlohmann { - -namespace detail { - -auto operator<<(std::ostream& out, json::value_t type) -> std::ostream& -{ - out << json(type).type_name(); - return out; -} - -} // !detail - -} // !nlohmann - -/* - * util::clamp - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(clamp) - -BOOST_AUTO_TEST_CASE(normal) -{ - BOOST_REQUIRE_EQUAL(5, util::clamp(5, 0, 10)); -} - -BOOST_AUTO_TEST_CASE(minimum) -{ - BOOST_REQUIRE_EQUAL(0, util::clamp(0, 0, 10)); -} - -BOOST_AUTO_TEST_CASE(maximum) -{ - BOOST_REQUIRE_EQUAL(10, util::clamp(10, 0, 10)); -} - -BOOST_AUTO_TEST_CASE(less) -{ - BOOST_REQUIRE_EQUAL(0, util::clamp(-10, 0, 10)); -} - -BOOST_AUTO_TEST_CASE(higher) -{ - BOOST_REQUIRE_EQUAL(10, util::clamp(20, 0, 10)); -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "b", true }, - { "i", 123 }, - { "s", "blabla" } - }; - - BOOST_REQUIRE_EQUAL(util::json::require( - object, "/b"_json_pointer, json::value_t::boolean).type(), json::value_t::boolean); - BOOST_REQUIRE_EQUAL(util::json::require( - object, "/i"_json_pointer, json::value_t::number_integer).type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(util::json::require( - object, "/s"_json_pointer, json::value_t::string).type(), json::value_t::string); -} - -BOOST_AUTO_TEST_CASE(nonexistent) -{ - auto json = json::object(); - - try { - util::json::require(json, "/non-existent"_json_pointer, json::value_t::string); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::null); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::string); - } -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-string", 123 } - }; - - try { - util::json::require(object, "/not-string"_json_pointer, json::value_t::string); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::string); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_array - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_array) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "a", { 1, 2, 3 } }, - { "l1", { - { "l2", { 4, 5, 6 } } - } - } - }; - - auto a = util::json::require_array(object, "/a"_json_pointer); - auto l2 = util::json::require_array(object, "/l1/l2"_json_pointer); - - BOOST_REQUIRE(a.is_array()); - BOOST_REQUIRE(l2.is_array()); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-array", 123 } - }; - - try { - util::json::require_array(object, "/not-array"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::array); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_bool - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_bool) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "b", true } - }; - - BOOST_REQUIRE_EQUAL(util::json::require_bool(object, "/b"_json_pointer), true); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-bool", 123 } - }; - - try { - util::json::require_bool(object, "/not-bool"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::boolean); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_int - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_int) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "i", 123 } - }; - - BOOST_REQUIRE_EQUAL(util::json::require_int(object, "/i"_json_pointer), 123); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-int", true } - }; - - try { - util::json::require_int(object, "/not-int"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::boolean); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_integer); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_object - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_object) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { - "network", json::object({ - { "host", "localhost" }, - { "port", 9090 } - }) - } - }; - - BOOST_REQUIRE(util::json::require_object(object, "/network"_json_pointer).is_object()); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-object", 123 } - }; - - try { - util::json::require_object(object, "/not-object"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::object); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_string - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_string) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "s", "hello" } - }; - - BOOST_REQUIRE_EQUAL(util::json::require_string(object, "/s"_json_pointer), "hello"); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-string", 123 } - }; - - try { - util::json::require_string(object, "/not-string"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::number_integer); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::string); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -/* - * util::json::require_uint - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(json_require_uint) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "u1", 123U } - }; - - BOOST_REQUIRE_EQUAL(util::json::require_uint(object, "/u1"_json_pointer), 123U); -} - -BOOST_AUTO_TEST_CASE(invalid) -{ - json object{ - { "not-uint", true } - }; - - try { - util::json::require_uint(object, "/not-uint"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::boolean); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(json_require_size) - -/* - * util::json::require_size - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "size", { - { "width", 10 }, - { "height", 20 } - } - } - }; - - BOOST_TEST((util::json::require_size(object, "/size"_json_pointer) == mlk::size{10, 20})); -} - -BOOST_AUTO_TEST_CASE(not_object) -{ - json object{ - { "size", false } - }; - - try { - util::json::require_size(object, "/size"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::null); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned); - } -} - -BOOST_AUTO_TEST_CASE(not_int_width) -{ - json object{ - { "size", { - { "width", "10" }, - { "height", 20 } - } - } - }; - - try { - util::json::require_size(object, "/size"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::string); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned); - } -} - -BOOST_AUTO_TEST_CASE(not_int_height) -{ - json object{ - { "size", { - { "width", 10 }, - { "height", "20" } - } - } - }; - - try { - util::json::require_size(object, "/size"_json_pointer); - BOOST_FAIL("exception expected"); - } catch (const util::json::property_error& ex) { - BOOST_REQUIRE_EQUAL(ex.type(), json::value_t::string); - BOOST_REQUIRE_EQUAL(ex.expected(), json::value_t::number_unsigned); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(json_get_size) - -BOOST_AUTO_TEST_CASE(simple) -{ - json object{ - { "size", { - { "width", 10 }, - { "height", 20 } - } - } - }; - - BOOST_TEST((util::json::get_size(object, "/size"_json_pointer) == mlk::size{10, 20})); -} - -BOOST_AUTO_TEST_CASE(not_object) -{ - const json object{{ "size", false }}; - const mlk::size def{10, 20}; - - BOOST_TEST(util::json::get_size(object, "/size"_json_pointer, def) == def); -} - -BOOST_AUTO_TEST_CASE(not_int_width) -{ - const json object{ - { "size", { - { "width", "10" }, - { "height", 20 } - } - } - }; - - const mlk::size def{10, 20}; - - BOOST_TEST(util::json::get_size(object, "/size"_json_pointer, def) == def); -} - -BOOST_AUTO_TEST_CASE(not_int_height) -{ - const json object{ - { "size", { - { "width", 10 }, - { "height", "20" } - } - } - }; - - const mlk::size def{10, 20}; - - BOOST_TEST(util::json::get_size(object, "/size"_json_pointer, def) == def); -} - -BOOST_AUTO_TEST_SUITE_END()
--- a/tests/tools/CMakeLists.txt Sat Oct 27 07:16:28 2018 +0200 +++ b/tests/tools/CMakeLists.txt Sun Oct 28 10:01:44 2018 +0100 @@ -17,4 +17,4 @@ # add_subdirectory(map) -add_subdirectory(tileset) +#add_subdirectory(tileset)
--- a/tools/tileset/main.cpp Sat Oct 27 07:16:28 2018 +0200 +++ b/tools/tileset/main.cpp Sun Oct 28 10:01:44 2018 +0100 @@ -28,8 +28,6 @@ #include <json.hpp> -#include <malikania/util.hpp> - namespace fs = boost::filesystem; namespace ptree = boost::property_tree; @@ -157,8 +155,14 @@ auto points = tree.get<std::string>("polyline.<xmlattr>.points"); auto array = nlohmann::json::array(); - for (const auto& p : mlk::util::split(points, " ")) - array.push_back(std::stod(p)); + std::istringstream iss(points); + + for (double point; iss; ) { + point = 0; + iss >> point; + iss >> std::skipws; + array.push_back(point); + } return { { "type", "polyline" },