diff --git a/CMakeLists.txt b/CMakeLists.txt index 83f144a7..da2065ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake" CA # --------------------------------------------------------- project ( data - VERSION 0.0.35 + VERSION 0.0.36 DESCRIPTION "functinal data structures, mathematics, cryptography, networking, wrappers, and other high-level programming constructs in c++" LANGUAGES CXX ) @@ -114,7 +114,7 @@ endif () # --------------------------------------------------------- # required for pretty much everything. -find_package (Boost 1.86 COMPONENTS thread chrono log_setup log REQUIRED) +find_package (Boost 1.88 COMPONENTS thread chrono log_setup log REQUIRED) # if we do not have boost # * linux: # * go to the boost website, download and extract the latest version. @@ -150,9 +150,6 @@ ENDIF () include (FetchContent) cmake_policy (SET CMP0135 NEW) -FetchContent_Declare (json URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz) -FetchContent_MakeAvailable (json) - FetchContent_Declare (ctre URL https://github.com/hanickadot/compile-time-regular-expressions/archive/refs/tags/v3.10.0.zip) FetchContent_MakeAvailable (ctre) diff --git a/Dockerfile b/Dockerfile index 989edce3..0394856d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # use this command to run the docker file with tests # docker build -f Dockerfile.test -t test . -FROM gigamonkey/gigamonkey-base-dev:v2.2.1 +FROM gigamonkey/gigamonkey-base-dev:v2.3.1 WORKDIR /home/data COPY . . diff --git a/Dockerfile.test b/Dockerfile.test index fcfe349f..7056685d 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,4 +1,4 @@ -FROM gigamonkey/gigamonkey-base-dev:v2.2.1 +FROM gigamonkey/gigamonkey-base-dev:v2.3.1 WORKDIR /home/data COPY . . diff --git a/cleanup.sh b/cleanup.sh new file mode 100755 index 00000000..5bb1a9bb --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,13 @@ +rm -rf /usr/local/include/data +rm -rf /usr/local/include/sv +rm -rf /usr/local/lib/libdata_crypto.a +rm -rf /usr/local/lib/libdata_net.a +rm -rf /usr/local/lib/libdata_numbers.a +rm -rf /usr/local/lib/libdata_hash.a +rm -rf /usr/local/lib/libdata_io.a +rm -rf /usr/local/lib/libdata_string.a +rm -rf /usr/local/include/data/version.hpp +rm -rf /usr/local/lib/cmake/data/dataTargets.cmake +rm -rf /usr/local/lib/cmake/data/dataTargets-debug.cmake +rm -rf /usr/local/lib/cmake/data/dataConfig.cmake +rm -rf /usr/local/lib/cmake/data/dataConfigVersion.cmake diff --git a/cmake/FindData.cmake b/cmake/FindData.cmake index a1a22018..023b4f5d 100644 --- a/cmake/FindData.cmake +++ b/cmake/FindData.cmake @@ -1,7 +1,7 @@ -find_package(PkgConfig) -pkg_check_modules(PC_DATA QUIET Data) +find_package (PkgConfig) +pkg_check_modules (PC_DATA QUIET Data) -find_path(DATA_INCLUDE_DIR +find_path (DATA_INCLUDE_DIR NAMES data.hpp HINTS ENV DATA_INC_DIR ENV DATA_DIR @@ -10,7 +10,7 @@ PATH_SUFFIXES include/data DOC "The directory containing the Data header files" ) -find_library(DATA_LIBRARY NAMES data +find_library (DATA_LIBRARY NAMES data HINTS ENV DATA_LIB_DIR ENV DATA_DIR $ENV{DATA_DIR}/lib @@ -18,14 +18,14 @@ find_library(DATA_LIBRARY NAMES data DOC "Path to the Data library" ) -message(STATUS "${DATA_INCLUDE_DIR}") -if(DATA_INCLUDE_DIR) - file(READ "${DATA_INCLUDE_DIR}/data/version.hpp" ver) +message (STATUS "${DATA_INCLUDE_DIR}") +if (DATA_INCLUDE_DIR) + file (READ "${DATA_INCLUDE_DIR}/data/version.hpp" ver) string(REGEX MATCH "#define DATA_VERSION \"([0-9*.]*)\"" _ ${ver}) - set(DATA_VERSION ${CMAKE_MATCH_1}) -endif() + set (DATA_VERSION ${CMAKE_MATCH_1}) +endif () -include(FindPackageHandleStandardArgs) +include (FindPackageHandleStandardArgs) find_package_handle_standard_args(Data FOUND_VAR DATA_FOUND REQUIRED_VARS @@ -34,20 +34,20 @@ find_package_handle_standard_args(Data VERSION_VAR DATA_VERSION ) -if(DATA_FOUND) - set(DATA_LIBRARIES ${DATA_LIBRARY}) - set(DATA_INCLUDE_DIRS ${DATA_INCLUDE_DIR}) -endif() +if (DATA_FOUND) + set (DATA_LIBRARIES ${DATA_LIBRARY}) + set (DATA_INCLUDE_DIRS ${DATA_INCLUDE_DIR}) +endif () -if(DATA_FOUND AND NOT TARGET Data::Data) - add_library(Data::Data UNKNOWN IMPORTED) - set_target_properties(Data::Data PROPERTIES +if (DATA_FOUND AND NOT TARGET Data::Data) + add_library (Data::Data UNKNOWN IMPORTED) + set_target_properties (Data::Data PROPERTIES IMPORTED_LOCATION "${DATA_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${DATA_INCLUDE_DIR}" ) -endif() +endif () -mark_as_advanced( +mark_as_advanced ( DATA_INCLUDE_DIR DATA_LIBRARY ) diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake index 2ea49e3f..08537703 100644 --- a/cmake/FindGMP.cmake +++ b/cmake/FindGMP.cmake @@ -5,13 +5,16 @@ # GMP_LIBRARIES_DIR - directory where the GMP libraries are located # GMP_LIBRARIES - Link these to use GMP +if (TARGET GMP::GMP) + return () +endif () + +function (define_imported_target library headers) -function(define_imported_target library headers) - add_library(GMP::GMP UNKNOWN IMPORTED) - set_target_properties(GMP::GMP PROPERTIES + add_library (GMP::GMP UNKNOWN IMPORTED) + set_target_properties (GMP::GMP PROPERTIES IMPORTED_LOCATION ${library} - INTERFACE_INCLUDE_DIRECTORIES ${headers} - ) + INTERFACE_INCLUDE_DIRECTORIES ${headers}) set (GMP_FOUND 1 CACHE INTERNAL "GMP found" FORCE) set (GMP_LIBRARIES ${library} @@ -28,63 +31,69 @@ if (GMP_LIBRARIES AND GMP_INCLUDES) return () endif () -set(QUIET_ARG) -if(GMP_FIND_QUIETLY) - set(QUIET_ARG QUIET) -endif() +set (QUIET_ARG) +if (GMP_FIND_QUIETLY) + set (QUIET_ARG QUIET) +endif () -set(REQUIRED_ARG) -if(GMP_FIND_REQUIRED) - set(REQUIRED_ARG REQUIRED) -endif() +set (REQUIRED_ARG) +if (GMP_FIND_REQUIRED) + set (REQUIRED_ARG REQUIRED) +endif () -file(TO_CMAKE_PATH "$ENV{GMP_DIR}" _GMP_DIR) +file (TO_CMAKE_PATH "$ENV{GMP_DIR}" _GMP_DIR) -include(FindPackageHandleStandardArgs) +include (FindPackageHandleStandardArgs) - find_path(GMP_INCLUDE_DIR - NAMES gmp.h gmpxx.h - HINTS ENV GMP_INC_DIR - ENV GMP_DIR - $ENV{GMP_DIR}/include - PATH_SUFFIXES include - DOC "The directory containing the GMP header files" - ) + find_path (GMP_INCLUDE_DIR + NAMES gmp.h gmpxx.h + HINTS ENV GMP_INC_DIR + ENV GMP_DIR + $ENV{GMP_DIR}/include + PATH_SUFFIXES include + DOC "The directory containing the GMP header files") - find_library(GMP_LIBRARY_RELEASE NAMES gmp libgmp-10 gmp-10 mpir + find_library (GMP_LIBRARY_RELEASE NAMES gmp libgmp-10 gmp-10 mpir HINTS ENV GMP_LIB_DIR ENV GMP_DIR $ENV{GMP_DIR}/lib PATH_SUFFIXES lib - DOC "Path to the Release GMP library" - ) + DOC "Path to the Release GMP library") - find_library(GMP_LIBRARY_DEBUG NAMES gmpd gmp libgmp-10 gmp-10 mpir + find_library (GMP_LIBRARY_DEBUG NAMES gmpd gmp libgmp-10 gmp-10 mpir HINTS ENV GMP_LIB_DIR ENV GMP_DIR $ENV{GMP_DIR}/include PATH_SUFFIXES lib - DOC "Path to the Debug GMP library" - ) - + DOC "Path to the Debug GMP library") - if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(GMP_LIBRARIES ${GMP_LIBRARY_DEBUG}) - else() - set(GMP_LIBRARIES ${GMP_LIBRARY_RELEASE}) - endif() + if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set (GMP_LIBRARIES ${GMP_LIBRARY_DEBUG}) + else () + set (GMP_LIBRARIES ${GMP_LIBRARY_RELEASE}) + endif () # Attempt to load a user-defined configuration for GMP if couldn't be found if ( NOT GMP_INCLUDE_DIR OR NOT GMP_LIBRARIES) - include( GMPConfig OPTIONAL ) + include ( GMPConfig OPTIONAL ) endif() find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARIES GMP_INCLUDE_DIR) if (GMP_FOUND) define_imported_target( "${GMP_LIBRARIES}" - "${GMP_INCLUDE_DIR}" - ) -elseif(GMP_FIND_REQUIRED) - message(FATAL_ERROR "Required Gmp library not found") -endif() + "${GMP_INCLUDE_DIR}") +elseif (GMP_FIND_REQUIRED) + message (FATAL_ERROR "Required Gmp library not found") +endif () + +message(STATUS "==== Dumping GMP::GMP target properties ====") + + get_property(_props TARGET GMP::GMP PROPERTY PROPERTY_NAMES) + + foreach(_prop ${_props}) + get_target_property(_val GMP::GMP ${_prop}) + message(STATUS "${_prop} = ${_val}") + endforeach() + + message(STATUS "===========================================") diff --git a/include/data/arithmetic.hpp b/include/data/arithmetic.hpp index ff505fcc..29fc1f91 100644 --- a/include/data/arithmetic.hpp +++ b/include/data/arithmetic.hpp @@ -194,9 +194,7 @@ namespace data { template concept proto_number_system = proto_system && ImplicitlyConvertible && - // NOTE: this clause should be uncommented, but the numbers - // don't work right ATM so we need to fix them. - //ExplicitlyConvertible && + Convertible && math::hetero_abs_and_negate; // here we look at types with bit operations defined on them. @@ -414,11 +412,11 @@ namespace data { template concept div_algebraic_unsigned_to = - ring_algebraic_unsigned_to /*&& + ring_algebraic_unsigned_to && requires (const X &a) { { a / 1u } -> ImplicitlyConvertible; - { 1u / a } -> ImplicitlyConvertible; - }*/; + //{ 1u / a } -> ImplicitlyConvertible; + }; template concept group_algebraic_signed_big_to = @@ -629,16 +627,16 @@ namespace data { template concept WholeNumber = div_number && requires (const Z &a, const Z &b) { { a % b }; - // NOTE this should be uncommented. - //{ divmod (a, b) }; + { divmod (a, math::nonzero {b}) }; } && (Signed || Unsigned); template concept Integer = WholeNumber && RingNumber && div_number_signed; - // TODO require homo modable. template concept Natural = - WholeNumber && div_number_unsigned; + WholeNumber && div_number_unsigned && requires (const Z &a, const Z &b) { + { a % b } -> Same; + }; // now we have two types that go together as signed and unsigned versions of each other. template concept number_system = diff --git a/include/data/async.hpp b/include/data/async.hpp deleted file mode 100644 index afe257e5..00000000 --- a/include/data/async.hpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_ASYNC -#define DATA_ASYNC -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace data { - - // we use awaitable for all coroutines. - template using awaitable = boost::asio::awaitable; - - using exec = boost::asio::any_io_executor; - - namespace meta { - template - struct is_awaitable : std::false_type {}; - - template - struct is_awaitable> : std::true_type {}; - } - - template concept Awaitable = meta::is_awaitable::value; - - // spawn a coroutine and rethrow all exceptions. - template - void spawn (exec ex, Coroutine &&coro) { - co_spawn (ex, - std::forward (coro), - [] (std::exception_ptr eptr) { - if (eptr) std::rethrow_exception (eptr); - }); - } - - // synced turns an async function and makes it work synchronously. - - // to be more precise, synced can take any function that returns an - // awaitable type and waits for it to complete. - - // synced can take either a function or a member function pointer. - // in the second case, the second argument must be a pointer to the - // object with the member function. - - // the pattern - // synced ([&] {return some_expression; }); - // works and that's usually a lot easier than getting a member function - // pointer and a this pointer. - template - requires std::regular_invocable && requires (fun f, args... a) { - { std::invoke (std::forward (f), std::forward (a)...) } -> Awaitable<>; - } auto synced (fun &&f, args &&...a); - - using millisecond = std::chrono::milliseconds; - - // wait for a given amount of time. - awaitable sleep (std::chrono::milliseconds duration); - - template - requires std::regular_invocable && requires (fun f, args... a) { - { std::invoke (std::forward (f), std::forward (a)...) } -> Awaitable<>; - } auto synced (fun &&f, args &&...a) { - using namespace boost::asio; - - // Deduce the awaitable type returned by invoking f(args...) - using awaitable_t = decltype (std::invoke (std::forward (f), std::forward (a)...)); - - // Extract the value type that the awaitable produces when co_awaited - using result_t = typename awaitable_t::value_type; - - boost::asio::io_context ioc; - - // We use std::promise/std::future instead of asio::use_future because: - // - use_future requires the result type to be default-constructible - // - promise allows us to support non-default-constructible types - std::promise p; - auto fut = p.get_future (); - - // Spawn a coroutine onto the io_context executor. - // This coroutine will: - // 1. invoke the async function - // 2. co_await its result - // 3. forward the result (or exception) into the promise - co_spawn ( - ioc.get_executor (), - [&]() -> awaitable { - try { - if constexpr (std::is_void_v) { - // If the async function returns void, just await it - // and signal completion via set_value() - co_await std::invoke (std::forward (f), std::forward (a)...); - p.set_value (); - } else { - // Otherwise, await the result and store it in the promise - p.set_value (co_await std::invoke (std::forward (f), std::forward (a)...)); - } - } catch (...) { - // Propagate exceptions across the async → sync boundary - p.set_exception (std::current_exception ()); - } - }, - detached // We do not use asio completion tokens; promise handles result - ); - - // Run the event loop until all work (our coroutine) is complete. - // This blocks the current thread, making the function synchronous. - ioc.run (); - - // Extract the result from the future. - // This will rethrow any exception set above. - return fut.get (); - } -} - -#endif - diff --git a/include/data/crypto/NIST_DRBG.hpp b/include/data/crypto/NIST_DRBG.hpp index 91a2ce2d..e05f9cf0 100644 --- a/include/data/crypto/NIST_DRBG.hpp +++ b/include/data/crypto/NIST_DRBG.hpp @@ -15,7 +15,6 @@ #include #include #include -#include namespace data::crypto::NIST { diff --git a/include/data/crypto/block.hpp b/include/data/crypto/block.hpp index 3b09d799..7fcfdcaf 100644 --- a/include/data/crypto/block.hpp +++ b/include/data/crypto/block.hpp @@ -11,8 +11,6 @@ #include -#include - namespace data::crypto { template struct block_cipher; diff --git a/include/data/io/arg_parser.hpp b/include/data/io/arg_parser.hpp deleted file mode 100644 index db729465..00000000 --- a/include/data/io/arg_parser.hpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_IO_ARG_PARSER -#define DATA_IO_ARG_PARSER - -/* ------------------------------------------------------------------------------- - Command-line argument parsing: design, scope, and rationale ------------------------------------------------------------------------------- - -Overview --------- - -This module provides a *minimal, deterministic* command-line argument parser. -It does not attempt to support every command-line convention in existence -because it is impossible to separate syntax from meaning in command-line -arguments in general. No argument parser can satisfy everybody. Instead we -define a particular format that ought to be ok unless you are very particular -and we leave interpretation and validation to higher layers - - -Argument model --------------- - -The command line is received exactly as provided to -`main(int argc, char** argv)`. Each argument is treated as an opaque string. - -Arguments are classified into three categories: - - 1. Flags - 2. Options - 3. Positional arguments - -The program name (`argv[0]`) is always included and is treated as positional -argument at index 0. - - -Grammar -------- - -The accepted grammar is intentionally simple and unambiguous: - - * Long flag: - --name - where `name` matches [A-Za-z][A-Za-z0-9_-]* - - * Long option: - --name=value - where `name` matches [A-Za-z][A-Za-z0-9_-]* and `value` is any string - - * Abbreviated flags: - -abcd - equivalent to the individual flags `-a`, `-b`, `-c`, `-d` - (no values are permitted in abbreviated form) - - * Positional argument: - anything that does not match one of the above forms - -No other forms are recognized. In particular: - - * `--name value` is not a key/value pair. Instead it would be interpreted - as a flag called `name` and a positional argument `value`. - * `-a=value` is not an option. Again, this would be treated as a - positional argument. - * other unrecognized forms are treated as positional arguments - -This keeps lexing independent of meaning or schema. - - -Uniqueness and repetition -------------------------- - -Each option name may appear as many times as needed. - -Flags, however, are not allowed to be repeated. - -Data representation -------------------- - -Parsed arguments are stored in an `io::args::parsed` object, constructed as: - - io::args::parsed args (argc, argv); - -Internally, the parsed representation consists of: - - * Flags - A set of flag names that were present. - - * Options - A map from option name to its associated value. - - * Arguments - A vector of positional arguments, in order. - Index 0 always contains the program name. - - -Interface ---------- - -The primary interface exposed to users of this parser consists of: - - * `bool has (std::string_view name) const` - Returns true if the given flag or option name is present. - - * `get (std::string_view key, maybe value)` - Search options for key, try to read as an X. - * `get (size_t index, maybe value)` - Search for an argument at position index and try to read as an X. - -Value interpretation is performed via: - - encoding::read {} (string_view) - -This function is responsible for converting a string into type `X` and is -expected to throw on failure. The argument parser itself does not perform type -checking or semantic validation. - - -Separation of concerns ----------------------- - -This module performs *lexical classification only*. - -It does not: - * infer user intent - * validate argument combinations - * enforce schemas - * assign meaning to positional arguments - * interpret quoting or escaping - -Those concerns are intentionally deferred to higher-level schema and validation -systems. This keeps the parser predictable, testable, and compatible with -existing programs while providing a solid foundation for future extensions. - ------------------------------------------------------------------------------- -*/ - -#include -#include -#include - -namespace data::io::args { - - struct parsed { - parsed (int arg_count, const char *const arg_value[]); - - set Flags; - dispatch Options; - cross Arguments; - - // whether a flag is present - bool has (const std::string &) const; - - // get the value of a particular option. If there is more than one - // value for the same option, none is read. - template void get (const std::string &, maybe &) const; - - template void get (size_t, maybe &) const; - }; - - // a schema used to check and read the command line arguments. - template - struct command { - // the set of allowed flags. - set Flags; - schema::rule::list Arguments; - schema::rule::map Options; - - command ( - set flags, - const schema::rule::list &args, - const schema::rule::map &opts): - Flags {flags}, Arguments {args}, Options {opts} {} - }; - - template - command (set flags, const schema::rule::list &args, const schema::rule::map &opts) -> command; - - template - auto validate (const parsed &, const command &); - - bool inline parsed::has (const std::string &x) const { - return contains (Flags, x); - } - - template void inline parsed::get (const std::string &z, maybe &x) const { - auto v = get_value (Options, string_view (z)); - if (v) x = encoding::read {} (*v); - else x = {}; - } - - template void inline parsed::get (size_t z, maybe &x) const { - if (Arguments.size () > z) x = encoding::read {} (Arguments[z]); - else x = {}; - } - - template - auto validate (const parsed &p, const command &c) { - - struct result { - map Flags; - typename schema::rule::validate::result Arguments; - typename schema::rule::validate::result Options; - }; - - // TODO here we convert key to string in order to fit it into the contains - // function. However, it should be possible to say that contains takes - // anything equality comparable with contained type. - for (const auto &key : p.Flags) - if (!c.Flags.contains (std::string (key))) - return result {{}, {}, {}}; - - map flags; - - for (const auto &key : c.Flags) flags = flags.insert (key, bool (p.Flags.contains (key))); - - return result {flags, schema::validate (p.Arguments, c.Arguments), schema::validate (p.Options, c.Options)}; - } -} - -#endif - - diff --git a/include/data/io/async.hpp b/include/data/io/async.hpp deleted file mode 100644 index de311c9a..00000000 --- a/include/data/io/async.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include - -namespace data { - - boost::asio::io_context IO; - - void async_main (function ()> f, uint16 num_threads = 1) { - for (int i = 0; i < num_threads; i++) data::spawn (IO.get_executor (), f); - - multi_main ([&IO] () { - IO.run (); - }, num_threads); - - IO.stop (); - } - -} \ No newline at end of file diff --git a/include/data/io/error.hpp b/include/data/io/error.hpp deleted file mode 100644 index a0118fc7..00000000 --- a/include/data/io/error.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_IO_ERROR -#define DATA_IO_ERROR - -#include -#include -#include - -namespace data::io { - - struct error { - enum code : int { - ok = 0, - - // Default value when no code is provided. - generic = 1, - - // Transient failure: network, lock, timeout. - // Automation may retry; no human yet. - try_again = 2, - - // Bad input, misuse, unmet preconditions. - // User can fix it; no retry; no escalation. - user_action = 3, - - // Environmental or configuration issue. - // A human operator must intervene. - operator_action = 4, - - // Invariant violation, bug, corrupted state. - // Escalate to developers. - programmer_action = 5, - - unknown = 6 - }; - - code Code; - maybe Message; - error () : Code {0}, Message {} {} - error (code code) : Code {code}, Message {} {} - error (code code, const string &err): Code {code}, Message {err} {} - error (const string &err): Code {6}, Message {err} {} - operator bool () const {return bool (Message) || Code != ok; } - }; - - std::ostream inline &operator << (std::ostream &o, const error &e) { - if (e.Code == error::ok) return o << "success"; - o << "error "; - if (e.Message) o << "message: " << *e.Message << "; "; - o << "code " << static_cast (e.Code); - if (static_cast (e.Code) <= 5) o << "; this is "; - switch (e.Code) { - case 0: return o << "not an error"; - case 2: return o << "try again"; - case 3: return o << "user should resolve his invalid action"; - case 4: return o << "summon administrator to resolve this issue"; - case 5: return o << "call the programmer to fix this bug"; - default: return o; - } - } - -} - -#endif diff --git a/include/data/io/log.hpp b/include/data/io/log.hpp deleted file mode 100644 index 3c7bb9ea..00000000 --- a/include/data/io/log.hpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_IO_LOG -#define DATA_IO_LOG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace data::log { - using namespace boost::log; - - enum severity_level : size_t { - debug, - normal, - note, - warning, - error, - critical - }; - - struct options { - // where to write logs. If blank, only display to the screen. - std::string filename = ""; - - // minimum level to log. - severity_level threshold = severity_level::normal; - - // always true if filename == "" - bool print_to_screen = true; - }; - - void init (options = {}); - - /* ---------------------------------------------------------------- - Call init () to initialize logging. - - Use - - init ({ - .filename = file to store logs (optinal) - .min_security_level = minimum level to log - }); - - */ -} - - /* ----------------------- Use this to log ----------------------- - Convenience macro for logging with only a severity, using the - default channel the logger was created with ("data"). - Example: - DATA_LOG (error) << "File not found"; - */ -#define DATA_LOG(sev) \ - BOOST_LOG_SEV((data::log::global_log::get ()),(data::log::severity_level::sev)) << \ - ::data::log::indent_string (::data::log::indent::depth ()) - -namespace data::log { - - std::ostream &operator << (std::ostream &strm, severity_level level); - - // Define a logger type that supports: - // * severity - // * channels (std::string) - // The “_mt” at the end means “multi-threaded”, i.e. thread-safe. - // This logger attaches two attributes to each record: - // - severity - // - channel - typedef sources::severity_channel_logger_mt < - severity_level, // the type of the severity level - std::string // the type of the channel name - > my_logger_mt; - - // Define a global logger accessible everywhere through global_log::get(). - // BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT is a Boost macro that: - // * declares a function global_log::get() - // * ensures the logger is constructed exactly once - // * optionally initializes it with attributes - BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(global_log, my_logger_mt) { - // Construct the logger with the channel name "data". - // This means every record emitted through global_log - // will contain channel="data". - // - // The 'keywords::channel' keyword binds the channel attribute. - return my_logger_mt (keywords::channel = "data"); - } - - // a utility for adding indents to logs. - // use indent abc {} to add indents to logs - // that will step once abc is destructed. - class indent { - public: - explicit indent (); - - ~indent (); - - static unsigned depth (); - - private: - - // singleton to store default fill string - static indent &instance (); - }; - - std::string inline indent_string (unsigned int indent_depth) { - return std::string (indent_depth, ' '); - } - -// Declare an attribute keyword named "severity". -// This lets you refer to the Severity attribute in formatters and filters. -// The string "Severity" must match the name used inside Boost.Log records. -// The type is your severity_level enum. -BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) - -// Convenience macro for logging with a specific channel + severity. -// Example usage: -// DATA_LOG_CHANNEL("network", warning) << "Lost connection"; -// This expands to BOOST_LOG_CHANNEL_SEV(logger, channel, severity) -#define DATA_LOG_CHANNEL(channel, sev) \ - BOOST_LOG_CHANNEL_SEV((data::log::global_log::get()),(channel),(data::log::severity_level::sev)) << \ - ::data::log::indent_string (::data::log::indent::depth ()) - -} - -#endif //DATA_LOG_HPP diff --git a/include/data/io/main.hpp b/include/data/io/main.hpp deleted file mode 100644 index 701018fd..00000000 --- a/include/data/io/main.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include - -#include -#include - -namespace data { - - void signal_handler (int signal) noexcept; - io::error main (std::span); - - // catch_all catches everything that could be thrown. - // The first argument can be a callable type or a member function pointer. If it's - // a member function pointer, the second argument must be a pointer to the object. - template requires std::regular_invocable - io::error catch_all (fun &&f, args &&...a) noexcept { - try { - return std::invoke (std::forward (f), std::forward (a)...); - } catch (const unimplemented &m) { - return io::error {io::error::programmer_action, m.what ()}; - } catch (const data::exception &x) { - return io::error {io::error::code {x.Code}, std::string {x.what ()}}; - } catch (const std::exception &x) { - return io::error {io::error::unknown, x.what ()}; - } catch (int i) { - return io::error {io::error::code {i}}; - } catch (...) { - return io::error {io::error::unknown}; - } - } - -} - -int main (int arg_count, char **arg_values) { - std::signal (SIGINT, data::signal_handler); - std::signal (SIGTERM, data::signal_handler); - - data::io::error err = data::catch_all (&data::main, data::slice {arg_values, arg_count}); - - if (bool (err)) { - std::cout << "Program exited normally with error code " << static_cast (err.Code) << "." << std::endl; - if (err.Message) std::cout << "Error message was " << *err.Message << "." << std::endl; - if (static_cast (err.Code) <= 5 && static_cast (err.Code) > 0) { - std::cout << "General advice to resolve this error: "; - - [] (data::io::error::code code, std::ostream &o) -> std::ostream & { - switch (code) { - case 0: return o << "not an error"; - case 2: return o << "try again"; - case 3: return o << "user should resolve his invalid action"; - case 4: return o << "summon administrator to resolve this issue"; - case 5: return o << "call the programmer to fix this bug"; - default: return o << "none"; - } - } (err.Code, std::cout) << "." << std::endl; - } - } - - return err.Code; - -} diff --git a/include/data/io/multithreaded.hpp b/include/data/io/multithreaded.hpp deleted file mode 100644 index 08a4814d..00000000 --- a/include/data/io/multithreaded.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include - -namespace data { - - // Indicate globally that a shutdown request has been received. - // Use extern std::atomic ShutdownRequested to access - // this variable from another translation unit. - std::atomic ShutdownRequested {false}; - - // ensure that all threads will wake up and reach a halt state. - void shutdown () noexcept; - - void signal_handler (int signal) noexcept { - if (signal == SIGINT || signal == SIGTERM) { - std::cout << "shutdown signal received" << std::endl; - ShutdownRequested = true; - shutdown (); - } - } - - // surround with catch_all or your own version - void multi_main (function f, uint16 num_threads) { - if (num_threads < 1) throw data::exception {} << - "We cannot run with zero threads. There is already one thread running to read in the input you have provided."; - - if (nome_threads > 20) throw data::exception {} << - "Do you really need more than 20 threads? " - "This is an experimental feature now. " - "Please make an issue on the github repo if you need more and we will strive to expedite this feature."; - - // Logic for handling exceptions thrown by threads. - std::exception_ptr stored_exception = nullptr; - std::mutex exception_mutex; - - std::vector threads {}; - - auto main_loop = [&]() { - try { - while (!Shutdown) f (); - // catch all exceptions - } catch (...) { - std::lock_guard lock (exception_mutex); - // Capture first exception - if (!stored_exception) stored_exception = std::current_exception (); - Shutdown = true; - } - - shutdown (); - }; - - for (int i = 1; i < num_threads; i++) threads.emplace_back (main_loop); - - main_loop (); - - for (int i = 1; i < num_threads; i++) threads[i - 1].join (); - - if (stored_exception) std::rethrow_exception (stored_exception); - - } - -} \ No newline at end of file diff --git a/include/data/io/random.hpp b/include/data/io/random.hpp deleted file mode 100644 index 0e127dec..00000000 --- a/include/data/io/random.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2026 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_IO_RANDOM -#define DATA_IO_RANDOM - -#include - -namespace data::random { - - struct options { - // if this is disabled, only the casual - // random generator will be available. - bool secure = true; - - // if a seed is not provided, we will - // read our entropy from the OS, which is - // the preferred option. - maybe seed = {}; - - // if a nonce is not provided, we will - // read it from the entropy source (either - // from the OS or from a seed.) - bytes nonce = {}; - - // If this is provided, it is used by the - // secure random generator each time we - // read from it. - source *additional = nullptr; - - // used to select the secure RNG we will use. - // ignored if secure == false - // allowed values are 112, 128, 192, 256 - size_t strength = 256; - - }; - - // after this function is called, random::get () and crypto::random::get () - void init (options = {}); - - // user must provide this to initialize the secure random generator. - extern bytes Personalization; - -} - -#endif diff --git a/include/data/io/run.hpp b/include/data/io/run.hpp deleted file mode 100644 index 9ee78441..00000000 --- a/include/data/io/run.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2022 Daniel Krawisz -// Distributed under the Open BSV software license, see the accompanying file LICENSE. - -#ifndef DATA_IO_RUN -#define DATA_IO_RUN - -#include -#include -#include - -namespace data::io { - - using error_code = net::asio::error_code; - using error_handler = net::asio::error_handler; - - using close_handler = handler; - - class process; - - struct handlers { - virtual ~handlers () {} - virtual void read_out (string_view) = 0; - virtual void read_err (string_view) = 0; - }; - - using interaction = function (ptr)>; - - // run an external commannd. - void run (boost::asio::io_context &, string command, error_handler, interaction, close_handler); - - class process : public net::async::message_queue { - ptr Child; - process (ptr cx, ptr> stream, error_handler errors): - net::async::message_queue {stream, errors}, Child {cx} {} - - friend void run (boost::asio::io_context &, string command, error_handler, interaction, close_handler); - }; - -} - -#endif - diff --git a/include/data/io/wait_for_enter.hpp b/include/data/io/wait_for_enter.hpp deleted file mode 100644 index a9c168d5..00000000 --- a/include/data/io/wait_for_enter.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_IO_WAIT_FOR_ENTER -#define DATA_IO_WAIT_FOR_ENTER - -#include -#include -#include - -namespace data { - /// @brief Waits for user to press enter, printing message again if anything other is pressed - /// @param message message to print - /// @warning Will cause an infinite loop if used in a non-terminal enviroment - void wait_for_enter (std::string message = "Press enter to continue..."); - - - /// @brief Waits for user to respond to a message with yes or no - /// @param message question to ask user - /// @param default_value default value to return if enter pressed or no terminal - /// @return true if yes, false otherwise. - bool get_user_yes_or_no (std::string message, maybe default_value = false); - - /// @brief Gets a password from the user - /// @param message message to ask user for password - /// @param mask masking character to use - /// @return password returned - std::string get_user_password (std::string message, char mask = '*'); - - /// @brief reads a decimal number from the user - /// @param question question to ask the user. - /// @return number returned as double - double read_decimal (const std::string &question); - -} - -#endif diff --git a/include/data/math/number/bounded.hpp b/include/data/math/number/bounded.hpp index a7f210ce..29f77b8a 100644 --- a/include/data/math/number/bounded.hpp +++ b/include/data/math/number/bounded.hpp @@ -66,16 +66,6 @@ namespace data::math::number { constexpr bounded inline operator / (const bounded &a, const bounded &b) { return def::divmod> {} (a, nonzero> {b}).Quotient; } - - template - constexpr uint inline operator / (const uint &a, uint64 b) { - return a / uint (b); - } - - template - constexpr sint inline operator / (const sint &a, int64 b) { - return a / sint (b); - } template constexpr bounded inline operator % (const bounded &a, const bounded &b) { diff --git a/include/data/math/number/bounded/bounded.hpp b/include/data/math/number/bounded/bounded.hpp index f2f6f65f..c5a56d0c 100644 --- a/include/data/math/number/bounded/bounded.hpp +++ b/include/data/math/number/bounded/bounded.hpp @@ -441,17 +441,20 @@ namespace data { template struct divmod, uint> { - constexpr division, uint> operator () (const uint &, const nonzero> &); + constexpr division, uint> + operator () (const uint &, const nonzero> &); }; template struct divmod, sint> { - constexpr division, sint> operator () (const sint &, const nonzero> &); + constexpr division, sint> + operator () (const sint &, const nonzero> &); }; template struct divmod, uint> { - constexpr division, uint> operator () (const sint &, const nonzero> &); + constexpr division, uint> + operator () (const sint &, const nonzero> &); }; template @@ -670,11 +673,11 @@ namespace data { endian::order o, neg neg, std::unsigned_integral w> std::weak_ordering operator <=> (const uint &, const Z_bytes &); - template - constexpr uint operator / (const uint &, uint64); + template + constexpr uint operator / (const uint &, I); - template - constexpr sint operator / (const sint &, int64); + template + constexpr sint operator / (const sint &, I); template constexpr uint64 operator % (const uint &, uint64); @@ -682,11 +685,11 @@ namespace data { template constexpr uint64 operator % (const sint &, uint64); - template - constexpr uint &operator /= (uint &, uint64); + template + constexpr uint &operator /= (uint &, I); - template - constexpr sint &operator /= (sint &, int64); + template + constexpr sint &operator /= (sint &, I); template constexpr uint &operator %= (uint &, uint64); @@ -697,12 +700,6 @@ namespace data { template constexpr bounded &operator %= (bounded &, const bounded &); - template - constexpr uint &operator /= (uint &, uint64); - - template - constexpr sint &operator /= (sint &, int64); - template struct bounded : public oriented { @@ -930,13 +927,13 @@ namespace data { return n; } - template - constexpr uint inline &operator /= (uint &a, uint64 b) { + template + constexpr uint inline &operator /= (uint &a, I b) { return a = a / b; } - template - constexpr sint inline &operator /= (sint &a, int64 b) { + template + constexpr sint inline &operator /= (sint &a, I b) { return a = a / b; } @@ -1190,6 +1187,16 @@ namespace data { return z *= sint {x}; } + template + constexpr uint inline operator / (const uint &a, uint64 b) { + return a / uint (b); + } + + template + constexpr sint inline operator / (const sint &a, int64 b) { + return a / sint (b); + } + } namespace encoding::hexidecimal { diff --git a/include/data/math/number/bytes/Z.hpp b/include/data/math/number/bytes/Z.hpp index 56247aaa..a5103e82 100644 --- a/include/data/math/number/bytes/Z.hpp +++ b/include/data/math/number/bytes/Z.hpp @@ -77,8 +77,8 @@ namespace data::math::number { static Z_bytes zero (size_t size = 0); - // cast to any signed built in type - template + // cast to any built in type + template explicit operator I () const; Z_bytes &trim (); @@ -114,8 +114,8 @@ namespace data::math::number { static Z_bytes zero (size_t size = 0, bool negative = false); - // cast to any signed integral type. - template + // cast to any built-in type. + template explicit operator I () const; Z_bytes &trim (); @@ -1256,7 +1256,7 @@ namespace data::math::number { } template - template + template Z_bytes::operator I () const { if (*this > std::numeric_limits::max ()) @@ -1268,7 +1268,7 @@ namespace data::math::number { if (this->size () == 0) return 0; if constexpr (Same) { - endian::integral xx {0}; + endian::integral, endian::little, sizeof (I)> xx {0}; std::copy (this->words ().begin (), this->words ().begin () + @@ -1283,9 +1283,41 @@ namespace data::math::number { } template - template + template inline Z_bytes::operator I () const { - return I (Z_bytes (*this)); + + if (*this > std::numeric_limits::max ()) + throw std::invalid_argument {"value too big"}; + + if (*this < std::numeric_limits::min ()) + throw std::invalid_argument {"value too small"}; + + if (this->size () == 0) return 0; + + bool neg = is_negative (*this); + + Z_bytes neg_if; + if (neg) neg_if = -*this; + + const Z_bytes &mag = neg ? neg_if : *this; + + I result; + + if constexpr (Same) { + endian::integral, endian::little, sizeof (I)> xx {0}; + + std::copy (this->words ().begin (), + this->words ().begin () + + std::min (static_cast (sizeof (I)), + this->size ()), + xx.begin ()); + + result = I (xx); + } else if constexpr (Same) { + result = mag->words () [0]; + } else throw unimplemented {"Z_bytes to signed integral"}; + + return neg ? -result : result; } template diff --git a/include/data/net/HTTP.hpp b/include/data/net/HTTP.hpp deleted file mode 100644 index a5e54204..00000000 --- a/include/data/net/HTTP.hpp +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_HTTP -#define DATA_NET_HTTP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace data::net::HTTP { - - struct request; - struct response; - - std::ostream &operator << (std::ostream &, const request &); - std::istream &operator >> (std::istream &, request &); - - std::ostream &operator << (std::ostream &, const response &); - std::istream &operator >> (std::istream &, response &); - - writer &operator << (writer &, const request &); - writer &operator << (writer &, const response &); - - using SSL = asio::ssl::context; - - struct header : ASCII { - using enum boost::beast::http::field; - using ASCII::ASCII; - header (boost::beast::http::field); - - bool operator == (const header &h) const { - return case_insensitive_equal (*this, h); - } - - bool operator == (boost::beast::http::field x) const { - return *this == header {x}; - } - }; - - using method = boost::beast::http::verb; - - struct status { - using enum boost::beast::http::status; - boost::beast::http::status Status; - - status (boost::beast::http::status x); - status (unsigned u); - operator unsigned () const; - - bool operator == (boost::beast::http::status) const; - }; - - std::ostream &operator << (std::ostream &, status); - - // the body of an HTTP request must correspond to a - // content-type header field that explains how to - // interpret it. This is a general type for content - // that includes common use cases as an enum. - struct content : ASCII { - enum type { - // Plain UTF-8 text. - text_plain, - - // HTML markup. - text_html, - - // JSON-encoded data (e.g., {"name": "Alice"}). - application_json, - - // URL-encoded key-value pairs like key1=value1&key2=value2. - application_x_www_form_urlencoded, - - // Used for file uploads; body is split by boundary strings. - multipart_form_data, - - // Raw binary data (e.g., file content). - application_octet_stream, - - // XML content. - application_xml, - - // Raw JavaScript code. - application_javascript, - - image_png, - - image_jpeg, - - something_else - }; - - using ASCII::ASCII; - content (type); - - bool operator == (type) const; - }; - - // NOTE: we only support version 1.1 - enum version { - version_1, - version_1_1, - version_2, - version_3 - }; - - struct message { - dispatch Headers {}; - bytes Body; - - maybe content_type () const; - - message (dispatch headers = {}, bytes body = {}): - Headers {headers}, Body {body} {} - }; - - struct request : message { - method Method; - - // the remaining part of the url starting with the path. - net::target Target; - - request (method m, const net::target &targ, dispatch headers = {}, bytes body = {}): - message {headers, body}, Method {m}, Target {targ} {} - - struct make { - operator request () const; - - make () {} - - make method (const HTTP::method &) const; - make target (const target &) const; - make path (const net::path &) const; - make query (const ASCII &) const; - make query_map (dispatch) const; - make fragment (const UTF8 &) const; - make host (const UTF8 &) const; - make user_agent (const ASCII &) const; - make authorization (const ASCII &) const; - - make body (const bytes &, const content &content_type = "application/octet-stream") const; - make body (const JSON &j, const content &content_type = "application/json") const; - make body (const std::string &u, const content &content_type = "text/plain") const; - - make add_headers (dispatch) const; - - private: - version Version {version_1_1}; - using pctstr = encoding::percent::string; - - maybe Method; - - target::make Target; - - dispatch Headers; - - maybe Body; - }; - - // host is required and the value must be an authority - bool valid () const; - - authority host () const; - - operator bytes () const { - std::stringstream ss; - ss << *this; - return bytes (string (ss.str ())); - } - }; - - struct response : message { - status Status {0}; - - response () {} - response (status x, dispatch headers = {}, bytes body = {}): - message {headers, body}, Status {x} {} - - operator bytes () const { - std::stringstream ss; - ss << *this; - return bytes (string (ss.str ())); - } - }; - - struct stream : net::stream { - awaitable request (const HTTP::request &req) { - co_await this->send (req); - co_return co_await this->receive (); - } - - virtual ~stream () {} - }; - - ptr inline get_SSL () { - return std::make_shared (net::HTTP::SSL::tlsv13_client); - }; - - // thread safe. - // we only support 1.1 now. - awaitable> connect (version, const authority &host_or_endpoint, SSL * = nullptr); - - // an exception class provided for the user, not thrown by anything here. - struct exception : data::exception { - request Request; - maybe Response; - - exception (const request &req, const response &res, const string &w) : - Request {req}, Response {res} { - *this << w; - } - - exception (const request &req, const string &w) : - Request {req}, Response {} { - *this << w; - } - }; - - inline status::status (boost::beast::http::status x) : Status {x} {} - - inline status::status (unsigned u) : Status {u} {} - - inline status::operator unsigned () const { - return static_cast (Status); - } - - inline header::header (boost::beast::http::field x) : ASCII {std::string {boost::beast::http::to_string (x)}} {} - - bool inline status::operator == (boost::beast::http::status x) const { - return Status == x; - } - - std::ostream inline &operator << (std::ostream &o, status x) { - return o << x.Status; - } - - bool inline content::operator == (type ttt) const { - return static_cast (*this) == static_cast (content (ttt)); - } - - request::make inline request::make::body (const JSON &j, const content &content_type) const { - return body (bytes (string {j.dump ()}), content_type); - } - - request::make inline request::make::body (const std::string &u, const content &content_type) const { - return body (bytes (string (u)), content_type); - } - - writer inline &operator << (writer &w, const request &r) { - return w << bytes (r); - } - - writer inline &operator << (writer &w, const response &r) { - return w << bytes (r); - } - -} - -#endif diff --git a/include/data/net/HTTP_client.hpp b/include/data/net/HTTP_client.hpp deleted file mode 100644 index d9ee695d..00000000 --- a/include/data/net/HTTP_client.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2022 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_HTTP_CLIENT -#define DATA_NET_HTTP_CLIENT - -#include -#include -#include - -namespace data::net::HTTP { - - // manage a remote connection to a server and abstract requests - // as function calls. The connection is closed on destruction. - struct client { - - HTTP::REST REST; - - client (const HTTP::REST &rest, rate_limiter rate = {}); - client (ptr ssl, const HTTP::REST &rest, rate_limiter rate = {}); - - awaitable operator () (const request &r); - awaitable GET (path path, dispatch params = {}); - awaitable POST (path path, dispatch form_data = {}); - - private: - // optional. If not present we use http. - ptr SSL; - - // optional. - rate_limiter Rate; - - // TODO add a timer to close the session if we wait too long. - ptr Session; - }; - - inline client::client (const HTTP::REST &rest, rate_limiter rate) : - client {std::make_shared (HTTP::SSL::tlsv12_client), rest, rate} { - SSL->set_default_verify_paths (); - SSL->set_verify_mode (asio::ssl::verify_peer); - } - - inline client::client (ptr ssl, const HTTP::REST &rest, rate_limiter rate) : - REST {rest}, SSL {ssl}, Rate {rate}, Session {} {} - - awaitable inline client::GET (path path, dispatch params) { - co_return co_await (*this) (REST.GET (path, params)); - } - - // POST form data - awaitable inline client::POST (path path, dispatch form_data) { - co_return co_await (*this) (REST.POST (path, form_data)); - } -} - -#endif diff --git a/include/data/net/HTTP_server.hpp b/include/data/net/HTTP_server.hpp deleted file mode 100644 index 1eff54d5..00000000 --- a/include/data/net/HTTP_server.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2022-2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_HTTP_SERVER -#define DATA_NET_HTTP_SERVER - -#include -#include -#include -#include -#include -#include - -namespace data::net::HTTP::beast { - bool is_websocket_upgrade (const request &); - awaitable write_websocket_decline (asio::ip::tcp::socket &); -} - -namespace data::net::HTTP { - - using request_handler = function (const request &)>; - - // Accepts incoming connections and creates new session objects to handle them - class server { - - class session; - - class sessions { - - std::unordered_set> Sessions; - std::mutex Mtx; - - public: - void add (ptr); - void remove (ptr); - void remove_all (); - }; - - // Handles an HTTP request and sends back an HTTP response - class session { - // Apply the request handler to the request and send the response back - awaitable handle_request (const beast::request req) { - if (beast::is_websocket_upgrade (req)) - co_await beast::write_websocket_decline (Socket); - // Write the response - else co_await beast::http::async_write (Socket, beast::to (co_await Handler (beast::from (req))), asio::use_awaitable); - } - - asio::ip::tcp::socket Socket; - request_handler Handler; - sessions &Sessions; - - public: - // Constructor - session (sessions &x, asio::ip::tcp::socket &&socket, request_handler handler) : - Sessions {x}, Socket (std::move (socket)), Handler (std::move (handler)) {} - - // Read and respond to an HTTP request from the socket - awaitable respond (); - - void close () { - Socket.cancel (); - } - - ptr Self {}; - }; - - asio::ip::tcp::acceptor Acceptor; - request_handler Handler; - - sessions Sessions; - - public: - // Constructor - server (exec ex, net::IP::TCP::endpoint ep, request_handler handler); - - // Accept a new connection and start a new session to handle it - awaitable accept (); - - void close () { - if (Acceptor.is_open ()) { - Acceptor.cancel (); - Acceptor.close (); - } - - Sessions.remove_all (); - } - - ~server () { - close (); - } - - }; -} - -#endif diff --git a/include/data/net/JSON.hpp b/include/data/net/JSON.hpp deleted file mode 100644 index bc523f50..00000000 --- a/include/data/net/JSON.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the Open BSV software license, see the accompanying file LICENSE. - -#ifndef DATA_NET_JSON -#define DATA_NET_JSON - -#include -#include -#include - - -namespace data { - using JSON = nlohmann::json; -} - -namespace data::net { - - open inline JSON_stream (open Open); - - // JSON line stream means that we insert a new line at the end of every JSON type. - open inline JSON_line_stream (open Open); - -} - -namespace data { - - // throws JSON::exception if the format is bad. - ptr> JSON_line_parser (handler); -} - -namespace data::net { - - open inline JSON_stream (open Open) { - return [Open] (close_handler on_close, interaction receiver) -> awaitable>> { - function generator = - [] (const JSON &j) -> std::string { - return j.dump (); - }; - - function reader = - [] (string_view x) -> JSON { - auto j = JSON::parse (x); - return j; - }; - - return serialized_stream_read (Open, generator, reader) (on_close, receiver); - }; - } - - // JSON line stream means that we insert a new line at the end of every JSON type. - open inline JSON_line_stream (open Open) { - return [Open] (close_handler on_close, interaction receiver) -> awaitable>> { - function generator = - [] (const JSON &j) -> std::string { - return string::write (j.dump (), "\n"); - }; - - function> (handler)> parser = - [] (handler h) -> ptr> { - return JSON_line_parser (h); - }; - - return serialized_stream_parsed (Open, generator, parser) (on_close, receiver); - }; - } - -} - -#endif diff --git a/include/data/net/REST.hpp b/include/data/net/REST.hpp deleted file mode 100644 index 15fcc96d..00000000 --- a/include/data/net/REST.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2022 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_REST -#define DATA_NET_REST - -#include - -namespace data::net::HTTP { - - // REST is for converting typical REST API formats into a regular HTTP request. - struct REST { - using form = dispatch; - - // a typical GET request - HTTP::request::make GET (path path) const; - HTTP::request::make GET (path path, form params) const; - - // POST form data - HTTP::request::make POST (path path, form params = {}) const; - - REST (const ASCII &); - REST (const ASCII &, const path &); - - authority Host; - - // the part of the path that is common to the whole API. - path Path; - - // if this is present, it will be put in the Authorization header. - maybe Authorization; - - HTTP::request::make operator () (HTTP::method meth, const path &path) const; - - static encoding::percent::string encode_form_data (form); - - }; - - HTTP::request::make inline REST::GET (path path) const { - return operator () (method::get, path); - } - - HTTP::request::make inline REST::GET (path path, dispatch params) const { - return operator () (method::get, path).query (encode_form_data (params)); - } - - HTTP::request::make inline REST::POST (path path, dispatch params) const { - return operator () (method::post, path).body (bytes (encode_form_data (params)), - content::application_x_www_form_urlencoded); - } - - inline REST::REST (const ASCII &host): - Host {host}, Path {} {} - - inline REST::REST (const ASCII &host, const path &p): - Host {host}, Path {p} {} - -} - -#endif diff --git a/include/data/net/TCP.hpp b/include/data/net/TCP.hpp deleted file mode 100644 index 3ea7d16d..00000000 --- a/include/data/net/TCP.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2021-2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_TCP -#define DATA_NET_TCP - -#include -#include - -namespace data::net::IP { - - struct exception : data::exception { - exception (asio::error err) : data::exception {} { - this->write ("IO error: ", err.message ()); - } - }; -} - -namespace data::net::IP::TCP { - - // open a TCP connection. - struct open { - endpoint Endpoint; - bool ThreadSafe; - - open (const endpoint e, bool thread_safe = true): Endpoint {e}, ThreadSafe {thread_safe} {} - - // we provide the option of bytes or string to represent the data along the stream. - awaitable>> operator () (close_handler, interaction); - awaitable>> operator () (close_handler, interaction); - }; - - // note: to make this thread safe, use make_strand on the executor. - ptr connect (exec ec, const endpoint &p, close_handler on_close); - -} - -#endif - diff --git a/include/data/net/URL.hpp b/include/data/net/URL.hpp deleted file mode 100644 index d0da7029..00000000 --- a/include/data/net/URL.hpp +++ /dev/null @@ -1,541 +0,0 @@ - -// Copyright (c) 2023-2024 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_URL -#define DATA_NET_URL - -#include -#include -#include - -// Implementation of a URL as described in RFC3986 -// https://www.rfc-editor.org/rfc/rfc3986 - -namespace data::net { - - // universal resource locator. - struct URL; - - std::ostream &operator << (std::ostream &, const URL &); - std::istream &operator >> (std::istream &, URL &); - - bool operator == (const URL &, const URL &); - - // protocol is the first part of the URL, and it could be something like ftp or http. - struct protocol; - - std::ostream &operator << (std::ostream &, const protocol &); - std::istream &operator >> (std::istream &, protocol &); - - bool operator == (const protocol &, const protocol &); - - // the domain name appears in URLs that do not directly refer to an IP address. - struct domain_name; - - std::ostream &operator << (std::ostream &, const domain_name &); - std::istream &operator >> (std::istream &, domain_name &); - - bool operator == (const domain_name &, const domain_name &); - - // a port may be directly specified in a url or may be inferred - // from the protocol. - struct port; - - std::ostream &operator << (std::ostream &, const port &); - std::istream &operator >> (std::istream &, port &); - - bool operator == (const port &, const port &); - - uint16 default_port (const protocol &); - - // the path is the part of the URL including and after - // the first '/' and before the first '?'. - struct path; - - std::ostream &operator << (std::ostream &, const path &); - std::istream &operator >> (std::istream &, path &); - - // the path all the way to the end. - struct target; - - std::ostream &operator << (std::ostream &, const target &); - std::istream &operator >> (std::istream &, target &); - - bool operator == (const target &, const target &); - - // the authority is the domain name or an IP address, possibly with port included. - struct authority; - - std::ostream &operator << (std::ostream &, const authority &); - std::istream &operator >> (std::istream &, authority &); - - bool operator == (const authority &, const authority &); - -} - -namespace data::net::IP { - - struct address; - - std::ostream &operator << (std::ostream &, const address &); - std::istream &operator >> (std::istream &, address &); - - bool operator == (const address &, const address &); - -} - -namespace data::net::IP::TCP { - - // a TCP endpoint is a URL consisting of TCP as the protocol with an ip address and port. - struct endpoint; - - std::ostream &operator << (std::ostream &, const endpoint &); - std::istream &operator >> (std::istream &, endpoint &); - - bool operator == (const endpoint &, const endpoint &); - -} - -namespace data::net { - - struct protocol : ASCII { - static constexpr auto pattern = ctll::fixed_string {R"([A-Za-z][A-Za-z0-9+\-\.]*)"}; - - using ASCII::ASCII; - protocol (const ASCII &x) : ASCII {x} {} - - bool valid () const; - - enum name { - unknown, - FTP, - TCP, - HTTP, - HTTPS, - WS, - WSS - }; - - explicit operator name () const; - - protocol (name); - - bool operator == (const name &) const; - - static protocol encode (name); - }; - - // port is a uint16 that may be in a url, but it may be inferred from the protocol. - // sometimes people call the protocol the port. - struct port : ASCII { - static constexpr auto pattern = ctll::fixed_string {R"([0-9]*)"}; - //using ASCII::ASCII; - port (const ASCII &x) : ASCII {x} {} - - port (uint16 number) : ASCII {std::to_string (number)} {} - - bool valid () const; - }; - - struct domain_name : ASCII { - using ASCII::ASCII; - domain_name (const ASCII &x) : ASCII {x} {} - - static bool valid (string_view); - bool valid () const; - }; - - struct authority : UTF8 { - using UTF8::UTF8; - - static string_view user_info (string_view); - - authority (const domain_name &d); - authority (const IP::address &ad); - - authority (const domain_name &d, uint16 port); - authority (const IP::address &ad, uint16 port); - - maybe address () const; - maybe endpoint () const; - maybe host () const; - maybe port () const; - maybe port_number () const; - - maybe user_info () const; - - static bool valid (string_view); - bool valid () const; - - }; -} - -namespace data::net::IP { - - struct address : UTF8 { - - using UTF8::UTF8; - address (const UTF8 &x) : UTF8 {x} {} - address (const byte_array<4> &); - address (const byte_array<16> &); - - bool valid () const; - - // 4 or 6 or -1 for error. - int32 version () const; - - // will be length 4 for ipv4 and 16 for ipv6 - explicit operator bytes () const; - operator asio::ip::address () const; - }; - - // for when you need just one type of address. - struct v4_address : address { - v4_address (const UTF8 &x) : address {x} {} - v4_address (const byte_array<4> &); - - explicit operator byte_array<4> () const; - - static v4_address localhost (); - }; - - struct v6_address : address { - v6_address (const UTF8 &x) : address {x} {} - v6_address (const byte_array<16> &); - - explicit operator byte_array<16> () const; - - static v6_address localhost (); - }; -} - -namespace data::net { - // A path must begin with '/' but '/' is not - // necessarily a delimiter. path must be a - // percent-encoded string because a delimeter - // character may be percent encoded, which would - // change structure of the path if it were - // decoded. Thus, two paths can be percent - // equal without being equal. - struct path : encoding::percent::string { - using encoding::percent::string::string; - path (list, char delim = '/'); - list read (char delim = '/') const; - - static bool valid (string_view); - bool valid () const; - }; - - // same with target, which includes path. - struct target : encoding::percent::string { - static bool valid (string_view); - - using encoding::percent::string::string; - - net::path path () const; - - maybe query () const; // the part after ? and before # - maybe> query_map () const; - - maybe fragment () const; // the part after the # - - bool valid () const; - - // make a target bit by bit. - struct make { - operator target () const; - - make () {} - explicit make (const target &); - - make path (const net::path &) const; - - make query_map (dispatch) const; - make query (const ASCII &) const; - - make fragment (const UTF8 &) const; - - using pctstr = encoding::percent::string; - - // we use pointers here instead of maybes - // so that we don't copy these over and over - // as we add more elements. - ptr Path; - ptr Query; - ptr Fragment; - - }; - - make read () const; - - }; - - struct URL : encoding::percent::URI { - - // get the protocol of the URL, which is the part before the first ":" - net::protocol protocol () const; - - // attempt to get port as a number. - maybe port_number () const; - - // get the port or the protocol if not available. - ASCII port_DNS () const; - - // attempt to get host as a DNS string. - maybe domain_name () const; - - // attempt to get host as an IP address. - maybe address () const; - - // attempt to get query as a map of key pairs in the form =&... - maybe> query_map () const; - - // get user info as :. - // (This is insecure but supported because we sometimes still use it anyway) - maybe> user_name_pass () const; - - // attempt to read as a TCP endpoint. - maybe endpoint () const; - - using encoding::percent::URI::URI; - URL (const encoding::percent::URI &x) : encoding::percent::URI {x} {} - - // for making urls in steps. - struct make { - operator URL () const; - - make () {} - explicit make (const URL &); - - make protocol (const net::protocol &) const; - make scheme (const ASCII &) const; - - make port (const uint16 &) const; - - make address (const IP::address &) const; - make domain_name (const net::domain_name &) const; - - // Handles the case that some other registeration process - // besides DNS is being used and we are using the - // registered name as the host. - make registered_name (const UTF8 &) const; - - make user_info (const UTF8 &info) const; - - // insecure for web - make user_name_pass (const UTF8 &username, const UTF8 &pass) const; - - make authority (const UTF8 &) const; - - make path (const net::path &) const; - make target (const net::target &) const; - - make query_map (dispatch) const; - make query (const ASCII &) const; - - make fragment (const UTF8 &) const; - - using pctstr = encoding::percent::string; - - ptr Protocol; - - ptr UserInfo; - ptr Host; - ptr Port; - - ptr Target; - }; - - private: - make read () const; - }; -} - -namespace data::net::IP::TCP { - - struct endpoint : authority { - - endpoint (): authority {} {} - endpoint (const IP::address &addr, uint16 port): authority {addr, port} {} - - // input endpoint as a string. - endpoint (const char *x): authority {x} {} - endpoint (const std::string &x): authority {x} {} - - bool valid () const; - - IP::address address () const; - uint16 port () const; - - uint16 port_number () const { - return port (); - } - - // we use asio for the backend of some of this stuff. - operator asio::ip::tcp::endpoint () const; - }; -} - -namespace data { - - bool case_insensitive_equal (const string &, const string &); - -} - -namespace data::net { - - bool inline IP::operator == (const IP::address &a, const IP::address &b) { - return case_insensitive_equal (a, b); - } - - bool inline operator == (const domain_name &a, const domain_name &b) { - return case_insensitive_equal (a, b); - } - - bool inline operator == (const protocol &a, const protocol &b) { - return case_insensitive_equal (a, b); - } - - bool inline operator == (const port &a, const port &b) { - return case_insensitive_equal (a, b); - } - - bool inline operator == (const authority &a, const authority &b) { - return static_cast (a) == static_cast (b); - } - - bool inline operator == (const target &a, const target &b) { - return static_cast (a) == static_cast (b); - } - - std::ostream inline &IP::operator << (std::ostream &o, const IP::address &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const domain_name &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const protocol &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const port &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const authority &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const target &x) { - return o << static_cast (x); - } - - std::ostream inline &operator << (std::ostream &o, const path &x) { - return o << static_cast (x); - } - - bool inline net::protocol::valid () const { - return ctre::match (*this); - } - - inline protocol::protocol (name n) : string {encode (n)} {} - - bool inline protocol::operator == (const name &n) const { - return *this == protocol (n); - } - - bool inline port::valid () const { - return ctre::match (*this) || ctre::match (*this); - } - - inline URL::make::make (const URL &u) : make {u.read ()} {} - - inline target::make::make (const target &t) : make {t.read ()} {} - - bool inline operator == (const URL &a, const URL &b) { - return static_cast (a.normalize ()) == static_cast (b.normalize ()); - } - - std::ostream inline &operator << (std::ostream &o, const URL &u) { - return o << static_cast (u); - } - - bool inline IP::TCP::operator == (const IP::TCP::endpoint &a, const IP::TCP::endpoint &b) { - return static_cast (a) == static_cast (b); - } - - std::ostream inline &IP::TCP::operator << (std::ostream &o, const IP::TCP::endpoint &u) { - return o << static_cast (u); - } - - protocol inline URL::protocol () const { - return net::protocol {this->scheme ()}; - } - - URL::make inline URL::make::scheme (const ASCII &x) const { - return protocol (x); - } - - ASCII inline URL::port_DNS () const { - auto p = this->port (); - if (!p) return this->protocol (); - return *p; - } - - bool inline domain_name::valid () const { - return valid (*this); - } - - inline authority::authority (const domain_name &d): UTF8 {d} {} - inline authority::authority (const IP::address &ad): UTF8 {ad} {} - - inline authority::authority (const domain_name &d, uint16 port): UTF8 {std::string {d} + ":" + net::port {port}} {} - inline authority::authority (const IP::address &ad, uint16 port): UTF8 {std::string {ad} + ":" + net::port {port}} {} - - bool inline path::valid () const { - return valid (*this); - } - - bool inline target::valid () const { - return valid (*this); - } - - bool inline authority::valid () const { - return valid (encoding::percent::encode (*this)); - } - - maybe inline authority::user_info () const { - string_view x = user_info (*this); - if (x.data () == nullptr) return {}; - return data::UTF8 {x}; - } -} - -namespace data::net::IP::TCP { - - bool inline endpoint::valid () const { - return authority::valid () && bool (static_cast (this)->port_number ()) && - bool (static_cast (this)->address ()) && !bool (static_cast (this)->user_info ()); - } - - IP::address inline endpoint::address () const { - return *static_cast (this)->address (); - } - - uint16 inline endpoint::port () const { - return *static_cast (this)->port_number (); - } - - inline endpoint::operator asio::ip::tcp::endpoint () const { - return valid () ? asio::ip::tcp::endpoint { - asio::ip::make_address (this->address ()), - this->port () - } : asio::ip::tcp::endpoint {}; - } -} - -#endif diff --git a/include/data/net/URL/percent.hpp b/include/data/net/URL/percent.hpp deleted file mode 100644 index 948ac3f0..00000000 --- a/include/data/net/URL/percent.hpp +++ /dev/null @@ -1,207 +0,0 @@ - -// Copyright (c) 2023-2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_ENCODING_PERCENT -#define DATA_ENCODING_PERCENT - -#include -#include - -// RFC3986 defines certain reserved characters which -// denote the structure of a URI. Percent encoding -// ensures that arbitrary UTF8 strings can be included -// in a URL without affecting the structure of the URL. -// -// A UTF8 character is percent encoded as "%" followed -// by the hexidecimal value of the encoded character. -// -// Control characters and non-ASCII characters are -// required to be percent encoded. Other characetrs -// may optionally be percent encoded. -namespace data::encoding::percent { - - bool valid (string_view input); - - constexpr const char *Reserved = ":/?#[]@!$&'()*+,;="; - constexpr const char *Delimiters = ":/?#[]@"; - constexpr const char *Subdelimiters = "!$&'()*+,;="; - - bool inline is_reserved (char c) { - return strchr (Reserved, c) != nullptr; - } - - bool inline is_unreserved (unsigned char c) { - return (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || - c == '-' || c == '.' || c == '_' || c == '~'; - } - - // encode the string, ensuring that the given characters are encoded. - std::string encode (const data::UTF8 &, const data::ASCII &required = ""); - - bool equivalent (const std::string &, const std::string &); - - // a %-encoded string. - struct string; - - // decode back to UTF8. - maybe decode (string_view); - - struct string : data::ASCII { - using data::ASCII::ASCII; - - string (const data::ASCII &x) : data::ASCII {x} {} - - // If a character is reserved, then its percent-encoded - // form is unequal to its non-encoded form. Otherwise - // they are treated as equal. - bool operator == (const string &x) const { - return equivalent (*this, x); - } - - bool valid () const { - return percent::valid (*this); - } - }; - - // https://www.ietf.org/rfc/rfc3986.txt - // A URI is a universal resource indicator, as opposed to locator. - // the URI may not enable you to retrieve the document but it will - // uniquely specify the document. - struct URI : string { - - static string_view scheme (string_view); - static string_view authority (string_view); - static string_view path (string_view); - static string_view query (string_view); - static string_view fragment (string_view); - - static string_view user_info (string_view); - static string_view host (string_view); - static string_view port (string_view); - - static string_view address (string_view); - - // everything from the path to the end. - static string_view target (string_view); - - data::ASCII scheme () const; - maybe user_info () const; - maybe host () const; - maybe port () const; - maybe authority () const; - string target () const; - string path () const; - maybe query () const; // the part after ? and before # - maybe fragment () const; // the part after the # - - bool valid () const; - - using string::string; - URI (const string &x) : string {x} {} - - URI (const data::ASCII &protocol, const data::UTF8 &user_utya, - const data::UTF8 &host, const data::ASCII &port, const string &path, - const data::UTF8 &query, const data::UTF8 &fragment); - - // convert to a standard equivalent form in which all hex digits are upper case, - // scheme and host are lower case, and only required digits are hex-encoded. - // this is used to test equality of URLs. - URI normalize () const; - - }; - /* funny mnemonic to remember which parts of a URL can be percent encoded. - * - * "Ugly Hippos Push Strange Quests, Possibly Even Frightening Frilly Unicorns!" 🦄 - * - * Each word stands for a part of a URL: - * - * Ugly → Userinfo (username:password@) - * - * Hippos → Host (only percent-encoded for IPv6 literals in some contexts — usually not encoded) - * - * Push → Path - * - * Strange → Search (query string) - * - * Quests → Query (alt word for search—helps reinforce) - * - * Possibly → Port (rarely encoded; mostly numeric, but technically valid) - * - * Even → Encoded path segments - * - * Frightening → Fragment - * - * Frilly → (for double “F” mnemonic) - * - * Unicorns → (Unifying idea — "URLs are weird but lovable") - */ - - bool inline operator == (const URI &a, const URI &b) { - return static_cast (a.normalize ()) == static_cast (b.normalize ()); - } - - std::ostream inline &operator << (std::ostream &o, const URI &u) { - return o << static_cast (u); - } - - data::ASCII inline URI::scheme () const { - string_view x = scheme (*this); - if (x.data () == nullptr) throw exception {} << "invalid URI " << *this; - return data::ASCII {x}; - } - - string inline URI::path () const { - string_view x = path (*this); - if (x.data () == nullptr) throw exception {} << "invalid URI " << *this; - return string {x}; - } - - string inline URI::target () const { - string_view x = target (*this); - if (x.data () == nullptr) throw exception {} << "invalid URI " << *this; - return string {x}; - } - - maybe inline URI::authority () const { - string_view x = authority (*this); - if (x.data () == nullptr) return {}; - return decode (x); - } - - maybe inline URI::user_info () const { - string_view x = user_info (*this); - if (x.data () == nullptr) return {}; - return decode (x); - } - - maybe inline URI::host () const { - string_view x = host (*this); - if (x.data () == nullptr) return {}; - return decode (x); - } - - maybe inline URI::port () const { - string_view x = port (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - maybe inline URI::query () const { - string_view x = query (*this); - if (x.data () == nullptr) return {}; - return {data::ASCII {x}}; - } - - maybe inline URI::fragment () const { - string_view x = fragment (*this); - if (x.data () == nullptr) return {}; - return decode (x); - } -} - - -#endif diff --git a/include/data/net/asio/SSL.hpp b/include/data/net/asio/SSL.hpp deleted file mode 100644 index ef94abd5..00000000 --- a/include/data/net/asio/SSL.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ASIO_UDP -#define DATA_NET_ASIO_UDP - -#include -#include - -namespace data::net::SSL { - - // not sure what to put here - -} - -#endif - - diff --git a/include/data/net/asio/TCP.hpp b/include/data/net/asio/TCP.hpp deleted file mode 100644 index b6d7afb7..00000000 --- a/include/data/net/asio/TCP.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ASIO_TCP -#define DATA_NET_ASIO_TCP - -#include -#include - -namespace data::net::IP::TCP { - - using stream = asio::byte_stream; - -} - -#endif - diff --git a/include/data/net/asio/UDP.hpp b/include/data/net/asio/UDP.hpp deleted file mode 100644 index 7ddf8588..00000000 --- a/include/data/net/asio/UDP.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ASIO_UDP -#define DATA_NET_ASIO_UDP - -#include -#include - -namespace data::net::IP::UDP { - - using stream = asio::byte_stream; - -} - -#endif - diff --git a/include/data/net/asio/periodic_timer.hpp b/include/data/net/asio/periodic_timer.hpp deleted file mode 100644 index a015f4ce..00000000 --- a/include/data/net/asio/periodic_timer.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ASIO_PERIODIC_TIMER -#define DATA_NET_ASIO_PERIODIC_TIMER - -#include -#include "session.hpp" -#include - -namespace data::net::asio { - // this timer will call a function until a stop condition is met. - class periodic_timer : public std::enable_shared_from_this { - deadline_timer Timer; - boost::posix_time::time_duration Interval; - function Function; - bool Stopped; - - public: - // the timer starts running once the user calls io.run (). - periodic_timer (io_context &io, boost::posix_time::time_duration interval, function tick): - Timer {io, boost::posix_time::from_time_t (std::chrono::system_clock::to_time_t (std::chrono::system_clock::now ())) + interval}, - Interval {interval}, - Function {[tick, self = this->shared_from_this ()] (const boost::system::error_code &e) { - if (bool (e) || self->Stopped) return; - tick (); - self->Timer.expires_at (self->Timer.expires_at () + self->Interval); - // Posts the timer event - self->Timer.async_wait (self->Function); - }}, Stopped {false} { - Timer.async_wait (Function); - } - - void stop () { - Stopped = true; - } - }; -} - -#endif - - diff --git a/include/data/net/asio/stream.hpp b/include/data/net/asio/stream.hpp deleted file mode 100644 index 3588130d..00000000 --- a/include/data/net/asio/stream.hpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2021-2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ASIO_STREAM -#define DATA_NET_ASIO_STREAM - -#include -#include -#include -#include - -namespace data::net::asio { - using namespace boost::asio; - using error = boost::system::error_code; - - template - concept ConstBufferSequence = std::copy_constructible && std::destructible && - requires (const const_buffer_sequence x) { - { buffer_sequence_begin (x) }; - { buffer_sequence_end (x) }; - }; - - template - concept AsyncWriteStream = ConstBufferSequence && requires (async_write_stream stream) { - { stream.get_executor () }; - } && requires (async_write_stream stream, const_buffer_sequence bf) { - { stream.async_write_some (bf, use_awaitable) } -> Same>; - }; - - template - concept AsyncReadStream = requires (async_read_stream stream, mutable_buffer_sequence m) { - { stream.async_read_some (buffer (m), use_awaitable) } -> Same>; - }; - - template requires AsyncReadStream class stream; - - template using byte_stream = stream; - template using char_stream = stream; - - struct exception : data::exception { - error Error; - exception (error ec): Error {ec} { - *this << "Operation failed because: " << ec; - } - }; - - template requires AsyncReadStream - class stream final : public net::stream { - - ptr Stream; - - close_handler OnClose; - - bool Closed; - - constexpr static const int buffer_size = 4096; - bytes Buffer; - - public: - stream (ptr socket, close_handler on_close) : - Stream {socket}, OnClose {on_close}, Closed {!Stream->is_open ()}, Buffer (buffer_size) {} - - awaitable send (byte_slice x) final override { - error ec; - std::size_t n = co_await async_write ( - *Stream, - buffer (x.data (), x.size ()), - asio::redirect_error (asio::use_awaitable, ec) - ); - - if (ec) { - throw exception {ec}; - close (); - } - } - - void close () final override { - if (Closed) return; - Closed = true; - Stream->close (); - OnClose (); - } - - bool closed () final override { - bool closed = !Stream->is_open (); - if (closed && !Closed) { - Closed = true; - OnClose (); - } - return closed; - } - - awaitable receive () final override { - error ec; - // if there is no error, bytes_transferred should always be greater than zero. - // if bytes_read == buffer_size, we cannot know if there are more bytes on their - // way. Reconstructing a complete message has to come at a higher level. - size_t bytes_read = co_await Stream->async_read_some (buffer (Buffer.data (), Buffer.size ()), redirect_error (use_awaitable, ec)); - - if (ec) { - close (); - throw exception {ec}; - } - - bytes b (bytes_read); - std::copy (Buffer.begin (), Buffer.begin () + bytes_read, b.begin ()); - co_return b; - } - - ~stream () { - close (); - } - }; - -} - -#endif - diff --git a/include/data/net/beast/http.hpp b/include/data/net/beast/http.hpp deleted file mode 100644 index c81a4257..00000000 --- a/include/data/net/beast/http.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef DATA_NET_HTTP_BEAST_HTTP -#define DATA_NET_HTTP_BEAST_HTTP - -// Copyright (c) 2021 Katrina Knight -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include - -#include - -// we use this for HTTP. -namespace data::net::HTTP::beast { - using namespace boost::beast; // from - namespace net = boost::asio; // from - using tcp = boost::asio::ip::tcp; // from - namespace ssl = boost::asio::ssl; // from - - using request = http::request; - using response = http::response; - - // convert to beast format - request to (const HTTP::request &r); - - response to (const HTTP::response &r); - - HTTP::response from (const response &res); - - HTTP::request from (const request &); -} - -#endif diff --git a/include/data/net/email.hpp b/include/data/net/email.hpp deleted file mode 100644 index a9e6a7b2..00000000 --- a/include/data/net/email.hpp +++ /dev/null @@ -1,585 +0,0 @@ - -// Copyright (c) 2024 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_EMAIL -#define DATA_NET_EMAIL - -#include -#include -#include - -// https://www.rfc-editor.org/rfc/rfc5322 -// https://www.rfc-editor.org/rfc/rfc6854 - -namespace data::net::email { - - struct address; - struct mailbox; - struct message; - struct header; - - std::istream &operator >> (std::istream &, address &); - std::istream &operator >> (std::istream &, mailbox &); - std::istream &operator >> (std::istream &, message &); - std::istream &operator >> (std::istream &, header &); - - writer &operator << (writer &, const address &); - writer &operator << (writer &, const mailbox &); - writer &operator << (writer &, const message &); - writer &operator << (writer &, const header &); - - reader &operator >> (reader &, address &); - reader &operator >> (reader &, mailbox &); - reader &operator >> (reader &, message &); - reader &operator >> (reader &, header &); - - struct address : ASCII { - using ASCII::ASCII; - address (const ASCII &x) : ASCII {x} {} - - address (const ASCII &local, const net::domain_name &domain); - address (const ASCII &local, const IP::address &); - address (const ASCII &display, const ASCII &local, const net::domain_name &domain); - address (const ASCII &display, const ASCII &local, const IP::address &); - - static bool valid (string_view); - bool valid () const; - - static string_view display_name (string_view); - static string_view local_part (string_view); - static string_view domain (string_view); - static string_view domain_name (string_view); - static string_view ip_address (string_view); - static string_view addr_spec (string_view); - - // addr_spec is what most people think of as an email address. - // you know, like "m@q.com". According to the RFC, an address - // can include a display name or could be a group address. - maybe
addr_spec () const; - - maybe local_part () const; - maybe domain () const; - - maybe display_name () const; - maybe domain_name () const; - maybe ip_address () const; - }; - - // a mailbox is more like what people think of as an email address. - // it is an address for an individual person, whereas address can - // also include group addresses. - struct mailbox : address { - using address::address; - - static bool valid (string_view); - bool valid () const; - - mailbox addr_spec () const; - - ASCII local_part () const; - ASCII domain () const; - }; - - struct field : ASCII { - enum name { - optional, - from, - sender, - reply_to, - to, - cc, - bcc, - message_id, - in_reply_to, - references, - subject, - comments, - keywords, - return_path, - received, - resent_date, - resent_from, - resent_sender, - resent_to, - resent_cc, - resent_bcc, - resent_msg_id, - orig_date - }; - - static ASCII to_string (name); - - using ASCII::ASCII; - field (name n) : field {to_string (n)} {} - field (const ASCII &x) : ASCII {x} {} - - static bool valid (string_view); - bool valid () const; - }; - - struct header : ASCII { - using ASCII::ASCII; - header (const ASCII &x) : ASCII {x} {}; - header (const field &f, const ASCII &v); - - static string_view name (string_view); - static string_view value (string_view); - - field name () const; - ASCII value () const; - - static bool valid (string_view); - bool valid () const; - }; - - using headers = list
; - - struct date_time : ASCII { - using ASCII::ASCII; - date_time (const ASCII &); - - static bool valid (string_view); - bool valid () const; - }; - - // a msg_id looks similar to an email address but is less restrictive. - struct msg_id : ASCII { - using ASCII::ASCII; - msg_id (const ASCII &); - - static bool valid (string_view); - bool valid () const; - }; - - struct message : ASCII { - using ASCII::ASCII; - message (const ASCII &x): ASCII {x} {} - - // an email message is divided into lines divided by CRLF. - static list lines (const ASCII &x); - - static list> headers (string_view); - - // body is optional - static string_view body (string_view); - - email::headers headers () const; - - ASCII body () const; - - static bool valid (string_view); - bool valid () const; - - // lines are not more than 78 chars. - // message_id field is included. - static bool standard (string_view); - bool standard () const; - - message (email::headers, const ASCII &body); - - // header values having to do with information on - // the sender and receiver. - struct sent_info { - // these are the only two required fields. - date_time Date; - address From; - maybe
Sender; - - maybe MessageID; - - // one of these fields will be present. - maybe> To; - maybe> Cc; - maybe> Bcc; - }; - - // more than one indicates a resend, most recent first. - list sent () const; - - maybe subject () const; - maybe> reply_to () const; - - maybe> in_reply_to () const; - maybe> references () const; - - list comments () const; - list> keywords () const; - - // put a list of headers here followed by the body. - template - explicit message (const header &, P... p); - - // for writing a message before you send it. - struct write { - sent_info Sent; - - maybe Subject; - list Comments; - list> Keywords; - - maybe> ReplyTo; - maybe> InReplyTo; - maybe> References; - - list
Optional; - - maybe Body; - }; - - message (const write &); - - // construct headers for a reply message. - write reply () const; - - write read (string_view) const; - write read () const; - - // add appropriate resend fields. - message resend () const; - - struct trace { - mailbox ReturnPath; - string ReceivedToken; - date_time ReceivedTime; - }; - - list traces () const; - - message add_trace (const trace &); - - static string_view date (string_view); - static string_view from (string_view); - static string_view message_id (string_view); - static string_view subject (string_view); - static string_view sender (string_view); - static string_view reply_to (string_view); - static string_view to (string_view); - static string_view cc (string_view); - static string_view bcc (string_view); - static string_view in_reply_to (string_view); - static string_view references (string_view); - static list comments (string_view); - static list keywords (string_view); - static list return_path (string_view); - static list received (string_view); - static list resent_date (string_view); - static list resent_from (string_view); - static list resent_message_id (string_view); - static list resent_sender (string_view); - static list resent_to (string_view); - static list resent_cc (string_view); - static list resent_bcc (string_view); - }; - - struct date : header { - date (const date_time &d): header {field::orig_date, d} {} - }; - - struct from : header { - from (list
); - }; - - struct sender : header { - sender (const address &a): header {field::sender, a} {} - }; - - struct reply_to : header { - reply_to (list
); - }; - - struct to : header { - to (list
); - }; - - struct cc : header { - cc (list
); - }; - - struct bcc : header { - bcc (list
); - }; - - struct message_id : header { - message_id (const msg_id &d): header {field::message_id, d} {} - }; - - struct in_reply_to : header { - in_reply_to (list); - }; - - struct references : header { - references (list); - }; - - struct subject : header { - subject (const ASCII &x) : header {field::subject, x} {} - }; - - struct comments : header { - comments (const ASCII &c) : header {field::comments, c} {} - }; - - struct keywords : header { - keywords (list); - }; - - struct return_path : header { - return_path (const mailbox &m) : header {field::return_path, m} {} - }; - - struct received : header { - received (const string &token, const date_time &); - }; - - struct resent_date : header { - resent_date (const date_time &d): header {field::orig_date, d} {} - }; - - struct resent_from : header { - resent_from (list
); - }; - - struct resent_sender : header { - resent_sender (list
); - }; - - struct resent_to : header { - resent_to (list
); - }; - - struct resent_cc : header { - resent_cc (list
); - }; - - struct resent_bcc : header { - resent_bcc (list
); - }; - - struct resent_message_id : header { - resent_message_id (const msg_id &d): header {field::message_id, d} {} - }; - - namespace { - struct incomplete_message { - - headers Headers; - ASCII Body; - - incomplete_message attach (const header &h) { - return incomplete_message {Headers << h, Body}; - } - - incomplete_message attach (const ASCII &b) { - return incomplete_message {Headers, b}; - } - - template - incomplete_message attach (const header &h, P... p) { - return attach (h).attach (p...); - } - - operator message () const { - return message {Headers, Body}; - } - }; - - } - - template - inline message::message (const header &h, P... p) { - *this = message (incomplete_message {}.attach (h, p...)); - } - - bool inline address::valid () const { - return valid (*this); - } - - bool inline mailbox::valid () const { - return valid (*this); - } - - bool inline field::valid () const { - return valid (*this); - } - - bool inline header::valid () const { - return valid (*this); - } - - bool inline date_time::valid () const { - return valid (*this); - } - - bool inline msg_id::valid () const { - return valid (*this); - } - - bool inline message::valid () const { - return valid (*this); - } - - bool inline message::standard () const { - return standard (*this); - } - - writer inline &operator << (writer &b, const address &a) { - b.write ((byte const *) (a.data ()), a.size ()); - return b; - } - - writer inline &operator << (writer &b, const mailbox &m) { - b.write ((byte const *) (m.data ()), m.size ()); - return b; - } - - writer inline &operator << (writer &b, const message &m) { - b.write ((byte const *) (m.data ()), m.size ()); - return b; - } - - writer inline &operator << (writer &b, const header &h) { - b.write ((byte const *) (h.data ()), h.size ()); - return b; - } - - inline address::address (const ASCII &local, const net::domain_name &domain): ASCII {local + "@" + domain} {} - - inline address::address (const ASCII &display, const ASCII &local, const net::domain_name &domain): - ASCII {display + " <" + local + "@" + domain + ">"} {} - - inline address::address (const ASCII &local, const IP::address &addr): ASCII {local + "@[" + addr + "]"} {} - - inline address::address (const ASCII &display, const ASCII &local, const IP::address &addr): - ASCII {display + " <" + local + "@[" + addr + "]>"} {} - - inline header::header (const field &f, const ASCII &v) : ASCII {f + ":" + v} {} - - maybe inline address::local_part () const { - string_view x = local_part (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - maybe inline address::domain () const { - string_view x = domain (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - maybe inline address::display_name () const { - string_view x = display_name (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - maybe inline address::domain_name () const { - string_view x = domain_name (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - maybe
inline address::addr_spec () const { - string_view x = addr_spec (*this); - if (x.data () == nullptr) return {}; - return data::ASCII {x}; - } - - mailbox inline mailbox::addr_spec () const { - string_view x = address::addr_spec (*this); - if (x.data () == nullptr) throw exception {} << "invalid mailbox"; - return data::ASCII {x}; - } - - ASCII inline mailbox::local_part () const { - string_view x = address::local_part (*this); - if (x.data () == nullptr) throw exception {} << "invalid mailbox"; - return data::ASCII {x}; - } - - ASCII inline mailbox::domain () const { - string_view x = address::domain (*this); - if (x.data () == nullptr) throw exception {} << "invalid mailbox"; - return data::ASCII {x}; - } - - field inline header::name () const { - return field {header::name (*this)}; - } - - ASCII inline header::value () const { - return data::ASCII {header::value (*this)}; - } - - list inline message::lines (const ASCII &x) { - char CRLF[] = {13, 10}; - return split (x, std::string {CRLF}); - } - - string_view inline header::name (string_view x) { - auto z = split (x, ":"); - if (z.size () < 2) throw exception {} << "invalid header"; - return z[1]; - } - - string_view inline header::value (string_view x) { - auto z = split (x, ":"); - if (z.size () < 2) throw exception {} << "invalid header"; - return string_view {x.data () + z[1].size () + 1, x.size () - z[1].size () - 1}; - } - - email::headers inline message::headers () const { - return lift ([] (std::pair x) -> email::header { - return email::header {email::field (x.first), ASCII (x.second)}; - }, headers (*this)); - } - - ASCII inline message::body () const { - return ASCII {body (*this)}; - } - - list inline message::comments () const { - return lift ([] (string_view x) -> ASCII { - return ASCII {x}; - }, comments (*this)); - } - - maybe inline message::subject () const { - string_view x = message::subject (*this); - if (x.data () == nullptr) return {}; - return {data::ASCII {x}}; - } - - inline from::from (list
x) : header {field::from, string_join (riffle (x, ", "))} {} - - inline reply_to::reply_to (list
x) : header {field::reply_to, string_join (riffle (x, ", "))} {} - - inline to::to (list
x) : header {field::to, string_join (riffle (x, ", "))} {} - - inline cc::cc (list
x) : header {field::cc, string_join (riffle (x, ", "))} {} - - inline bcc::bcc (list
x) : header {field::bcc, string_join (riffle (x, ", "))} {} - - inline in_reply_to::in_reply_to (list x) : header {field::in_reply_to, string_join (riffle (x, ", "))} {} - - inline references::references (list x) : header {field::references, string_join (riffle (x, ", "))} {} - - inline keywords::keywords (list x): header {field::keywords, string_join (riffle (x, ", "))} {} - - inline resent_from::resent_from (list
x) : header {field::resent_from, string_join (riffle (x, ", "))} {} - - inline resent_sender::resent_sender (list
x) : header {field::resent_sender, string_join (riffle (x, ", "))} {} - - inline resent_to::resent_to (list
x) : header {field::resent_to, string_join (riffle (x, ", "))} {} - - inline resent_cc::resent_cc (list
x) : header {field::resent_cc, string_join (riffle (x, ", "))} {} - - inline resent_bcc::resent_bcc (list
x) : header {field::resent_bcc, string_join (riffle (x, ", "))} {} - - inline received::received (const string &token, const date_time &time): header {field::received, token + "; " + time} {} - -} - - -#endif diff --git a/include/data/net/error.hpp b/include/data/net/error.hpp deleted file mode 100644 index 9f80988e..00000000 --- a/include/data/net/error.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2024 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_ERROR -#define DATA_NET_ERROR - -#include -#include -#include - -// https://www.rfc-editor.org/rfc/rfc7159 -// https://www.rfc-editor.org/rfc/rfc7807 - -namespace data::net { - - struct error : JSON { - bool valid () const; - - /* - "type" (string) - A URI reference [RFC3986] that identifies the - problem type. This specification encourages that, when - dereferenced, it provide human-readable documentation for the - problem type (e.g., using HTML [W3C.REC-html5-20141028]). When - this member is not present, its value is assumed to be - "about:blank". */ - URL type () const; - - /* - "title" (string) - A short, human-readable summary of the problem - type. It SHOULD NOT change from occurrence to occurrence of the - problem, except for purposes of localization (e.g., using - proactive content negotiation; see [RFC7231], Section 3.4). */ - maybe title () const; - - /* - "status" (number) - The HTTP status code ([RFC7231], Section 6) - generated by the origin server for this occurrence of the problem. */ - maybe status () const; - - /* - "detail" (string) - A human-readable explanation specific to this - occurrence of the problem. */ - maybe detail () const; - - /* - "instance" (string) - A URI reference that identifies the specific - occurrence of the problem. It may or may not yield further - information if dereferenced. */ - maybe instance () const; - - error (JSON &&j) : JSON {j} {} - error (const JSON &j) : JSON {j} {} - - // none of these fields are actually required. - static bool valid (const JSON &j); - static URL type (const JSON &); - static maybe title (const JSON &); - static maybe status (const JSON &); - static maybe detail (const JSON &); - static maybe instance (const JSON &); - }; - - bool inline error::valid () const { - return valid (*this); - } - - URL inline error::type () const { - return type (*this); - } - - maybe inline error::title () const { - return title (*this); - } - - maybe inline error::status () const { - return status (*this); - } - - maybe inline error::detail () const { - return detail (*this); - } - - maybe inline error::instance () const { - return instance (*this); - } - - bool inline error::valid (const JSON &j) { - return j.is_object (); - } - - URL inline error::type (const JSON &j) { - return j.contains ("type") ? j["type"] : "about:blank"; - } - -} - -#endif diff --git a/include/data/net/serialized.hpp b/include/data/net/serialized.hpp deleted file mode 100644 index 2c45d385..00000000 --- a/include/data/net/serialized.hpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the Open BSV software license, see the accompanying file LICENSE. - -#ifndef DATA_NET_SERIALIZED -#define DATA_NET_SERIALIZED - -#include -#include -#include -#include -#include - -namespace data::net { - - // open a serialized stream with a parser, meaning that the serialized message type - // can be read as a stream to determine where a message begins ends. We would use - // this with something like a TCP connection. - template - open serialized_stream_parsed ( - open Open, - function Generator, - function> (handler)> Parser); - - // open a serialized stream with a reader, meaning that string that is received is known - // to be a complete message and we just read it into the deserialized type. We would use - // this with a websockets connection or something like that. - template - open serialized_stream_read ( - open Open, - function Generator, - function Reader); - - // same thing but with strings. - template - open serialized_stream_parsed ( - open Open, - function Generator, - function> (handler)> Parser); - - template - open serialized_stream_read ( - open Open, - function Generator, - function Reader); - - // an implementation of session that takes a message type that can be serialized into a string. - template - struct serialized_out_stream final : out_stream { - - awaitable send (out o) final override { - co_return co_await Out->send (Generator (o)); - }; - - void close () final override { - return Out->close (); - }; - - serialized_out_stream (function g, ptr> o) : Generator {g}, Out {o} {} - - private: - // the low-level session. - ptr> Out; - - // write as a string. - function Generator; - - }; - - // open a serialized stream with a parser, meaning that the serialized message type - // can be read as a stream to determine where a message begins ends. We would use - // this with something like a TCP connection. - template - open serialized_stream_parsed ( - open Open, - function Generator, - function> (handler)> Parser) { - // returning a function that opens a lower level channel and attaches - // the parser and generator to it. - open open_serialized = [Open, Generator, Parser] - (close_handler on_close, interaction interact) -> awaitable>> { - - // the high level out stream. - ptr> high_level_out; - - interaction low_level_interaction = - [&high_level_out, interact, Generator, Parser] - (ptr> low_level_out) -> handler { - - // Here we actually create the high level out stream. - // this won't happen until high_level_out actually gets called, however. - high_level_out = std::make_shared> (Generator, low_level_out); - - // return the in handler. - return [ - Parse = Parser ( - [In = interact (std::static_pointer_cast> (high_level_out))] - (in i) { - In (i); - })] (const bytes &i) -> void { - Parse->write (i.data (), i.size ()); - }; - - }; - - // open the low level channel. We get a low level out stream - // but we don't use it because it gets set in here. - co_await Open (on_close, low_level_interaction); - - // We have to assume that low_level_interaction - // will get called so that high_level_out will be set. - // TODO throw if high_level_out is nullptr. - co_return static_pointer_cast> (high_level_out); - }; - return open_serialized; - } - - // open a serialized stream with a reader, meaning that string that is received is known - // to be a complete message and we just read it into the deserialized type. We would use - // this with a websockets connection or something like that. - template - open serialized_stream_read ( - open Open, - function Generator, - function Reader) { - // returning a function that opens a lower level channel and attaches - // the reader and the generator to it. - open open_serialized = [Open, Generator, Reader] - (close_handler on_close, interaction interact) -> awaitable>> { - - // the high level out stream. - ptr> high_level_out; - - interaction low_level_interaction = [&high_level_out, interact, Generator, Reader] - (ptr> low_level_out) -> handler { - // Here we actually create the high level out stream. - // this won't happen until high_level_out actually gets called, however. - high_level_out = std::make_shared> (Generator, low_level_out); - - return [In = interact (std::static_pointer_cast> (high_level_out)), Reader] - (const bytes &x) { - In (Reader (x)); - }; - - }; - - // open the low level channel. We get a low level out stream - // but we don't use it because it gets set in here. - co_await Open (on_close, low_level_interaction); - - // We have to assume that low_level_interaction - // will get called so that high_level_out will be set. - co_return static_pointer_cast> (high_level_out); - }; - return open_serialized; - } - - -} - -#endif - - diff --git a/include/data/net/stream.hpp b/include/data/net/stream.hpp deleted file mode 100644 index 9c0aaf70..00000000 --- a/include/data/net/stream.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_STREAM -#define DATA_NET_STREAM - -#include -#include - -namespace data::net { - - // A basic session type. Not every session - // works exactly the same way, so - // you are not required to use this. - - template - struct out_stream { - // throw data::exception on failure. - virtual awaitable send (message) = 0; - virtual void close () = 0; - virtual ~out_stream () {} - }; - - template - struct in_stream { - // throw data::exception on failure. - virtual awaitable receive () = 0; - virtual bool closed () = 0; - virtual ~in_stream () {} - }; - - template - struct stream : in_stream, out_stream { - virtual ~stream () {} - }; - - using close_handler = function; - - // interaction with a remote peer. - template - using interaction = function> (ptr>)>; - - // a function to open a new session. - // you don't get an in session because you already - // gave the function a way to respond to incoming messages. - template - using open = function>> (close_handler, interaction)>; - -} - -#endif - - diff --git a/include/data/net/websocket.hpp b/include/data/net/websocket.hpp deleted file mode 100644 index 11951291..00000000 --- a/include/data/net/websocket.hpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2022-2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_NET_WEBSOCKET -#define DATA_NET_WEBSOCKET - -#include -#include -#include - -namespace data::net::websocket { - - // open a websocket connection. - void open ( - asio::io_context &, - const URL &, - HTTP::SSL *, - asio::error_handler error_handler, - close_handler, - interaction); - - void open_secure ( - asio::io_context &, - const URL &, - HTTP::SSL &, - asio::error_handler error_handler, - close_handler, - interaction); - - void open_insecure ( - asio::io_context &, - const URL &, - asio::error_handler error_handler, - close_handler, - interaction); - -} - -#include -#include -#include -#include -#include - -namespace data::net::websocket { - - namespace beast = boost::beast; - namespace websocket = beast::websocket; - namespace net = boost::asio; - using tcp = net::ip::tcp; - - class WebSocketSession : public std::enable_shared_from_this { - public: - explicit WebSocketSession (tcp::socket socket) : ws_ (std::move (socket)) {} - - void run () { - ws_.async_accept (beast::bind_front_handler(&WebSocketSession::on_accept, shared_from_this())); - } - - private: - void on_accept (beast::error_code ec) { - if (ec) { - std::cerr << "Accept error: " << ec.message () << std::endl; - return; - } - - read (); - } - - void read () { - ws_.async_read (buffer_, beast::bind_front_handler (&WebSocketSession::on_read, shared_from_this ())); - } - - void on_read (beast::error_code ec, std::size_t bytes_transferred) { - if (ec) { - std::cerr << "Read error: " << ec.message () << std::endl; - return; - } - - ws_.text (ws_.got_text ()); - ws_.async_write (buffer_.data (), beast::bind_front_handler (&WebSocketSession::on_write, shared_from_this ())); - - buffer_.consume (bytes_transferred); - } - - void on_write (beast::error_code ec, std::size_t) { - if (ec) { - std::cerr << "Write error: " << ec.message() << std::endl; - return; - } - - read (); - } - - websocket::stream ws_; - beast::flat_buffer buffer_; - }; -/* - void accept_connection (tcp::acceptor &acceptor) { - acceptor.async_accept([&] (beast::error_code ec, tcp::socket socket) { - if (!ec) std::make_shared (std::move (socket))->run (); - accept_connection (acceptor); - }); - } - - int main () { - const auto address = net::ip::make_address("127.0.0.1"); - const auto port = static_cast(8080); - - net::io_context ioc; - tcp::acceptor acceptor {ioc, {address, port}}; - - std::cout << "WebSocket server listening on " << address << ":" << port << std::endl; - accept_connection (acceptor); - ioc.run (); - - return 0; - }*/ -} - -#endif diff --git a/include/data/string.hpp b/include/data/string.hpp index cd4986f3..cbc2c3f3 100644 --- a/include/data/string.hpp +++ b/include/data/string.hpp @@ -10,6 +10,9 @@ namespace data { + // functions of string: + // * easily convertible to bytes + // the only difference between data::string and std::string is that // data::string prints with "" around it. struct string : std::string { diff --git a/include/data/tools/circular_queue.hpp b/include/data/tools/circular_queue.hpp deleted file mode 100644 index 35f32edf..00000000 --- a/include/data/tools/circular_queue.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_CIRCULAR_QUEUE_H -#define DATA_CIRCULAR_QUEUE_H - -#include -#include -#include -#include - -namespace data::tools { - template - struct circular_queue { - // Initialize front and rear - int cur; - - // Circular Queue - size_t size; - std::vector circularQueue; - - explicit circular_queue (size_t sz, T init_value = T {0}) { - cur = 0; - size = sz; - circularQueue.resize (sz); - std::fill (circularQueue.begin (), circularQueue.end (), init_value); - } - - void set (const T &val); - const T &get (); - }; - - template - void circular_queue::set (const T &val) { - circularQueue[cur] = val; - cur = (cur + 1) % size; - } - - template - const T &circular_queue::get () { - T &x = circularQueue[cur]; - return x; - } - -} - -#endif //DATA_CIRCULAR_QUEUE_H diff --git a/include/data/tools/rate_limiter.hpp b/include/data/tools/rate_limiter.hpp deleted file mode 100644 index 90dff6bc..00000000 --- a/include/data/tools/rate_limiter.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef DATA_RATE_LIMITER_H -#define DATA_RATE_LIMITER_H - -#include -#include "circular_queue.hpp" -#include -#include - -namespace data { - - using millisecond = std::chrono::milliseconds; - - // Do you have an operation that cannot be repeated too quicky? - // for example, a free API that is rate-limited? Use this to - // limit your requests to the required time interval. - struct rate_limiter { - // the rate limiter will not allow more actions than 'hits' per 'duration'. - - rate_limiter (size_t hits, millisecond duration) : - m_queue (hits), m_duration (duration), mutex {std::make_shared ()} {}; - - // how much time we need to wait until an action can be taken? - millisecond get_time (); - - // use this to make an unlimited rate limiter that does nothing. - rate_limiter () : m_queue (0), m_duration (0), mutex {} {}; - - private: - tools::circular_queue m_queue; - millisecond m_duration; - ptr mutex; - }; -} - -#endif //DATA_RATE_LIMITER_H diff --git a/include/data/tools/schema.hpp b/include/data/tools/schema.hpp deleted file mode 100644 index 933deb51..00000000 --- a/include/data/tools/schema.hpp +++ /dev/null @@ -1,1880 +0,0 @@ - -#ifndef DATA_TOOLS_SCHEMA -#define DATA_TOOLS_SCHEMA -/** - * @file schema.hpp - * - * Facilities for validating and decoding map-like string→string data - * structures (e.g. URL query parameters, form data, headers). - * - * A schema is expressed as a composable *rule*. Given a map and a rule, - * validation both checks structural correctness and decodes values into - * typed results. - * - * The core operation is: - * - * validate (data::map, rule) - * validate (std::map, rule) - * - * If validation succeeds, a value is returned whose type is determined - * entirely by the rule. If validation fails, an error is produced. - * - * --------------------------------------------------------------------- - * Mental model - * --------------------------------------------------------------------- - * - * A rule is best understood as a *typed transducer*: - * - * map → typed C++ value - * - * Validation is not just a boolean check. If validation succeeds, the - * rule *produces a value* whose type is determined entirely by the rule - * expression itself. - * - * Conceptually: - * - * input map - * | - * v - * [ rule expression ] - * | - * v - * decoded, strongly-typed result - * - * Rules can be composed to form larger schemas. Composition determines - * both the structural constraints on the map and the structure of the - * resulting C++ type. - * - * For example: - * - * key ("x") && key ("y") - * - * can be visualized as: - * - * key ("x") key ("y") - * | | - * +--------- && ---------+ - * | - * v - * tuple - * - * In this sense, rule operators are not merely logical combinators: - * - * • && constructs product types (tuples) - * • || constructs sum types (variants) - * • * constructs optional types - * • - enforces closed-world constraints on accepted keys - * - * The intent is that schemas can be read declaratively while remaining - * fully type-safe, with result types deduced at compile time from the - * rule expression. - * - * --------------------------------------------------------------------- - * Design notes - * --------------------------------------------------------------------- - * - * • Rules describe *both* structure and decoding. - * • Result types are determined statically by the rule expression. - * • Composition preserves meaning: schemas can be built bottom-up. - * • No implicit acceptance of unknown keys unless explicitly allowed. - * - * This allows map-like inputs (such as URL queries) to be validated - * declaratively while remaining fully type-safe. - * - * --------------------------------------------------------------------- - * Basic rules - * --------------------------------------------------------------------- - * - * empty () - * Match an empty map. - * Result type: void - * - * key ("name") - * Require the presence of key "name" and parse its value as X. - * Result type: X - * - * key ("name", X default_value) - * Optional key "name". If missing, default_value is supplied. - * Result type: X - * - * --------------------------------------------------------------------- - * Rule modifiers - * --------------------------------------------------------------------- - * - * *rule - * Make a rule optional. - * Result type: maybe - * - * -rule (ONLY) - * Restrict the map so that it may not contain keys other than those - * explicitly mentioned in the rule. - * Result type: same as rule - * - * +rule (INVERT ONLY) - * Invert the meaning of the ONLY restriction. - * Result type: same as rule - * - * --------------------------------------------------------------------- - * Rule composition - * --------------------------------------------------------------------- - * - * ruleA && ruleB - * The map must satisfy both rules. - * Result type: - * tuple - * - * If either result is itself a tuple, it is flattened into the result - * (behaves like a parameter pack). - * - * Special case: - * -ruleA && -ruleB is equivalent to -(ruleA && ruleB) - * - * ruleA || ruleB - * The map must satisfy exactly one of the rules. - * Result type: - * std::variant - * - * --------------------------------------------------------------------- - * Closed vs open rules - * --------------------------------------------------------------------- - * - * Rules are *closed by default*. A rule describes a complete schema for - * the map, not a partial constraint. - * - * In particular: - * - * key("k") - * - * means that the map: - * • contains the key "k" - * • contains no other keys - * - * As a consequence: - * - * -key("k") - * - * does not further restrict the rule and has no additional effect. - * - * The '+' operator is used to *open* a rule: - * - * +key("k") - * - * means that the map must contain "k", but may contain additional keys - * beyond those mentioned in the rule. - * - * The '-' operator becomes meaningful primarily after rule composition, - * where it enforces that no keys other than those implied by the composed - * rule are present. - * - * --------------------------------------------------------------------- - * Examples - * --------------------------------------------------------------------- - * - * Example 1: Require an empty query - * - * auto r = empty (); - * validate (query, r); // returns void - * - * Example 2: Required and optional keys - * - * auto r = - * key ("page") && - * key ("limit", 10); - * - * // Result type: tuple - * auto [page, limit] = validate (query, r); - * - * Example 3: Optional rule - * - * auto r = *key("search"); - * - * // Result type: std::optional - * auto value = validate(query, r); - * - * Example 4: Allow other keys to be present - * - * auto r = -( - * key("x") && - * key("y") - * ); - * - * // Map may contain values other than "x" and "y", which are ignored. - * auto [x, y] = validate(query, r); - * - * Example 5: Alternatives - * - * auto r = - * key("id") || - * key("name"); - * - * // Result type: std::variant - * auto v = validate(query, r); - * - * --------------------------------------------------------------------- - * Reading values from strings - * --------------------------------------------------------------------- - * - * Rules that decode values (such as key (...)) rely on a uniform - * conversion mechanism: - * - * data::encoding::read {} (std::string) - * - * This function object is responsible for converting a string into a - * value of type X. Users may provide template specializations of - * data::encoding::read to define custom parsing behavior for their - * own types. - * - * The default behavior is: - * - * 1. For std::integral types, attempt conversion using std::from_chars - * 2. Attempt extraction using operator >> with an - * std::istream - * 3. Attempt construction from a string - * - * Validation fails if none of these conversions succeed. - * - * This design keeps schema logic independent of parsing policy: - * schemas describe *what* is required, while data::encoding::read - * defines *how* values of type X are obtained from strings. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace data::schema::rule { - - // a schema for reading map-like data structures - // written as a string. - template struct map; - - // a schema for list-like data structures. - template struct list; - - // a schema for reading map-like data structures that - // are presented with an ordering and possibly repeated keys - // such as an HTTP form and a command line. Not currently - // implemented, so just use map for now. - template struct dispatch; - - // starting here, we have a lot of definitions that you don't - // really need to read. Scroll down until the next note to - // arrive at a natural interface for using the map. - - // specifies that a data structure is empty. - struct empty; - - // specifies that a data structure can have anything. - struct blank; - - // specifies that a map may not have anything else. - template struct only; - - // specifies that a data structure has a given value. - template struct value; - - // specifies that a data structure has a certain number of values. - template ::max ()> struct values; - - template struct default_value; - - // specifies that the structure is equal to a given value. - template struct equal; - -} - -namespace data::schema::map { - // make a rule for an empty map. - constexpr rule::map empty (); - - // make a rule for an a map that can have anything in it. - constexpr rule::map blank (); - - // make a rule that says a map has a given key. - template constexpr rule::map>> key (const string &key); - - // make a rule that says a map has a given key with a given default. - template constexpr rule::map>> key (const string &key, const X &def); - - // A rule allowing multiple keys (at least one). - template constexpr rule::map>> keys (const string &key); -} - -namespace data::schema::list { - // make a rule for an empty list. - constexpr rule::list empty (); - - // make a rule for any value in a list. - constexpr rule::list blank (); - - // make a rule for a list with a single value. - template constexpr rule::list> value (); - - // make a rule for a list with a single value with default. - template constexpr rule::list> value (const X &def); - - // make a rule for a list with a single value that equals a given value. - template constexpr rule::list> equal (const X &val); -} - -namespace data::schema::dispatch { - // make a rule for an empty map. - constexpr rule::dispatch empty (); - - // make a rule for an a map that can have anything in it. - constexpr rule::dispatch blank (); - - // make a rule that says a map has a given key. - template constexpr rule::dispatch>> key (const string &key); - - // make a rule that says a map has a given key with a given default. - template constexpr rule::dispatch>> key (const string &key, const X &def); - - // make a rule that says a map has a given key. - template constexpr rule::dispatch>> keys (const string &key); -} - -// rules that modify other rules. -namespace data::schema::rule { - - // match a rule or match its nonexistence. - template struct optional; - - template struct all; - - template struct any; - - template struct sequence; - - // specifies that a rule cannot match additional unspecified values. - template constexpr auto operator - (const map &); - - // use the + operator to allow a rule to match with extra values. - template constexpr auto operator + (const map &); - - // use the * operator to make a rule optional. - template constexpr auto operator * (const map &); - - template constexpr auto operator * (const list &); - - // use the & operator to combine two map rules. - template auto operator && (const map &, const map &); - - // use the | make alternatives. - template auto operator || (const map &, const map &); - - // join two list schemas together. - template auto operator + (const list &, const list &); - - // specifies that a rule cannot match additional unspecified values. - template constexpr auto operator - (const dispatch &); - - // use the + operator to allow a rule to match with extra values. - template constexpr auto operator + (const dispatch &); - - // use the * operator to make a rule optional. - template constexpr auto operator * (const dispatch &); - - // use the & operator to combine two dispatch rules. - template auto operator && (const dispatch &, const dispatch &); - - // use the | make alternatives. - template auto operator || (const dispatch &, const dispatch &); - - template concept String = /*std::convertible_to && */std::equality_comparable_with; -} - -namespace data::schema { - - // Throw something that inherets from mismatch if validation fails. - struct mismatch {}; - - // If validation succeeds, return a logical type containing - // all parsed values. - template - auto validate (data::map m, const rule::map &r); - - template - auto validate (const std::map &m, const rule::map &r); - - template - auto validate (data::dispatch m, const rule::map &r); - - template - auto validate (data::list m, const rule::list &r); - - template - auto validate (data::stack m, const rule::list &r); - - template - auto validate (const data::cross &m, const rule::list &r); - - // thrown when the value could not be read. - struct invalid_entry : mismatch { - data::string Key; - data::string Value; - constexpr invalid_entry (const data::string &k, const data::string &v) : mismatch {}, Key {k}, Value {v} {} - }; - - // thrown when a key was expected that was not available. - struct missing_key : mismatch { - data::string Key; - constexpr missing_key (const data::string &k) : mismatch {}, Key {k} {} - }; - - struct too_many_keys : mismatch { - data::string Key; - constexpr too_many_keys (const data::string &k) : mismatch {}, Key {k} {} - }; - - // thrown when a key is present which is only valid in some alternative - // rule that doesn't match the whole map or when an optional expression - // is partially matched. - struct incomplete_match : mismatch { - data::string Key; - constexpr incomplete_match (const data::string &k): mismatch {}, Key {k} {} - }; - - // thrown when a map has keys that are not provided by the schema. - struct unknown_key : mismatch { - data::string Key; - constexpr unknown_key (const data::string &k) : mismatch {}, Key {k} {} - }; - - struct invalid_value_at : mismatch { - size_t Position; - constexpr invalid_value_at (size_t pos) : mismatch {}, Position {pos} {} - }; - - struct end_of_sequence : mismatch { - size_t Position; - constexpr end_of_sequence (size_t pos) : mismatch {}, Position {pos} {} - }; - - struct no_end_of_sequence : mismatch { - size_t Position; - constexpr no_end_of_sequence (size_t pos) : mismatch {}, Position {pos} {} - }; - -} - -namespace data::schema::rule { - - template <> struct map { - constexpr map () {} - constexpr map (const map &) {} - }; - - template <> struct map { - constexpr map () {} - constexpr map (map) {} - }; - - template <> struct list { - constexpr list () {} - }; - - template struct map> { - string Key; - }; - - template struct map> { - string Key; - constexpr map (const string &k): Key {k} {} - template - constexpr map (const map> &m): Key {m.Key} {} - }; - - template struct dispatch> { - string Key; - }; - - template struct dispatch> { - string Key; - constexpr dispatch (const string &k): Key {k} {} - template - constexpr dispatch (const dispatch> &m): Key {m.Key} {} - }; - - template struct map> : map> { - X Default; - constexpr map (const string &k, const X &def): map> {k}, Default {def} {} - }; - - template struct map> : map { - using map::map; - constexpr map (const map &m): map {m} {} - }; - - template struct map> : tuple, map...> { - using parent = tuple, map...>; - using tuple, map...>::tuple; - constexpr map (parent &&p): parent {p} {} - - // map rule all is valid if no operand - // has a same key as any other operand. - bool valid () const; - }; - - template struct map> : tuple, map...> { - using parent = tuple, map...>; - using tuple, map...>::tuple; - constexpr map (parent &&p): parent {p} {} - // map rule all is valid if no operand - // has a same key as any other operand. - bool valid () const; - }; - - template struct map> : map { - using map::map; - constexpr map (const map &m): map {m} {} - }; - - template <> struct list {}; - - template struct list> {}; - - template struct list> { - X Default; - }; - - template struct list> { - X Value; - }; - - template - struct is_optional : std::false_type {}; - - template - struct is_optional> : std::true_type {}; - - template - struct is_default : std::false_type {}; - - template - struct is_default> : std::true_type {}; - - template - struct trail : std::true_type {}; - - template - struct trail - : std::bool_constant< - !(is_optional::value && !is_optional::value) && - !(is_default::value && !(is_default::value || is_optional::value)) && - trail::value - > {}; - - template - struct list> { - static_assert ( - trail::value, - "optional must be at the end or followed by optional. default_value must be at the end or followed only by default_value or optional" - ); - - using tuple_type = std::tuple...>; - tuple_type Rules; - - constexpr list (X... xs) - : Rules (std::move (xs)...) {} - - constexpr list (tuple_type &&rules): Rules {rules} {} - }; - - template struct list> : list { - using list::list; - constexpr list (const list &m): list {m} {} - }; - - template struct apply_optional { - using result = optional; - }; - - template struct apply_optional> { - using result = values; - }; - - template struct apply_optional> { - using result = only::result>; - }; - - // here is some stuff that says how blank composes. - template struct apply_blank { - using result = X; - }; - - template <> struct apply_blank { - using result = blank; - }; - - template struct apply_blank> { - using result = X; - }; - - template struct apply_only { - using result = only; - }; - - template <> struct apply_only { - using result = empty; - }; - - template <> struct apply_only { - using result = empty; - }; - - template struct apply_only> { - using result = only; - }; - - // here is some stuff that says how blank composes. - template struct intersect; - - template struct intersect { - using result = all; - constexpr map operator () (const map &, const map &) const; - }; - - // intersect anything with empty is itself - template struct intersect { - using result = Y; - constexpr map operator () (map a, const map &b) const; - }; - - template struct intersect { - using result = X; - constexpr map operator () (const map &a, map b) const; - }; - - template struct intersect { - using result = apply_blank::result; - constexpr map operator () (map a, const map &b) const; - }; - - template struct intersect { - using result = apply_blank::result; - constexpr map operator () (const map &a, map b) const; - }; - - template struct intersect, only> { - using result = only::result>; - constexpr map operator () (const map> &, const map> &) const; - }; - - template struct intersect, Y> { - using result = intersect::result; - constexpr map operator () (const map> &a, const map &b) const; - }; - - template struct intersect> { - using result = intersect::result; - constexpr map operator () (const map &a, const map> &b) const; - }; - - template struct intersect, all> { - using result = all; - constexpr map operator () (const map> &, const map> &) const; - }; - - template struct intersect, Y> { - using result = all; - constexpr map operator () (const map> &, const map &) const; - }; - - template struct intersect> { - using result = all; - constexpr map operator () (const map &, const map> &) const; - }; - - template struct unite; - - template struct unite, only> { - using result = only::result>; - constexpr map operator () (const map> &, const map> &) const; - }; - - template struct unite { - using result = any; - constexpr map operator () (const map &, const map &) const; - }; - - template struct unite, Y> { - using result = any; - constexpr map operator () (const map> &, const map &) const; - }; - - template struct unite> { - using result = any; - constexpr map operator () (const map &, const map> &) const; - }; - - template struct unite, any> { - using result = any; - constexpr map operator () (const map> &, const map> &) const; - }; - - template struct join { - using result = sequence; - constexpr list operator () (const list &, const list &) const; - }; - - template struct join, sequence> { - using result = sequence; - constexpr list operator () (const list> &, const list> &) const; - }; - - template struct join> { - using result = sequence; - constexpr list operator () (const list &, const list> &) const; - }; - - template struct join, Y> { - using result = sequence; - constexpr list operator () (const list> &, const list &) const; - }; - - template struct join { - using result = Y; - constexpr list operator () (const list &, const list &) const; - }; - - template struct join { - using result = X; - constexpr list operator () (const list &, const list &) const; - }; - - // specifies that a rule cannot match additional unspecified values. - template constexpr auto inline operator - (const map &m) { - return map::result> {m}; - } - - // use the + operator to allow a rule to match with extra values. - template constexpr auto inline operator + (const map &m) { - return map::result> {m}; - } - - // use the * operator to make a rule optional. - template constexpr auto inline operator * (const map &m) { - return map::result> {m}; - } - - template constexpr auto inline operator * (const list &m) { - return list::result> {m}; - } - - template constexpr auto inline operator * (const dispatch &m) { - return dispatch::result> {m}; - } - - // use the & operator to combine two map rules. - template auto inline operator && (const map &a, const map &b) { - return intersect {} (a, b); - } - - // use the | make alternatives. - template auto inline operator || (const map &a, const map &b) { - return unite {} (a, b); - } - - template auto inline operator && (const dispatch &a, const dispatch &b) { - return intersect {} (a, b); - } - - template auto inline operator || (const dispatch &a, const dispatch &b) { - return unite {} (a, b); - } - - // join two list schemas together. - template auto inline operator + (const list &a, const list &b) { - return join {} (a, b); - } - -} - -namespace data::schema::rule { - - template struct validate; - - using unit = std::monostate; - - template struct validate { - using result = unit; - - template - result operator () (const any &m, map r); - - template - result operator () (const any &m, dispatch r); - - template - result sequence (iterator &i, sen s, const list &r, size_t index); - }; - - template struct validate { - using result = unit; - - template - result operator () (const seq &x, const list &r); - - template - result operator () (data::map m, const map &r); - - template - result operator () (const std::map &m, const map &r); - - template - result operator () (data::dispatch m, const map &r); - }; - - template struct validate, context...> { - using result = X; - - template - result operator () (data::map m, const map> &r); - - template - result operator () (const std::map &m, const map> &r); - - template - result operator () (data::dispatch m, const map> &r); - - template result operator () (const seq &x, const list> &r); - - template - result sequence (iterator &i, sen s, const list> &r, size_t index); - }; - - template struct validate, context...> { - using result = data::stack; - - template - result operator () (data::map m, const map> &r); - - template - result operator () (const std::map &m, const map> &r); - - template - result operator () (data::dispatch m, const map> &r); - }; - - template struct validate, context...> { - using result = X; - - template - result operator () (data::map m, const map> &r); - - template - result operator () (const std::map &m, const map> &r); - - template - result operator () (data::dispatch m, const map> &r); - - template result operator () (const seq &x, const list> &r); - - template - result sequence (iterator &i, sen s, const list> &r, size_t index); - }; - - template struct validate, context...> { - using result = unit; - - template - result operator () (data::map m, const map> &r); - - template - result operator () (const std::map &m, const map> &r); - - template result operator () (const seq &x, const list> &r); - - template - result sequence (iterator &i, sen s, const list> &r, size_t index); - }; - - template struct validate, context...> { - using result = maybe::result>; - - template - result operator () (data::map m, const map> &r); - - template - result operator () (data::dispatch m, const map> &r); - - template - result operator () (const std::map &m, const map> &r); - - template result operator () (const seq &x, const list> &r); - - template - result sequence (iterator &i, sen s, const list> &r, size_t index); - }; - - template struct validate, context...> { - using result = tuple::result...>; - - template result operator () (const seq &x, const list> &r); - }; - - template struct validate, context...> { - using result = tuple::result...>; - template - result operator () (const Map &m, const map> &r); - }; - - template struct validate, context...> { - using result = either::result...>; - - template - result operator () (const Map &m, const map> &r); - - }; - - template struct validate, context...> { - using result = typename validate::result; - - template - result operator () (const Map &m, const map> &r); - }; - -} - -namespace data::schema::map { - - // this is the end of the user interface. Next comes definitions of functions. - constexpr typename rule::map inline empty () { - return rule::map {}; - } - - // make a rule that says a map has a given key. - template constexpr typename rule::map>> inline key (const string &key) { - return rule::map>> {rule::map> {key}}; - } - - template constexpr typename rule::map>> inline key (const string &key, const X &def) { - return rule::map>> {rule::map> {key, def}}; - } - - template constexpr typename rule::map>> inline keys (const string &key) { - return rule::map>> {rule::map> {key}}; - } -} - -namespace data::schema::list { - // make a rule for an empty list. - constexpr rule::list inline empty () { - return rule::list {}; - } - - constexpr rule::list inline blank () { - return rule::list {}; - } - - // make a rule for a list with a single value. - template constexpr rule::list> inline value () { - return rule::list> {}; - } - - // make a rule for a list with a single value with default. - template constexpr rule::list> inline value (const X &def) { - return rule::list> {def}; - } - - // make a rule for a list with a single value that equals a given value. - template constexpr rule::list> inline equal (const X &val) { - return rule::list> {val}; - } -} - -namespace data::schema { - - template - auto inline validate (data::map m, const rule::map &r) { - return rule::validate {} (m, r); - } - - template - auto inline validate (data::dispatch m, const rule::map &r) { - return rule::validate {} (m, r); - } - - // we don't really support std::map yet. - template - auto inline validate (const std::map &m, const rule::map &r) { - return rule::validate {} (m, r); - } - - template - auto inline validate (data::list m, const rule::list &r) { - return rule::validate {} (m, r); - } - - template - auto inline validate (data::stack m, const rule::list &r) { - return rule::validate {} (m, r); - } - - template - auto inline validate (const data::cross &m, const rule::list &r) { - return rule::validate {} (m, r); - } -} - -namespace data::schema::rule { - - template template - typename validate::result - validate::operator () (const seq &x, const list &r) { - auto b = x.begin (); - if (b == x.end ()) return {}; - else throw end_of_sequence {0}; - } - - template template - typename validate::result - validate::operator () (data::map m, const map &r) { - auto b = m.begin (); - if (b == m.end ()) return {}; - else throw unknown_key {data::string (b->Key)}; - } - - template template - typename validate::result - validate::operator () (data::dispatch m, const map &r) { - auto b = m.begin (); - if (b == m.end ()) return {}; - else throw unknown_key {data::string (b->Key)}; - } - - template template - typename validate::result - validate::operator () (const std::map &m, const map &r) { - auto b = m.begin (); - if (b == m.end ()) return {}; - else throw unknown_key {b->first}; - } - - template template - typename validate::result inline - validate::operator () (const any &m, map r) { - return {}; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::map m, const map> &r) { - const string *v = m.contains (r.Key); - if (!bool (v)) throw missing_key {data::string (r.Key)}; - maybe x = encoding::read {} (*v); - if (!bool (x)) throw invalid_entry {data::string (r.Key), data::string (*v)}; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::dispatch m, const map> &r) { - auto v = get_values (m, r.Key); - if (data::size (v) > 1) throw too_many_keys {data::string (r.Key)}; - if (data::size (v) == 0) throw missing_key {data::string (r.Key)}; - maybe x = encoding::read {} (data::first (v)); - if (!bool (x)) throw invalid_entry {data::string (r.Key), data::string (data::first (v))}; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (const std::map &m, const map> &r) { - auto v = m.find (r.Key); - if (v == m.end ()) throw missing_key {data::string (r.Key)}; - maybe x = encoding::read {} (v->second); - if (!bool (x)) throw invalid_entry {r.Key, data::string (v->second)}; - return *x; - } - - template template - typename validate, context...>::result - validate, context...>::operator () (data::dispatch m, const map> &r) { - auto v = get_values (m, r.Key); - if (data::size (v) < min) throw missing_key {data::string (r.Key)}; - if (data::size (v) > max) throw too_many_keys {data::string (r.Key)}; - stack res; - for (const auto &z : v) { - maybe x = encoding::read {} (z); - if (!bool (x)) throw invalid_entry {data::string (r.Key), data::string (z)}; - res >>= *x; - } - return data::reverse (res); - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::map m, const map> &r) { - const string *v = m.contains (r.Key); - if (!bool (v)) return r.Default; - maybe x = encoding::read {} (*v); - if (!bool (x)) throw invalid_entry {r.Key, *v}; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::dispatch m, const map> &r) { - auto v = get_values (m, r.Key); - if (data::size (v) > 1) throw too_many_keys {r.Key}; - if (data::size (v) == 0) return r.Default; - maybe x = encoding::read {} (data::first (v)); - if (!bool (x)) throw invalid_entry {data::string (r.Key), data::string (data::first (v))}; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::map m, const map> &r) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - return {}; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (data::dispatch m, const map> &r) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - return {}; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (const std::map &m, const map> &r) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - return {}; - } - - template template - typename validate::result inline - validate::sequence (iterator &i, sen s, const list &r, size_t index) { - if (i == s) throw end_of_sequence {index}; - i++; - return {}; - } - - template template - typename validate, context...>::result inline - validate, context...>::sequence (iterator &i, sen s, const list> &r, size_t index) { - if (i == s) throw end_of_sequence {index}; - maybe x = encoding::read {} (*i); - if (!bool (x)) throw invalid_value_at {index}; - i++; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::sequence (iterator &i, sen s, const list> &r, size_t index) { - if (i == s) return r.Default; - maybe x = encoding::read {} (*i); - if (!bool (x)) throw invalid_value_at {index}; - i++; - return *x; - } - - template template - typename validate, context...>::result inline - validate, context...>::sequence (iterator &i, sen s, const list> &r, size_t index) { - if (i == s) throw end_of_sequence {index}; - maybe x = encoding::read {} (*i); - if (!bool (x) || *x != r.Value) throw invalid_value_at {index}; - i++; - return unit {}; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (const seq &x, const list> &r) { - if (x.size () == 0) throw end_of_sequence {0}; - if (x.size () > 1) throw no_end_of_sequence {1}; - auto i = x.begin (); - return sequence (i, x.end (), r, 0); - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (const seq &x, const list> &r) { - if (x.size () > 1) throw no_end_of_sequence {1}; - auto i = x.begin (); - return sequence (i, x.end (), r, 0); - } - - template template - typename validate, context...>::result - validate, context...>::operator () (const seq &x, const list> &r) { - if (x.size () > 1) throw no_end_of_sequence {1}; - auto i = x.begin (); - return sequence (i, x.end (), r, 0); - } - - template template - typename validate, context...>::result - validate, context...>::sequence (iterator &i, sen s, const list> &r, size_t index) { - using result_type = validate::result; - if (i == s) return {}; - maybe x = encoding::read {} (*i); - if (!bool (x)) throw invalid_value_at {index}; - i++; - return x; - } - - template template - typename validate, context...>::result inline - validate, context...>::operator () (const seq &x, const list> &r) { - if (x.size () == 0) throw end_of_sequence {0}; - if (x.size () > 1) throw no_end_of_sequence {1}; - auto i = x.begin (); - return sequence (i, x.end (), r, 0); - } - - template - struct validate_sequence { - - template - static auto apply (iterator &it, senital end, const list>& schema) { - - // validate current element - auto value = validate::type, context...> { - }.sequence (it, end, std::get (schema.Rules), index); - - if constexpr (index + 1 == sizeof... (X)) { - return std::make_tuple (std::move (value)); - } else { - // recurse - auto tail = validate_sequence::template apply (it, end, schema); - - return std::tuple_cat ( - std::make_tuple (std::move (value)), - std::move (tail) - ); - } - } - }; - - template - struct validate_sequence { - - template - static auto apply (iterator &it, senital end, const list>& schema) { - return tuple<> {}; - } - }; - - template template - typename validate, context...>::result - validate, context...>::operator () (const seq &x, const list> &r) { - auto it = x.begin (); - auto end = x.end (); - - auto result = validate_sequence<0, X...>::template apply (it, end, r); - - // NOTE this is not actually correct. If we had a - // pattern that represented several list entries, - // we would not correctly point out where we expected - // the sequence to end. - if (it != end) throw no_end_of_sequence {sizeof... (X)}; - - return result; - } - - // NOTE: we should be able to get rid of get_rule by - // making context... first in validate. - template struct get_rule; - - template struct get_rule> { - using type = X; - }; - - // a special attribute saying that no keys from alternate - // branches of a rule are allowed. - template struct branch_isolated; - - template struct map> : map { - using map::map; - constexpr map (const map &m): map {m} {} - }; - - template - struct get_keys; - - template - struct get_keys> { - set operator () (const map> &x) const { - return {x.Key}; - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return {x.Key}; - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return get_keys {} (static_cast &> (x)); - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return {x.Key}; - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return get_keys {} (static_cast &> (x)); - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return std::apply ([] (auto &&...elems) { - return (get_keys>>::type> {} (elems) | ...); - }, static_cast...> &> (x)); - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return std::apply ([] (auto &&...elems) { - return (get_keys>>::type> {} (elems) | ...); - }, static_cast...> &> (x)); - } - }; - - template - struct get_keys> { - set operator () (const map> &x) const { - return get_keys {} (static_cast &> (x)); - } - }; - - template - template - typename validate, context...>::result - validate, context...>::operator () (const Map &m, const map> &r) { - using tuple_type = tuple...>; - - constexpr const size_t tuple_size = std::tuple_size_v; - - using rule_type = typename get_rule>::type; - - struct validate_alternative { - result operator () (const Map &m, const map> &r) { - result res {std::in_place_index, - validate {} - (m, std::get (static_cast (r)))}; - return res; - } - }; - - result res; - if constexpr (index + 1 < tuple_size) { - try { - return validate_alternative {} (m, r); - } catch (const mismatch &) { - return operator () (m, r); - } - } else if constexpr (index + 1 == tuple_size) { - return validate_alternative {} (m, r); - } else { - throw data::exception {} << "Invalid index parameter"; - } - - } - - // branch_isolated makes no difference other than any and optional. - - template struct validate>, context...> { - using result = typename validate, context...>::result; - - template - result operator () (data::map m, const map>> &r, set allowed_keys = {}); - - template - result operator () (data::dispatch m, const map>> &r, set allowed_keys = {}); - - template - result operator () (const std::map &m, const map>> &r, set allowed_keys = {}); - }; - - template struct validate>, context...> { - using result = typename validate, context...>::result; - - template - result operator () (const Map &m, const map>> &r); - }; - - template template - typename validate>, context...>::result inline - validate>, context...>::operator () - (data::map m, const map>> &r, set allowed_keys) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - // if the match fails, the map may not contain any keys - // of the sub rule. - for (const string &k: get_keys {} (static_cast> (r))) - if (!allowed_keys.contains (k) && m.contains (k)) throw incomplete_match {data::string (k)}; - return {}; - } - - template template - typename validate>, context...>::result inline - validate>, context...>::operator () - (data::dispatch m, const map>> &r, set allowed_keys) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - // if the match fails, the map may not contain any keys - // of the sub rule. - for (const string &k: get_keys {} (static_cast> (r))) - if (!allowed_keys.contains (k) && get_values (m, k).size () != 0) throw incomplete_match {data::string (k)}; - return {}; - } - - template template - typename validate>, context...>::result inline - validate>, context...>::operator () - (const std::map &m, const map>> &r, set allowed_keys) { - try { - return {validate {} (m, static_cast> (r))}; - } catch (const mismatch &) {} - // if the match fails, the map may not contain any keys - // of the sub rule. - for (const string &k: get_keys {} (static_cast> (r))) - if (!allowed_keys.contains (k) && m.find (k) != m.end ()) throw incomplete_match {data::string (k)}; - return {}; - } - - template - template - typename validate, context...>::result inline - validate, context...>::operator () (const Map &m, const map> &r) { - using tuple_type = tuple...>; - return std::apply ([&m] (auto &&...elems) -> result { - return result {validate>>::type, context...> {} (m, elems)...}; - }, static_cast (r)); - } - - // get all keys from other indices that do not appear in the given index. - template struct get_alternative_keys { - template - set operator () (const map> &r, const set &x = {}) { - using tuple_type = tuple...>; - constexpr const size_t tuple_size = std::tuple_size_v; - if constexpr (recursive_index < tuple_size) { - using rule_type = get_rule (r))>>>::type; - set these_keys = get_keys {} (std::get (r)); - set next_keys; - - if constexpr (index == recursive_index) { - next_keys = x; - for (const string &y : these_keys) next_keys = next_keys.remove (y); - } else { - next_keys = x | these_keys; - } - return operator () (r, next_keys); - } else { - return x; - } - } - }; - - template struct has_no_unused_alternatives; - - template - struct has_no_unused_alternatives, X...> { - unit operator () (const data::map &m, const map> &r) const { - for (const data::string &k: get_alternative_keys {}.template operator () (r)) - if (m.contains (k)) throw incomplete_match {k}; - return unit {}; - } - }; - - template - struct has_no_unused_alternatives, X...> { - unit operator () (const data::dispatch &m, const map> &r) const { - for (const data::string &k: get_alternative_keys {}.template operator () (r)) - if (get_values (m, k).size () != 0) throw incomplete_match {k}; - return unit {}; - } - }; - - template - struct has_no_unused_alternatives, X...> { - unit operator () (const std::map &m, const map> &r) const { - for (const data::string &k: get_alternative_keys {}.template operator () (r)) - if (m.find (k) != m.end ()) throw incomplete_match {k}; - return unit {}; - } - }; - - template - template - typename validate>, context...>::result - validate>, context...>::operator () (const Map &m, const map>> &r) { - using tuple_type = tuple...>; - - constexpr const size_t tuple_size = std::tuple_size_v; - - using rule_type = typename get_rule>::type; - - struct validate_alternative { - result operator () (const Map &m, const map> &r) { - result res {std::in_place_index, - validate {} - (m, std::get (static_cast (r)))}; - has_no_unused_alternatives {} (m, r); - return res; - } - }; - - result res; - if constexpr (index + 1 < tuple_size) { - try { - return validate_alternative {} (m, r); - } catch (const mismatch &) { - return operator () (m, r); - } - } else if constexpr (index + 1 == tuple_size) { - return validate_alternative {} (m, r); - } else { - throw data::exception {} << "Invalid index parameter (this should not happen)"; - } - - } - - template - struct tuple_rule_valid { - template - bool operator () (const tuple...> &m, const set &x = {}) { - constexpr const size_t tuple_size = std::tuple_size_v...>>; - if constexpr (index < tuple_size) { - using rule_type = get_rule (m))>>>::type; - set next_keys = get_keys {} (std::get (m)); - auto intersection = x & next_keys; - if (!data::empty (intersection)) return false; - return operator () (m, x | next_keys); - } - - return true; - } - }; - - template - bool inline map>::valid () const { - if (!std::apply ([] (auto &&...elems) -> bool { - return (data::valid (elems) && ...); - }, static_cast, map...>> (*this))) return false; - return tuple_rule_valid {} (static_cast, map...>&> (*this), {}); - } - - template - bool inline map>::valid () const { - return std::apply ([] (auto &&...elems) -> bool { - return (data::valid (elems) && ...); - }, static_cast, map...>> (*this)); - } - - // check that the map has no other elements than those given. - struct none_else { - template - void operator () (data::map m, const set &x) { - - auto z = m.begin (); - auto n = x.begin (); - - while (z != m.end ()) { - // if we are at the end of the list of allowed keys, - // then there must be keys in the map that aren't allowed. - if (n == x.end ()) throw unknown_key {data::string (z->Key)}; - - if (z->Key > *n) n++; - else { - if (z->Key != *n) throw unknown_key {data::string (z->Key)}; - n++; - z++; - } - } - } - - template - void operator () (const std::map &m, const set &x) { - auto z = m.begin (); - auto n = x.begin (); - - while (z != m.end ()) { - // if we are at the end of the list of allowed keys, - // then there must be keys in the map that aren't allowed. - if (n == x.end ()) throw unknown_key {data::string (z->first)}; - - if (z->first > *n) n++; - else { - if (z->first != *n) throw unknown_key {data::string (z->first)}; - n++; - z++; - } - } - } - - template - void operator () (data::dispatch m, const set &x) { - auto keys = data::get_keys (m); - auto z = keys.begin (); - auto n = x.begin (); - - while (z != keys.end ()) { - // if we are at the end of the list of allowed keys, - // then there must be keys in the map that aren't allowed. - if (n == x.end ()) throw unknown_key {data::string (*z)}; - - if (*z > *n) n++; - else { - if (*z != *n) throw unknown_key {data::string (*z)}; - n++; - z++; - } - } - } - }; - - template struct isolated_type; - - template using isolated = isolated_type::result; - - template <> struct isolated_type { - using result = empty; - }; - - template <> struct isolated_type { - using result = blank; - }; - - template struct isolated_type> { - using result = value; - }; - - template struct isolated_type> { - using result = values; - }; - - template struct isolated_type> { - using result = default_value; - }; - - template struct isolated_type> { - using result = branch_isolated>>; - }; - - template struct isolated_type> { - using result = branch_isolated...>>; - }; - - template struct isolated_type> { - using result = all...>; - }; - - struct isolate { - map operator () (const map &x) { - return x; - } - - map operator () (const map &x) { - return x; - } - - template - map> operator () (const map> &x) { - return x; - } - - template - map> operator () (const map> &x) { - return x; - } - - template - map> operator () (const map> &x) { - return x; - } - - template - map>>> operator () (const map> &x) { - return map>>> {isolate {} (static_cast &> (x))}; - } - - template - map...>> operator () (const map> &x) { - return map...>> {lift ([] (const auto &n) { - return isolate {} (n); - }, static_cast>::parent &> (x))}; - } - - template - map...>>> operator () (const map> &x) { - return map...>>> {lift ([] (const auto &n) { - return isolate {} (n); - }, static_cast>::parent &> (x))}; - } - }; - - template - template - typename validate, context...>::result inline - validate, context...>::operator () (const Map &m, const map> &r) { - none_else {} (m, get_keys> {} (r)); - auto val = validate, context...> {} (m, isolate {} (+r)); - return val; - } - - template - constexpr map::result> inline - intersect::operator () (const map &a, const map &b) const { - return map {a, b}; - } - - template - constexpr map, only>::result> inline - intersect, only>::operator () (const map> &a, const map> &b) const { - return map {intersect {} (static_cast &> (a), static_cast &> (b))}; - } - - template - constexpr map, Y>::result> inline - intersect, Y>::operator () (const map> &a, const map &b) const { - return map {static_cast &> (a), b}; - } - - template - constexpr map>::result> inline - intersect>::operator () (const map &a, const map> &b) const { - return map {a, static_cast &> (b)}; - } - - template - constexpr map, all>::result> inline - intersect, all>::operator () - (const map> &a, const map> &b) const { - return map {std::tuple_cat (a, b)}; - } - - template - constexpr map, Y>::result> inline - intersect, Y>::operator () (const map> &a, const map &b) const { - using tuple_type_A = tuple...>; - return map {std::tuple_cat (static_cast (a), tuple> (b))}; - } - - template - constexpr map>::result> inline - intersect>::operator () (const map &a, const map> &b) const { - using tuple_type_B = tuple...>; - return map {std::tuple_cat (tuple> (a), static_cast (b))}; - } - - template - constexpr map::result> inline - intersect::operator () (map a, const map &b) const { - return b; - } - - template - constexpr map::result> inline - intersect::operator () (const map &a, map b) const { - return a; - } - - template - constexpr map::result> inline - unite::operator () (const map &a, const map &b) const { - return map {a, b}; - } - - template - constexpr map, only>::result> inline - unite, only>::operator () (const map> &a, const map> &b) const { - return map {unite {} (static_cast &> (a), static_cast &> (b))}; - } - - template - constexpr map, Y>::result> inline - unite, Y>::operator () (const map> &a, const map &b) const { - using tuple_type_A = tuple...>; - return map {std::tuple_cat (static_cast (a), tuple> (b))}; - } - - template - constexpr map>::result> inline - unite>::operator () (const map &a, const map> &b) const { - using tuple_type_B = tuple...>; - return map {std::tuple_cat (tuple> (a), static_cast (b))}; - } - - template - constexpr list::result> inline - join::operator () (const list &a, const list &b) const { - return b; - } - - template - constexpr list::result> - join::operator () (const list &a, const list &b) const { - return a; - } - - template - constexpr list::result> inline - join::operator () (const list &a, const list &b) const { - return std::make_tuple (a, b); - } - - template - constexpr list, sequence>::result> inline - join, sequence>::operator () (const list> &a, const list> &b) const { - return std::tuple_cat (a.Rules, b.Rules); - } - - template - constexpr list>::result> inline - join>::operator () (const list &a, const list> &b) const { - return std::tuple_cat (std::make_tuple (a), b.Rules); - } - - template - constexpr list, Y>::result> inline - join, Y>::operator () (const list> &a, const list &b) const { - return std::tuple_cat (a.Rules, std::make_tuple (b)); - } - -} - -#endif diff --git a/include/data/tuple.hpp b/include/data/tuple.hpp index 89507cb7..5977fd2e 100644 --- a/include/data/tuple.hpp +++ b/include/data/tuple.hpp @@ -107,12 +107,10 @@ namespace data { // apply F to each respective element of the given tuples template - constexpr void for_each (F &&f, T &&t, Ts &&...ts) - { - constexpr std::size_t N = - std::tuple_size_v>; + constexpr void for_each (F &&f, T &&t, Ts &&...ts) { + constexpr std::size_t N = std::tuple_size_v>; - static_assert(((std::tuple_size_v> == N) && ...), + static_assert (((std::tuple_size_v> == N) && ...), "All tuples must have the same size"); for_each_impl (std::make_index_sequence {}, std::forward (f), @@ -122,17 +120,13 @@ namespace data { // apply a function to a part of a tuple and return the result. template - constexpr decltype (auto) apply_at (T &&t, F &&f, size_t i) { + constexpr decltype (auto) inline apply_at (T &&t, F &&f, size_t i) { return tuple_apply_at_rec<0> (t, std::forward (f), i); } template - constexpr auto lift(F&& f, const tuple& t) - -> tuple()))...> - { - return lift_impl(std::forward(f), - t, - std::index_sequence_for{}); + constexpr auto inline lift (F&& f, const tuple &t) -> tuple()))...> { + return lift_impl (std::forward (f), t, std::index_sequence_for {}); } } diff --git a/src/data/CMakeLists.txt b/src/data/CMakeLists.txt index 23d4b3f8..1ff0934d 100644 --- a/src/data/CMakeLists.txt +++ b/src/data/CMakeLists.txt @@ -18,16 +18,9 @@ add_library (Data::hash ALIAS hash) add_library (numbers STATIC) add_library (Data::numbers ALIAS numbers) -# networking -add_library (net STATIC) -add_library (Data::net ALIAS net) - add_library (crypto STATIC) add_library (Data::crypto ALIAS crypto) -add_library (io STATIC) -add_library (Data::io ALIAS io) - set_target_properties (string PROPERTIES OUTPUT_NAME "data_string" ) @@ -40,18 +33,10 @@ set_target_properties (hash PROPERTIES OUTPUT_NAME "data_hash" ) -set_target_properties (net PROPERTIES - OUTPUT_NAME "data_net" -) - set_target_properties (crypto PROPERTIES OUTPUT_NAME "data_crypto" ) -set_target_properties (io PROPERTIES - OUTPUT_NAME "data_io" -) - target_include_directories ( core @@ -84,7 +69,6 @@ target_link_libraries ( Data::core Boost::boost - nlohmann_json::nlohmann_json ctre Boost::log_setup Boost::log @@ -160,45 +144,6 @@ target_link_libraries ( ) -target_sources ( - net - - PRIVATE - - # this is first because it takes a really long time. - net/email.cpp - - net/URL.cpp - - async.cpp - net/beast/http.cpp - net/HTTP.cpp - net/REST.cpp - net/JSON.cpp - net/TCP.cpp - #net/websocket.cpp - net/HTTP_client.cpp - net/HTTP_server.cpp - - tools/rate_limiter.cpp -) - -target_compile_features (net PUBLIC cxx_std_23) -set_target_properties (net PROPERTIES CXX_EXTENSIONS ON) - -target_link_libraries ( - net - - PUBLIC - - Data::string - Boost::chrono - Boost::thread - taocpp::pegtl - OpenSSL::SSL - OpenSSL::Crypto -) - target_sources ( crypto @@ -220,31 +165,6 @@ target_link_libraries ( Data::data ) -target_sources ( - io - - PRIVATE - - #io/run.cpp - io/wait_for_enter.cpp - io/log.cpp - io/arg_parser.cpp - io/random.cpp -) - -target_compile_features (io PUBLIC cxx_std_23) -set_target_properties (io PROPERTIES CXX_EXTENSIONS ON) - -target_link_libraries ( - io - - PUBLIC - - Data::crypto - Boost::log_setup - Boost::log -) - configure_file ( ${PROJECT_SOURCE_DIR}/include/data/version.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/generated/data/version.hpp @@ -261,7 +181,7 @@ install ( ) install ( - TARGETS data crypto net numbers hash io string core nlohmann_json rotella + TARGETS data crypto numbers hash string core rotella EXPORT DataTargets ARCHIVE ) diff --git a/src/data/Config.cmake.in b/src/data/Config.cmake.in index ecbb0937..9a3ec11a 100644 --- a/src/data/Config.cmake.in +++ b/src/data/Config.cmake.in @@ -1,9 +1,8 @@ @PACKAGE_INIT@ -include(CMakeFindDependencyMacro) +include (CMakeFindDependencyMacro) find_dependency (Boost COMPONENTS thread chrono log_setup log REQUIRED) find_dependency (OpenSSL REQUIRED Crypto SSL) find_dependency (GMP REQUIRED) find_dependency (SECP256K1 REQUIRED) -find_dependency (pegtl REQUIRED) -include ( "${CMAKE_CURRENT_LIST_DIR}/dataTargets.cmake" ) \ No newline at end of file +include ( "${CMAKE_CURRENT_LIST_DIR}/dataTargets.cmake" ) diff --git a/src/data/async.cpp b/src/data/async.cpp deleted file mode 100644 index 4fd33b45..00000000 --- a/src/data/async.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2025 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include - -namespace data { - - // wait for a given amount of time. - awaitable sleep (std::chrono::milliseconds duration) { - // Get the current executor associated with the coroutine - auto executor = co_await boost::asio::this_coro::executor; - - // Create a timer using the same executor - boost::asio::steady_timer timer (executor); - - // Set the timer to expire after the given duration - timer.expires_after (duration); - - // Suspend the coroutine until the timer expires - co_await timer.async_wait (boost::asio::use_awaitable); - } - -} diff --git a/src/data/io/arg_parser.cpp b/src/data/io/arg_parser.cpp deleted file mode 100644 index 8b76a957..00000000 --- a/src/data/io/arg_parser.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -namespace data::io::args { - inline const std::regex option_re { - R"(^--([A-Za-z][A-Za-z0-9_]*)=(.*)$)" - }; - - inline const std::regex flag_re { - R"(^--([A-Za-z][A-Za-z0-9_]*)$)" - }; - - inline const std::regex ab_flag_re { - R"(^-([A-Za-z]+)$)" - }; - - parsed::parsed (int arg_count, const char *const arg_value[]) { - list positionals; - for (int i = 0; i < arg_count; ++i) { - string_view arg {arg_value[i]}; - std::cmatch m; - - if (std::regex_match (arg_value[i], m, option_re)) { - std::string_view key {m[1].first, static_cast (m[1].length ())}; - // --name=value - Options <<= {key, std::string_view {m[2].first, static_cast (m[2].length ())}}; - } else if (std::regex_match (arg_value[i], m, flag_re)) { - // --name - std::string_view q {m[1].first, static_cast (m[1].length ())}; - Flags = Flags.insert (q); - } else if (std::regex_match (arg_value[i], m, ab_flag_re)) { - for (int i = 0; i < m[1].length (); i++) { - std::string_view q {m[1].first + i, 1}; - Flags = Flags.insert (q); - } - } else { - // positional - positionals <<= arg; - } - } - - Arguments = cross (positionals); - } -} diff --git a/src/data/io/log.cpp b/src/data/io/log.cpp deleted file mode 100644 index 7c9d5890..00000000 --- a/src/data/io/log.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2019 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace data::log { - - // Thread-local depth counter - thread_local unsigned IndentDepth = 0; - - void init (options o) { - - auto core = boost::log::core::get (); - core->set_filter (severity >= o.threshold); - - // Register a simple formatter for the severity_level enum - // This allows Boost.Log to convert severity_level values to strings automatically - register_simple_formatter_factory ("Severity"); - - if (o.filename.empty ()) { - // Use std::cout instead of a file - auto sink = boost::make_shared (); - sink->add_stream (boost::shared_ptr (&std::cout, boost::null_deleter ())); - auto sync_sink = boost::make_shared> (sink); - sync_sink->set_formatter( - [](boost::log::record_view const& rec, boost::log::formatting_ostream& strm) { - // Channel - if (auto attr = rec.attribute_values ()["Channel"].extract()) - strm << "[" << attr.get() << "] "; - else - strm << "[?] "; - - // Severity - if (auto sev = rec.attribute_values ()["Severity"].extract()) - strm << "[" << sev.get() << "]: "; - else - strm << "[?]: "; - - // Message - strm << rec[boost::log::expressions::smessage]; - } - ); - - core::get ()->add_sink (sync_sink); - } else { - - if (o.print_to_screen) - throw unimplemented {"printing logs to screen and writing to file"}; - - // Add a file sink for logging - add_file_log ( - keywords::file_name = o.filename, - keywords::rotation_size = 10 * 1024 * 1024, - keywords::time_based_rotation = sinks::file::rotation_at_time_point (0, 0, 0), - keywords::format = "[%Channel%] [%Severity%] %Message%"); - } - - add_common_attributes (); - } - - static const cross strings { - "debug", - "normal", - "note", - "warning", - "error", - "critical" - }; - - std::ostream &operator << (std::ostream &strm, severity_level level) { - - if (static_cast (level) < strings.size ()) - strm << strings[level]; - else - strm << static_cast (level); - - return strm; - } - - indent::indent () { - ++IndentDepth; // increment depth - } - - indent::~indent () { - if (IndentDepth > 0) --IndentDepth; // decrement depth - } - - unsigned indent::depth () { - return IndentDepth; - } - - // singleton to store default fill string - indent &indent::instance () { - static indent inst; // default fill string - return inst; - } - - -} diff --git a/src/data/io/random.cpp b/src/data/io/random.cpp deleted file mode 100644 index 91e427b4..00000000 --- a/src/data/io/random.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include -#include -#include - -namespace data::random { - - // statistical randomness, fast - ptr Casual {}; - - // cryptographic randomness, slow - ptr Secure {}; - -} - -namespace data::random { - - source &get () { - return *Casual; - } - -} - -namespace data::crypto::random { - - source &get () { - return *Secure; - } - -} - -namespace data::random { - - // our source of strong entropy - ptr StrongEntropy {}; - - ptr WeakEntropy {}; - - source *SecureEntropy {}; - source *CasualEntropy {}; - - void init (options o) { - - if (o.secure && o.strength != 112 && o.strength != 128 && o.strength != 192 && o.strength != 256) - throw exception {} << "invalid value for secure RNG strength (" << o.strength << "). Allowed values are 112, 128, 192, and 256"; - - if (bool (o.seed)) { - WeakEntropy = std::static_pointer_cast ( - std::make_shared ( - crypto::NIST::Hash_df (*o.seed))); - - CasualEntropy = WeakEntropy.get (); - - } else { - WeakEntropy = std::static_pointer_cast ( - std::make_shared ()); - - CasualEntropy = WeakEntropy.get (); - } - - Casual = ptr ( - new crypto::random::default_casual_random {*CasualEntropy, 1 << 30}); - - if (!o.secure) return; - - if (bool (o.seed)) { - SecureEntropy = WeakEntropy.get (); - - } else { - StrongEntropy = std::static_pointer_cast ( - std::make_shared ()); - - SecureEntropy = StrongEntropy.get (); - } - - bytes nonce; - - if (o.nonce == bytes {}) { - nonce.resize (o.strength / 2); - *WeakEntropy >> nonce; - } else nonce = o.nonce; - - if (o.strength == 112) { - using nist = crypto::NIST::CTR_DRBG<21, crypto::cipher::block::TDEA3>; - if (o.additional) { - using secure = automatic_reseed>; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, *o.additional, - byte_slice (nonce), byte_slice (Personalization)})); - } else { - using secure = automatic_reseed; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, - data::byte_slice (nonce), data::byte_slice (Personalization)})); - } - } else if (o.strength == 128) { - using nist = crypto::NIST::Hash_DRBG; - if (o.additional) { - using secure = automatic_reseed>; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, *o.additional, - byte_slice (nonce), byte_slice (Personalization)})); - } else { - using secure = automatic_reseed; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, - data::byte_slice (nonce), data::byte_slice (Personalization)})); - } - } else if (o.strength == 192) { - using nist = crypto::NIST::HMAC_DRBG; - if (o.additional) { - using secure = automatic_reseed>; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, *o.additional, - byte_slice (nonce), byte_slice (Personalization)})); - } else { - using secure = automatic_reseed; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, - data::byte_slice (nonce), data::byte_slice (Personalization)})); - } - } else if (o.strength == 256) { - using nist = crypto::NIST::Hash_DRBG; - if (o.additional) { - using secure = automatic_reseed>; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, *o.additional, - byte_slice (nonce), byte_slice (Personalization)})); - } else { - using secure = automatic_reseed; - Secure = std::static_pointer_cast ( - std::shared_ptr ( - new secure {*SecureEntropy, 1 << 30, - data::byte_slice (nonce), data::byte_slice (Personalization)})); - } - } - } -} diff --git a/src/data/io/run.cpp b/src/data/io/run.cpp deleted file mode 100644 index f1476289..00000000 --- a/src/data/io/run.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2022 Daniel Krawisz -// Distributed under the Open BSV software license, see the accompanying file LICENSE. - -#include -#include -#include - -namespace data::io { - - namespace bp = boost::process; - - using pipe = bp::async_pipe; - - using socket = net::asio::socket; - - // run an external command with standard in and standard out connected. - void run (boost::asio::io_context &io, string command, error_handler err_handler, interaction i, close_handler close) { - - ptr in {new pipe {io}}; - ptr out {new pipe {io}}; - ptr err {new pipe {io}}; - - ptr child { new bp::child (command, bp::std_out > *out, bp::std_err > *err, bp::std_in < *in) }; - - ptr In { new socket {in, close}}; - ptr Out { new socket {out, close}}; - ptr Err { new socket {err, close}}; - - ptr p {new process {std::move (child), In, err_handler}}; - - auto handle = i (p); - - net::async::wait_for_message (Out, [handle] (string_view x) -> void { - handle->read_out (x); - }, err_handler); - - net::async::wait_for_message (Err, [handle] (string_view x) -> void { - handle->read_err (x); - }, err_handler); - - } - -} - - diff --git a/src/data/io/wait_for_enter.cpp b/src/data/io/wait_for_enter.cpp deleted file mode 100644 index bcffc11a..00000000 --- a/src/data/io/wait_for_enter.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include - -#include -#include -#include - -namespace data { - - void wait_for_enter (std::string message) { - do { - if (message != "") std::cout << '\n' << message; - } while (std::cin.get () != '\n'); - } - - bool get_user_yes_or_no (std::string message, maybe default_value) { - std::string input; - while (true) { - std::cout << message << " ("; - std::cout << ((default_value.has_value () && default_value.value ()) ? "Y":"y"); - std::cout << "/"; - std::cout << ((default_value.has_value () && !default_value.value ()) ? "N":"n"); - std::cout << ")" << std::endl; - std::getline (std::cin,input); - - if (input.length () > 0) { - char answer = std::tolower (input[0]); - if (answer =='y' || answer =='n') return answer=='y'; - continue; - } - - if (default_value.has_value ()) return default_value.value (); - } - } - - std::string get_user_password (std::string message, char mask) { - struct termios term, original; - std::string password; - - // Get current terminal settings - if (tcgetattr (STDIN_FILENO, &term) != 0) return ""; - - original = term; - // Unset ECHO flag - term.c_lflag &= ~ECHO & ~ICANON; - if (tcsetattr (STDIN_FILENO, TCSANOW, &term) != 0) - return ""; - // Prompt for password - std::cout << message; - - char c; - - while (true) { - c = getchar (); - if (c!='\n') break; - std::cout << mask; - password.push_back (c); - } - - std::cout << std::endl; - // Set ECHO flag - tcsetattr (STDIN_FILENO, TCSANOW, &original); - return password; - } - - double read_decimal (const std::string &question) { - double value; - while (true) { - std::cout << question << " "; - std::cin >> value; - - // Check if input was valid - if (std::cin.fail ()) { - std::cin.clear (); // Clear error flag - std::cin.ignore (std::numeric_limits::max (), '\n'); // Discard invalid input - std::cout << "Invalid input. Please enter a decimal number.\n"; - } else { - std::cin.ignore (std::numeric_limits::max (), '\n'); // Clear buffer - return value; - } - } - } -} diff --git a/src/data/net/HTTP.cpp b/src/data/net/HTTP.cpp deleted file mode 100644 index 25ca8350..00000000 --- a/src/data/net/HTTP.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include - -namespace data::net::HTTP { - - std::ostream &operator << (std::ostream &o, const request &r) { - return o << beast::to (r); - } - - std::ostream &operator << (std::ostream &o, const response &r) { - return o << "HTTP response {status: " << r.Status << ", headers: " << r.Headers << ", body: " << r.Body << "}"; - } - - ASCII write_content_type (content::type x) { - switch (x) { - case content::text_plain: return "text/plain"; - case content::text_html: return "text/html"; - case content::application_json: return "application/json"; - case content::application_x_www_form_urlencoded: return "application/x-www-form-urlencoded"; - case content::multipart_form_data: return "multipart/form-data"; - case content::application_octet_stream: return "application/octet-stream"; - case content::application_xml: return "application/xml"; - case content::application_javascript: return "application/javascript"; - case content::image_png: return "image/png"; - case content::image_jpeg: return "image/jpeg"; - default: throw data::exception {} << "unknown content type"; - } - } - - content::content (content::type x): ASCII {write_content_type (x)} {} - - request::make::operator request () const { - if (Version != version_1_1) throw data::exception {} << "Only version 1.1 is supported."; - - if (!bool (Method)) throw data::exception {} << "Method is not set"; - - if (bool (Body)) { - for (auto &[h, setting] : Headers) if (h == header::content_type) goto content_type_set; - throw data::exception {} << "there is a body but content-type is not set"; - } - content_type_set: - - // check for host, required in version 1.1. - if (Version == version_1_1) { - for (auto &[h, setting] : Headers) if (h == header::host) goto host_set; - throw data::exception {} << "Host is not set, required in version 1.1"; - } - host_set: - - net::target targ = net::target (bool (Target.Path) ? Target : Target.path ("/")); - - if (!targ.valid ()) - throw data::exception {} << "invalid target " << targ; - - return request {*Method, targ, Headers, bool (Body) ? *Body : bytes {}}; - - } - - maybe message::content_type () const { - maybe ct = get_value (Headers, header {header::content_type}); - if (ct) return content {*ct}; - return {}; - } - - // host is required and the value must be an authority - bool request::valid () const { - auto h = host (); - return h.valid () && h != authority {}; - } - - authority request::host () const { - maybe auth = get_value (Headers, header {header::host}); - if (!auth) return {}; - return authority {*auth}; - } - - request::make request::make::method (const HTTP::method &m) const { - if (bool (Method)) throw data::exception {"method already set"}; - auto r = *this; - r.Method = m; - return r; - } - - request::make request::make::target (const net::target &p) const { - if (bool (Target.Path) || bool (Target.Query) || bool (Target.Fragment)) throw data::exception {"target already set"}; - auto r = *this; - r.Target = net::target::make (p); - return r; - } - - request::make request::make::path (const net::path &p) const { - if (bool (Target.Path)) throw data::exception {"path already set"}; - auto r = *this; - r.Target.Path = std::make_shared (p); - return r; - } - - request::make request::make::query (const ASCII &p) const { - if (bool (Target.Query)) throw data::exception {"query already set"}; - auto r = *this; - r.Target.Query = std::make_shared (encoding::percent::encode (p)); - return r; - } - - request::make request::make::query_map (dispatch q) const { - auto r = *this; - r.Target = r.Target.query_map (q); - return r; - } - - request::make request::make::fragment (const UTF8 &p) const { - if (bool (Target.Fragment)) - throw data::exception {"query already set"}; - - auto r = *this; - r.Target.Fragment = std::make_shared (encoding::percent::encode (p)); - return r; - } - - request::make request::make::add_headers (dispatch h) const { - auto r = *this; - r.Headers = r.Headers + h; - return r; - } - - request::make request::make::body (const bytes &b, const content &content_type) const { - if (bool (Body)) throw data::exception {"body is already set"}; - - auto r = add_headers ({entry {header::content_type, content_type}}); - - r.Body = b; - return r; - } - - request::make request::make::host (const UTF8 &u) const { - return add_headers ({entry {header::host, encoding::percent::encode (u)}}); - } - - request::make request::make::authorization (const ASCII &a) const { - return add_headers ({entry {header::authorization, a}}); - } - - -} - -namespace data::net::HTTP::beast { - template - awaitable connect (beast_stream &x, const authority &host_or_endpoint, bool use_ssl) { - - get_lowest_layer (x).expires_after (std::chrono::seconds (30)); - - maybe maybe_endpoint = host_or_endpoint.endpoint (); - - if (bool (maybe_endpoint)) { - co_await get_lowest_layer (x).async_connect (asio::ip::tcp::endpoint (*maybe_endpoint), asio::use_awaitable); - } else { - maybe maybe_host = host_or_endpoint.host (); - - if (!bool (maybe_host)) throw data::exception {} << "could not read endpoint or host from " << host_or_endpoint; - - maybe maybe_port = host_or_endpoint.port (); - ASCII port = bool (maybe_port) ? *maybe_port : bool (use_ssl) ? ASCII {"https"} : ASCII {"HTTP"}; - - asio::ip::tcp::resolver resolver {asio::make_strand (co_await asio::this_coro::executor)}; - auto results = co_await resolver.async_resolve (*maybe_host, port, asio::use_awaitable); - - co_await get_lowest_layer (x).async_connect (results, asio::use_awaitable); - } - } - - awaitable handshake (ssl_stream &x) { - co_await x.async_handshake (asio::ssl::stream_base::client, asio::use_awaitable); - } - - template - struct session final : HTTP::stream { - beast_stream Stream; - flat_buffer Buffer; // (Must persist between reads) - - session (beast_stream &&x): Stream {std::move (x)} {} - - awaitable start (const authority &host_or_endpoint); - - virtual bool closed () final override { - return !get_lowest_layer (Stream).socket ().is_open (); - } - - virtual void close () final override { - boost::system::error_code ec; - get_lowest_layer (Stream).socket ().shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); - get_lowest_layer (Stream).socket ().close (ec); - } - - virtual awaitable send (const HTTP::request &req) final override { - boost::system::error_code ec; - co_await http::async_write (Stream, to (req), asio::redirect_error (asio::use_awaitable, ec)); - if (ec) throw asio::exception {ec}; - } - - virtual awaitable receive () final override { - boost::system::error_code ec; - response res; - co_await http::async_read (Stream, Buffer, res, asio::redirect_error (asio::use_awaitable, ec)); - - if (ec) { - close (); - throw asio::exception {ec}; - } - - co_return HTTP::response {from (res)}; - } - }; - - template <> awaitable session::start (const authority &host_or_endpoint) { - co_await connect (Stream, host_or_endpoint, false); - } - - template <> awaitable session>::start (const authority &host_or_endpoint) { - co_await connect (Stream, host_or_endpoint, true); - co_await handshake (Stream); - } -} - -namespace data::net::HTTP { - - // async HTTP call - awaitable> connect (version v, const authority &host_or_endpoint, SSL *ssl) { - if (v != version_1_1) throw data::exception {} << "Only version 1.1 supported."; - - if (bool (ssl)) { - auto z = new beast::session> - {std::move (beast::ssl_stream {beast::net::make_strand (co_await asio::this_coro::executor), *ssl})}; - co_await z->start (host_or_endpoint); - co_return ptr {static_cast (z)}; - } else { - auto z = new beast::session - {std::move (boost::beast::tcp_stream {beast::net::make_strand (co_await asio::this_coro::executor)})}; - co_await z->start (host_or_endpoint); - co_return ptr {static_cast (z)}; - } - } - -} diff --git a/src/data/net/HTTP_client.cpp b/src/data/net/HTTP_client.cpp deleted file mode 100644 index b28ac637..00000000 --- a/src/data/net/HTTP_client.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include - -namespace data::net::HTTP { - - awaitable client::operator () (const request &r) { - if (Session.get () == nullptr || Session->closed ()) - Session = co_await connect (version_1_1, REST.Host, SSL.get ()); - - std::chrono::milliseconds wait = Rate.get_time (); - if (wait != std::chrono::milliseconds {0}) co_await sleep (wait); - co_return co_await Session->request (r); - } - -} - diff --git a/src/data/net/HTTP_server.cpp b/src/data/net/HTTP_server.cpp deleted file mode 100644 index 8e19aec7..00000000 --- a/src/data/net/HTTP_server.cpp +++ /dev/null @@ -1,118 +0,0 @@ - -#include -#include - -namespace data::net::HTTP::beast { - bool is_websocket_upgrade (const request &req) { - return req.method () == http::verb::get && - req[http::field::connection].find ("Upgrade") != boost::beast::string_view::npos && - req[http::field::upgrade] == "websocket"; - } - - awaitable write_websocket_decline (asio::ip::tcp::socket &socket) { - http::response res {http::status::upgrade_required, 11}; - res.set(http::field::content_type, "text/plain"); - res.body() = "WebSocket upgrade not supported."; - res.prepare_payload(); - - co_await beast::http::async_write (socket, res, asio::use_awaitable); - } - -} - -namespace data::net::HTTP { - server::server (exec ex, net::IP::TCP::endpoint ep, request_handler handler): - Acceptor {ex}, Handler {std::move (handler)} { - - asio::error ec; - asio::ip::tcp::endpoint endpoint (ep); - - // Open the acceptor - Acceptor.open (endpoint.protocol (), ec); - if (ec) throw data::exception {} << "Error opening acceptor: " << ec.message (); - - // Bind to the endpoint - Acceptor.bind (endpoint, ec); - if (ec) throw data::exception {} << "Error binding acceptor: " << ec.message (); - - // Start listening for connections - Acceptor.listen (asio::socket_base::max_listen_connections, ec); - if (ec) throw data::exception {} << "Error listening for connections: " << ec.message (); - } - - awaitable server::accept () { - if (!Acceptor.is_open ()) co_return false; - - try { - asio::ip::tcp::socket socket = co_await Acceptor.async_accept (asio::use_awaitable); - // Spawn a new session to handle the connection - ptr sess = std::make_shared (this->Sessions, std::move (socket), this->Handler); - this->Sessions.add (sess); - - sess->Self = sess; - - auto ex = co_await asio::this_coro::executor; - - spawn (ex, [sess] () -> awaitable { - co_await sess->respond (); - sess->Self = nullptr; - co_return; - }); - - // yield if single-threaded - co_await asio::post (ex, asio::use_awaitable); - } catch (const boost::system::system_error &e) { - if (e.code () == boost::asio::error::operation_aborted) { - co_return false; // The acceptor was closed—time to shut down - } else throw e; - } - - co_return true; - } - - awaitable server::session::respond () { - while (true) { - beast::flat_buffer buff; - beast::request req; - try { - co_await beast::http::async_read (Socket, buff, req, asio::use_awaitable); - } catch (const boost::system::system_error &e) { - // Normal: client closed connection after request - if (e.code () == beast::http::error::end_of_stream || - e.code () == boost::asio::error::eof) { - // Remote side closed connection - Sessions.remove (Self); - co_return; - } else if (e.code () == boost::asio::error::operation_aborted || - e.code () == boost::asio::error::bad_descriptor) { - // You cancelled this (e.g. during shutdown) - co_return; - } else throw e; - } - - co_await handle_request (req); - - if (!req.keep_alive ()) break; - } - } - - void server::sessions::remove_all () { - std::lock_guard lock (Mtx); - auto x = Sessions.begin (); - while (x != Sessions.end ()) { - (*x)->close (); - x = Sessions.erase (x); - } - } - - void server::sessions::add (ptr x) { - std::lock_guard lock (Mtx); - Sessions.insert (x); - } - - void server::sessions::remove (ptr x) { - std::lock_guard lock (Mtx); - x->close (); - Sessions.erase (x); - } -} diff --git a/src/data/net/JSON.cpp b/src/data/net/JSON.cpp deleted file mode 100644 index 909a92c9..00000000 --- a/src/data/net/JSON.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include - -#include - -namespace data { - - ptr> JSON_line_parser (handler errors, handler handle) { - struct parser : writer { - handler Handler; - handler ErrorHandler; - - std::stringstream Stream; - - parser (handler handle, handler errors): - Handler {handle}, ErrorHandler {errors}, Stream {} {} - - void write (const char *data, size_t size) { - int last_new_line = 0; - - for (int i = 0; i < size; i++) if (data[i] == '\n') { - Stream.write (data + last_new_line, i); - - try { - Handler (JSON::parse (Stream.str ())); - } catch (const JSON::exception &x) { - ErrorHandler (x); - } - - Stream = std::stringstream {}; - last_new_line = i + 1; - } - - Stream.write((const char*) (data + last_new_line), size - last_new_line); - } - }; - - return std::static_pointer_cast> (std::make_shared (handle, errors)); - } -} - diff --git a/src/data/net/REST.cpp b/src/data/net/REST.cpp deleted file mode 100644 index fc08f1ab..00000000 --- a/src/data/net/REST.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include - -namespace data::net::HTTP { - - encoding::percent::string REST::encode_form_data (dispatch params) { - std::stringstream q; - - auto i = params.begin (); - - if (i != params.end ()) { - - while (true) { - q << static_cast (encoding::percent::encode ((*i).Key, ":#[]@=&")) << "=" << - static_cast (encoding::percent::encode ((*i).Value, ":#[]@=&")); - i++; - if (i == params.end ()) break; - q << "&"; - } - } - - return q.str (); - } - - HTTP::request::make REST::operator () (HTTP::method meth, const path &path) const { - auto rr = HTTP::request::make {}.method (meth).path (Path + path).host (Host); - if (bool (Authorization)) return rr.authorization (*Authorization); - return rr; - } -} diff --git a/src/data/net/TCP.cpp b/src/data/net/TCP.cpp deleted file mode 100644 index 90fd1c17..00000000 --- a/src/data/net/TCP.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2021-2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include - -namespace data::net::IP::TCP { - - ptr connect (exec ec, const endpoint &p, close_handler on_close) { - - ptr x {new asio::ip::tcp::socket (ec)}; - asio::error error; - x->connect (asio::ip::tcp::endpoint (p), error); - - if (error) throw exception {error}; - return ptr {new stream {x, on_close}}; - } - - awaitable>> open::operator () ( - close_handler on_close, - interaction interact) { - - auto ec = co_await asio::this_coro::executor; - - auto tcp_stream = std::static_pointer_cast> ( - ThreadSafe ? connect (asio::make_strand (ec), Endpoint, on_close) : connect (ec, Endpoint, on_close)); - - auto out = std::static_pointer_cast> (tcp_stream); - - spawn (ec, [ - in = std::static_pointer_cast> (tcp_stream), - in_handler = interact (out) - ]() -> awaitable { - while (true) { - maybe msg = co_await in->receive (); - if (!bool (msg)) break; - in_handler (*msg); - } - }); - - co_return out; - - } - -} diff --git a/src/data/net/URL.cpp b/src/data/net/URL.cpp deleted file mode 100644 index 7b2f4b82..00000000 --- a/src/data/net/URL.cpp +++ /dev/null @@ -1,1353 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace data { - - bool case_insensitive_equal (const string &a, const string &b) { - - if (a.size () != b.size ()) return false; - - auto ait = a.begin (); - auto bit = b.begin (); - while (ait != a.end ()) { - if (std::tolower (*ait) != std::tolower (*bit)) return false; - ait++; - bit++; - } - - return true; - } -} - -namespace data::encoding::percent { - - bool valid (string_view input) { - if (!ASCII::valid (input)) return false; - - for (size_t i = 0; i < input.size (); ++i) { - - char c = input[i]; - - // must not be a control character. - if (c <= 32 || c == 127) return false; - - // Check if the character is a percent sign. - if (c == '%') { - // Make sure there are at least two more characters after the percent sign. - if (i + 2 >= input.size ()) - return false; - - // Check that the next two characters are valid hexadecimal digits. - if (!std::isxdigit (static_cast (input[i + 1])) || - !std::isxdigit (static_cast (input[i + 2]))) - return false; - - // Skip the next two characters (the hexadecimal digits). - i += 2; - } - } - - return true; - } - - bool equivalent (const std::string &a, const std::string &b) { - - if (!valid (a) || !valid (b)) return false; - - auto a_iter = a.begin (); - auto b_iter = b.begin (); - - while (a_iter != a.end () && b_iter != b.end ()) { - char a_char = *a_iter; - char b_char = *b_iter; - - int a_decoded; - int b_decoded; - - if (a_char == '%') { - a_decoded = (std::tolower (*(a_iter + 1)) - '0') * 16 + (std::tolower (*(a_iter + 2)) - '0'); - a_iter += 3; - } else { - a_decoded = a_char; - a_iter++; - } - - if (b_char == '%') { - b_decoded = (std::tolower (*(b_iter + 1)) - '0') * 16 + (std::tolower (*(b_iter + 2)) - '0'); - b_iter += 3; - } else { - b_decoded = b_char; - b_iter++; - } - - if (a_decoded != b_decoded) return false; - - if (is_reserved (a_char) && a_char != b_char) return false; - } - - // Both iterators should reach the end of the strings at the same time. - return a_iter == a.end () && b_iter == b.end (); - } - - constexpr const char *also_required = R"("%<>\^`{|})"; - - std::string encode (const data::UTF8 &input, const data::ASCII &additional_chars) { - std::ostringstream encoded; - encoded << std::hex << std::uppercase; - - for (const auto &ch : input) { - if (ch <= 0x20 || ch >= 0x7F || std::strchr (additional_chars.c_str (), ch) != nullptr || std::strchr (also_required, ch)) - encoded << '%' << std::setw (2) << static_cast (static_cast (ch)); - else encoded << ch; - } - - return encoded.str (); - } - - unsigned char from_hex (string_view str) { - int value; - - if (std::sscanf (str.data (), "%2x", &value) != 1) throw std::invalid_argument ("Invalid hex encoding"); - - return static_cast (value); - } - - maybe decode (string_view input) { - - if (!valid (input)) return {}; - data::UTF8 rt {}; - - std::ostringstream decoded; - - for (std::size_t i = 0; i < input.size (); ++i) - if (input[i] == '%') { - if (i + 2 >= input.size ()) return {}; - - if (!std::isxdigit (static_cast (input[i + 1])) || - !std::isxdigit (static_cast (input[i + 2]))) - return {}; - - decoded << from_hex (input.substr (i + 1, 2)); - i += 2; - } else decoded << input[i]; - - return {decoded.str ()}; - } - - maybe decode_not_reserved (string_view input) { - - if (!valid (input)) return {}; - data::UTF8 rt {}; - - std::ostringstream decoded; - - for (std::size_t i = 0; i < input.size (); ++i) - if (input[i] == '%') { - - if (i + 2 >= input.size ()) return {}; - - if (!std::isxdigit (static_cast (input[i + 1])) || - !std::isxdigit (static_cast (input[i + 2]))) - return {}; - - char ch = from_hex (input.substr (i + 1, 2)); - - if (ch <= 0x20 || ch >= 0x7F || std::strchr (Reserved, ch) != nullptr) decoded << input[i]; - else { - decoded << ch; - i += 2; - } - } else decoded << input[i]; - - return {decoded.str ()}; - } - -} - -namespace data::net { - - uint16 default_port (const protocol &p) { - switch (protocol::name (p)) { - default: return 0; - case (protocol::FTP) : return 20; - case (protocol::HTTP) : return 80; - case (protocol::HTTPS) : return 443; - } - } - - maybe URL::endpoint () const { - auto addr = this->address (); - if (!addr) return {}; - auto p = this->port_number (); - uint16 port = bool (p) ? *p : default_port (this->protocol ()); - if (port == 0) return {}; - return {IP::TCP::endpoint {*addr, port}}; - } - - protocol::operator name () const { - string self = *this; - - boost::to_lower (self); - - if (self == "ftp") return FTP; - if (self == "tcp") return TCP; - if (self == "http") return HTTP; - if (self == "https") return HTTPS; - if (self == "ws") return WS; - if (self == "wss") return WSS; - return unknown; - } - - protocol protocol::encode (name n) { - - if (n == FTP) return "ftp"; - if (n == TCP) return "tcp"; - if (n == HTTP) return "http"; - if (n == HTTPS) return "https"; - if (n == WS) return "ws"; - if (n == WSS) return "wss"; - return ""; - - } - - target::make::operator target () const { - - std::stringstream targ; - - if (bool (Path)) targ << static_cast (*Path); - if (bool (Query)) targ << "?" << static_cast (*Query); - if (bool (Fragment)) targ << "#" << static_cast (*Fragment); - - return targ.str (); - - } - - target::make target::make::query (const ASCII &q) const { - if (bool (Query)) - throw exception {"URL error: query already set."}; - - make m = *this; - m.Query = std::make_shared (q); - return m; - } - - target::make target::make::fragment (const UTF8 &u) const { - if (bool (Fragment)) - throw exception {"URL error: fragment already set."}; - - make m = *this; - m.Fragment = std::make_shared (encoding::percent::encode (u, ":#[]@=&")); - return m; - } - - target::make target::make::query_map (dispatch p) const { - if (bool (Query)) - throw exception {"URL error: query already set."}; - - make m = *this; - m.Query = std::make_shared (HTTP::REST::encode_form_data (p)); - return m; - } - - target::make target::make::path (const net::path &p) const { - if (Path != nullptr && *Path != pctstr {""}) - throw exception {"URL error: path already set."}; - - make m = *this; - m.Path = std::make_shared (p); - return m; - } - - URL::make::operator URL () const { - if (!Protocol) throw exception {"invalid URI; no protocol given"}; - - std::stringstream url; - url << static_cast (*Protocol) << ":"; - - if (UserInfo || Host || Port) { - url << "//"; - - if (UserInfo) url << static_cast (*UserInfo) << "@"; - if (Host) url << static_cast (*Host); - if (Port) url << ":" << static_cast (*Port); - } - - if (Target) url << static_cast (net::target (*Target)); - - URL u {url.str ()}; - - if (!u.valid ()) throw exception {} << "invalid URI: " << u; - - return u; - - } - - URL::make URL::make::path (const net::path &p) const { - make m = *this; - if (!bool (m.Target)) m.Target = std::make_shared (); - *m.Target = m.Target->path (p); - return m; - } - - URL::make URL::make::target (const net::target &t) const { - if (bool (Target)) - throw data::exception {} << "URL error: target already set"; - - make m = *this; - m.Target = std::make_shared (target::make {t}); - return m; - } - - URL::make URL::make::query_map (dispatch d) const { - make m = *this; - if (!bool (m.Target)) m.Target = std::make_shared (); - *m.Target = m.Target->query_map (d); - return m; - } - - URL::make URL::make::query (const ASCII &q) const { - make m = *this; - if (!bool (m.Target)) m.Target = std::make_shared (); - *m.Target = m.Target->query (q); - return m; - } - - URL::make URL::make::fragment (const UTF8 &f) const { - make m = *this; - if (!bool (m.Target)) m.Target = std::make_shared (); - *m.Target = m.Target->fragment (f); - return m; - } - - URL::make URL::make::protocol (const net::protocol &p) const { - if (bool (Protocol)) - throw exception {"URL error: protocol already set."}; - - if (!p.valid ()) - throw exception {"URL error: invalid protocol."}; - - make m = *this; - m.Protocol = std::make_shared (p); - return m; - } - - URL::make URL::make::port (const uint16 &u) const { - if (bool (Port)) - throw exception {"URL error: port already set."}; - - make m = *this; - m.Port = std::make_shared (net::port {u}); - return m; - } - - URL::make URL::make::address (const IP::address &ip) const { - if (Host != nullptr) throw exception {"URL error: host already set."}; - auto version = ip.version (); - if (version != 4 && version != 6) throw exception {} << "URL error: invalid IP address " << ip; - - make m = *this; - - if (version == 4) m.Host = std::make_shared (ip); - else { - std::stringstream literal; - literal << "[" << static_cast (ip) << "]"; - m.Host = std::make_shared (literal.str ()); - } - return m; - } - - URL::make URL::make::domain_name (const net::domain_name &name) const { - - if (Host != nullptr) throw exception {"URL error: host already set."}; - if (!name.valid ()) throw exception {} << "URL error: invalid domain name " << name; - - make m = *this; - m.Host = std::make_shared (name); - return m; - } - - URL::make URL::make::registered_name (const UTF8 &z) const { - if (Host != nullptr) throw exception {"URL error: host already set."}; - - make m = *this; - m.Host = std::make_shared (encoding::percent::encode (z, ":/?#")); - return m; - } - - URL::make URL::make::user_name_pass (const UTF8 &username, const UTF8 &pass) const { - - if (UserInfo != nullptr) throw exception {"URL error: user info already set."}; - - std::stringstream user_info; - user_info << encoding::percent::encode (username, ":/@?#") << ":" << encoding::percent::encode (pass, ":/@?#"); - - make m = *this; - m.UserInfo = std::make_shared (user_info.str ()); - return m; - } - - URL::make URL::make::user_info (const UTF8 &info) const { - - make m = *this; - if (m.UserInfo != nullptr) throw exception {"URL error: user info already set."}; - - m.UserInfo = std::make_shared (encoding::percent::encode (info, "/@?#")); - return m; - } - - path::path (list uf, char delim) { - if (delim < 32 || delim >= 127 || strchr ("/?#[]%", delim) != nullptr) - throw exception {} << "invalid path delimiter " << delim; - - std::string to_encode = std::string {"/?#[]%"} + delim; - - std::stringstream p; - - for (const UTF8 &u : uf) p << delim << encoding::percent::encode (u, ASCII {to_encode}); - - *this = encoding::percent::string {p.str ()}; - - } - - list path::read (char delim) const { - if (delim < 32 || delim >= 127 || strchr ("/?#[]", delim) == nullptr) - throw exception {} << "invalid path delimiter " << delim; - - list x; - - list zz = split (*this, string {} + delim); - - for (const string_view &z : zz) { - auto meep = encoding::percent::decode (z); - if (!meep) throw exception {} << "invalid percent-encoded string"; - x = x << *meep; - } - - return x; - } - - maybe URL::port_number () const { - auto zp = this->port (); - if (!zp) return {}; - maybe u {0}; - *u = std::stoi (*zp); - return u; - } - - maybe URL::address () const { - auto hh = URI::address (*this); - if (hh.data () == nullptr) return {}; - maybe ip {hh}; - if (!ip->valid ()) return {}; - return ip; - } - - maybe> URL::user_name_pass () const { - string_view hh = URI::user_info (*this); - if (hh.data () == nullptr) return {}; - list z = split (hh, ":"); - if (z.size () != 2) return {}; - return {entry {*encoding::percent::decode (z[0]), *encoding::percent::decode (z[1])}}; - } - - maybe> read_query_map (string_view query) { - list z = split (query, "&"); - - dispatch params; - - for (const string_view &e : z) { - list p = split (e, "="); - if (p.size () != 2) return {}; - params = params << entry {*encoding::percent::decode (p[0]), *encoding::percent::decode (p[1])}; - } - - return params; - } - - maybe> URL::query_map () const { - string_view q = URI::query (*this); - if (q.data () == nullptr) return {}; - - return read_query_map (q); - } - - maybe URL::domain_name () const { - if (this->address ()) return {}; - auto dn = this-> host (); - if (!dn) return {}; - if (net::domain_name::valid (*dn)) return {net::domain_name {*dn}}; - return {}; - } - -} - -namespace data::net::IP { - - address::operator asio::ip::address () const { - asio::error err {}; - auto addr = asio::ip::make_address (static_cast (*this), err); - if (err) throw exception {err}; - return addr; - } - -} - -#include - -namespace pegtl { - - using namespace tao::pegtl; - - // Rules for parsing domain names - struct domain_label : seq, star, plus>>> {}; - struct domain_name : seq, domain_label>>> {}; - struct domain_name_whole : seq {}; - - struct gen_delim : sor, one<'/'>, one<'?'>, one<'#'>, one<'['>, one<']'>, one<'@'>> {}; - - struct sub_delim : sor, one<'$'>, one<'&'>, one<'\''>, - one<'('>, one<')'>, one<'*'>, one<'+'>, one<','>, one<';'>, one<'='>> {}; - - struct reserved : sor {}; - - struct unreserved : sor, one<'.'>, one<'_'>, one<'~'>> {}; - - struct percent_encoded : seq, xdigit, xdigit> {}; - - struct pchar : sor, one<'@'>> {}; - - struct segment : star {}; - - struct segment_nz : plus {}; - - struct segment_nz_nc : plus>> {}; - - struct path_ab_empty : star, segment>> {}; - - struct path_rootless : seq {}; - - struct path_absolute : seq, segment_nz, path_ab_empty> {}; - - struct path_after_authority : path_ab_empty {}; - - struct reg_name : star> {}; - - struct scheme : seq, one<'-'>, one<'.'>>>> {}; - - struct whole_scheme : seq {}; - - struct user_info : plus>> {}; - - struct final_section : star, one<'?'>>> {}; - - struct query : final_section {}; - - struct fragment : final_section {}; - - struct ipv4_octet : sor< - seq, range<'0', '5'>>, - seq, range<'0', '4'>, digit>, - seq, digit, digit>, - seq, digit>, - digit> {}; - - struct ipv4 : seq, ipv4_octet, one<'.'>, ipv4_octet, one<'.'>, ipv4_octet> {}; - struct ipv4_whole : seq {}; - - struct h16 : seq, opt, opt> {}; - - struct ls32 : sor, h16>, ipv4> {}; - - struct ipv6 : sor>>, ls32>, - seq, rep<5, seq>>, ls32>, - seq, string<':', ':'>, rep<4, seq>>, ls32>, - seq, h16>>>>, string<':', ':'>, rep<3, seq>>, ls32>, - seq, h16>>>>>, string<':', ':'>, rep<2, seq>>, ls32>, - seq, h16>>>>>, string<':', ':'>, seq>, ls32>, - seq, h16>>>>>, string<':', ':'>, ls32>, - seq, h16>>>>>, string<':', ':'>, h16>, - seq, h16>>>>>, string<':', ':'>>> {}; - - struct ip_future : seq, plus, one<'.'>, plus>>> {}; - - struct ip_literal : seq, sor, one<']'>> {}; - - struct ipv6_whole : seq {}; - - struct ip_address : sor {}; - - struct ip_address_whole : seq, eof> {}; - - struct host : sor {}; - - struct user_info_at : seq> {}; - - struct port : star {}; - - struct authority : seq, host, opt, port>>> {}; - - struct endpoint : seq, port> {}; - - struct authority_whole : seq {}; - - struct path : sor {}; - - struct path_whole : seq {}; - - struct hierarchical : sor, authority, path_after_authority>, path_absolute, path_rootless> {}; - - struct target : seq, opt, query>>, opt, fragment>>> {}; - - struct target_whole : seq {}; - - struct uri : seq, hierarchical, opt, query>>, opt, fragment>>> {}; - - struct uri_whole : seq {}; - -} - -namespace data { - - bool net::domain_name::valid (string_view c) { - tao::pegtl::memory_input<> in (c, "domain_name"); - return tao::pegtl::parse (in); - } - - bool encoding::percent::URI::valid () const { - tao::pegtl::memory_input<> in (*this, "uri"); - return tao::pegtl::parse (in); - } - - bool net::IP::address::valid () const { - tao::pegtl::memory_input<> in (*this, "ip_address"); - return tao::pegtl::parse (in); - } - - bool net::authority::valid (string_view c) { - tao::pegtl::memory_input<> in (c, "authority"); - return tao::pegtl::parse (in); - } - - bool net::path::valid (string_view c) { - tao::pegtl::memory_input<> in (c, "path"); - return tao::pegtl::parse (in); - } - - bool net::target::valid (string_view c) { - tao::pegtl::memory_input<> in (c, "target"); - return tao::pegtl::parse (in); - } - - int32 net::IP::address::version () const { - tao::pegtl::memory_input<> in (*this, "ip_address"); - if (tao::pegtl::parse (in)) return 4; - if (tao::pegtl::parse (in)) return 6; - return -1; - } - - template struct domain_action : pegtl::nothing {}; - - template <> struct domain_action { - template - static void apply (const Input& in, data::list &labels) { - labels = labels << in.string (); - } - }; - - template struct make_uri_action : pegtl::nothing {}; - - template struct make_target_action : pegtl::nothing {}; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (m.Protocol == nullptr) m.Protocol = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (m.UserInfo == nullptr) - m.UserInfo = std::make_shared (in.begin (), in.begin () + in.size () - 1); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (m.Host == nullptr) m.Host = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (m.Port == nullptr) m.Port = std::make_shared (in.string ()); - } - }; - - template <> struct make_target_action { - template - static void apply (const Input& in, net::target::make &m) { - if (m.Path == nullptr) m.Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (!bool (m.Target)) m.Target = std::make_shared (); - if (m.Target->Path == nullptr) m.Target->Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_target_action { - template - static void apply (const Input& in, net::target::make &m) { - if (m.Path == nullptr) m.Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (!bool (m.Target)) m.Target = std::make_shared (); - if (m.Target->Path == nullptr) m.Target->Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_target_action { - template - static void apply (const Input& in, net::target::make &m) { - if (m.Path == nullptr) m.Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (!bool (m.Target)) m.Target = std::make_shared (); - if (m.Target->Path == nullptr) m.Target->Path = std::make_shared (in.string ()); - } - }; - - template <> struct make_target_action { - template - static void apply (const Input& in, net::target::make &m) { - if (m.Query == nullptr) m.Query = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (!bool (m.Target)) m.Target = std::make_shared (); - if (m.Target->Query == nullptr) m.Target->Query = std::make_shared (in.string ()); - } - }; - - template <> struct make_target_action { - template - static void apply (const Input& in, net::target::make &m) { - if (m.Fragment == nullptr) m.Fragment = std::make_shared (in.string ()); - } - }; - - template <> struct make_uri_action { - template - static void apply (const Input& in, net::URL::make &m) { - if (!bool (m.Target)) m.Target = std::make_shared ();; - if (m.Target->Fragment == nullptr) m.Target->Fragment = std::make_shared (in.string ()); - } - }; - - net::URL::make net::URL::read () const { - make m {}; - tao::pegtl::memory_input<> in (*this, "uri"); - return tao::pegtl::parse (in, m) ? m : make {}; - } - - net::target::make net::target::read () const { - make m {}; - tao::pegtl::memory_input<> in (*this, "uri"); - return tao::pegtl::parse (in, m) ? m : make {}; - } - - net::URL::make net::URL::make::authority (const UTF8 &x) const { - if (UserInfo || Host || Port) throw exception {"URL error: authority already set."}; - make m = *this; - - UTF8 encoded = encoding::percent::encode (x); - tao::pegtl::memory_input<> in (encoded, "authority"); - - return tao::pegtl::parse (in, m) ? m : make {}; - } - - template struct read_scheme_action : pegtl::nothing {}; - - template <> struct read_scheme_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - string_view encoding::percent::URI::scheme (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "scheme"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_authority_action : pegtl::nothing {}; - - template <> struct read_authority_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::authority &p) { - p = net::authority {std::string_view (in)}; - } - }; - - string_view encoding::percent::URI::authority (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "authority"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_user_info_action : pegtl::nothing {}; - - template <> struct read_user_info_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - string_view encoding::percent::URI::user_info (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "user_info"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub.substr (0, sub.size () - 1); - } - - string_view net::authority::user_info (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "user_info"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub.substr (0, sub.size () - 1); - } - - template struct read_host_action : pegtl::nothing {}; - - template <> struct read_host_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - string_view encoding::percent::URI::host (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "host"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_port_action : pegtl::nothing {}; - - template <> struct read_port_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::port &p) { - auto view = std::string_view (in); - p.resize (view.size ()); - std::copy (view.begin (), view.end (), p.begin ()); - } - }; - - string_view encoding::percent::URI::port (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "port"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_path_action : pegtl::nothing {}; - - template <> struct read_path_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {std::string_view (in)}; - } - template - static void apply (const Input& in, net::path &m) { - m = net::path {std::string_view (in)}; - } - }; - - template <> struct read_path_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::path &m) { - m = net::path {std::string_view (in)}; - } - }; - - template <> struct read_path_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::path &m) { - m = net::path {std::string_view (in)}; - } - }; - - string_view encoding::percent::URI::path (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "path"); - // in theory this will not happen. - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - string_view encoding::percent::URI::target (string_view x) { - string_view p = path (x); - // in theory this should not happen. - if (p.data () == nullptr) return p; - return string_view {p.data (), x.size () - (p.data () - x.data ())}; - } - - template struct read_query_action : pegtl::nothing {}; - - template <> struct read_query_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - string_view encoding::percent::URI::query (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "query"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_fragment_action : pegtl::nothing {}; - - template <> struct read_fragment_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - string_view encoding::percent::URI::fragment (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "fragment"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - template struct read_ip_address_action : pegtl::nothing {}; - - template <> struct read_ip_address_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::IP::address &x) { - x = net::IP::address {std::string_view (in)}; - } - }; - - template <> struct read_ip_address_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::IP::address &x) { - x = net::IP::address {std::string_view (in)}; - } - }; - - template <> struct read_ip_address_action { - template - static void apply (const Input& in, string_view &x) { - x = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - - template - static void apply (const Input& in, net::IP::address &x) { - x = net::IP::address {std::string_view (in)}; - } - }; - - string_view encoding::percent::URI::address (string_view x) { - string_view sub; - tao::pegtl::memory_input<> in (x, "ip_address"); - if (!tao::pegtl::parse (in, sub)) return {}; - return sub; - } - - encoding::percent::URI encoding::percent::URI::normalize () const { - if (!valid ()) throw exception {} << "invalid URI: " << *this; - - // we can do this because we know the URL is valid. - net::URL::make m {*decode_not_reserved (*this)}; - - // again, since we know the URL is valid, we can do this. - if (!m.Protocol) throw exception {} << "No protocol read in " << *this; - - for (char &x : *m.Protocol) x = std::tolower (x); - - if (m.Host) for (char &x : *m.Host) x = std::tolower (x); - return static_cast (net::URL (m)); - } - - net::path net::target::path () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "path"); - // in theory this will not happen. - if (!tao::pegtl::parse (in, sub)) - throw data::exception {"invalid target"}; - return net::path {sub}; - } - - maybe net::target::query () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "query"); - if (!tao::pegtl::parse (in, sub)) - throw data::exception {"invalid target"}; - if (sub.data () == nullptr) return {}; - return ASCII {sub}; - } - - maybe> net::target::query_map () const { - maybe q = query (); - - if (!bool (q)) return {}; - - return read_query_map (*q); - } - - maybe net::target::fragment () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "fragment"); - if (!tao::pegtl::parse (in, sub)) - throw data::exception {"invalid target"}; - if (sub.data () == nullptr) return {}; - return encoding::percent::decode (sub); - } - - maybe net::authority::port () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "port"); - if (!tao::pegtl::parse (in, sub)) - return {}; - if (sub.data () == nullptr) return {}; - return ASCII {sub}; - } - - maybe net::authority::host () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "host"); - if (!tao::pegtl::parse (in, sub)) - throw data::exception {"invalid authority"}; - if (sub.data () == nullptr) return {}; - return encoding::percent::decode (sub); - } - - maybe net::authority::address () const { - string_view sub; - tao::pegtl::memory_input<> in (*this, "ip_address"); - if (!tao::pegtl::parse (in, sub)) - throw data::exception {"invalid authority"}; - if (sub.data () == nullptr) return {}; - return encoding::percent::decode (sub); - } - - maybe net::authority::port_number () const { - auto zp = this->port (); - if (!zp) return {}; - maybe u {0}; - *u = std::stoi (*zp); - return u; - } - - maybe net::authority::endpoint () const { - maybe p = port_number (); - maybe a = address (); - if (!bool (p) || !bool (a)) return {}; - return net::IP::TCP::endpoint {*a, *p}; - } - - template struct read_uri_action : pegtl::nothing {}; - - template <> struct read_uri_action { - template - static void apply (const Input& in, net::URL &m) { - m = net::URL {std::string_view (in)}; - } - }; - - template struct read_protocol_action : pegtl::nothing {}; - - template <> struct read_protocol_action { - template - static void apply (const Input& in, net::protocol &m) { - m = net::protocol {std::string_view (in)}; - } - }; - - template struct read_domain_name_action : pegtl::nothing {}; - - template <> struct read_protocol_action { - template - static void apply (const Input& in, net::domain_name &m) { - m = net::domain_name {std::string_view (in)}; - } - }; - - template struct read_target_action : pegtl::nothing {}; - - template <> struct read_target_action { - template - static void apply (const Input& in, net::target &m) { - m = net::target {std::string_view (in)}; - } - }; - - template struct read_endpoint_action : pegtl::nothing {}; - - template <> struct read_target_action { - template - static void apply (const Input& in, net::IP::TCP::endpoint &m) { - m = net::IP::TCP::endpoint {std::string_view (in)}; - } - }; - - std::istream &net::operator >> (std::istream &i, net::domain_name &dom) { - parse::URL::domain_name m {}; - auto result = parse::read_token (i, m); - if (i) dom = net::domain_name {result}; - return i; - } - - std::istream &net::operator >> (std::istream &i, net::port &p) { - parse::IP::port m {}; - auto result = parse::read_token (i, m); - if (i) p = net::port {static_cast (m.value)}; - return i; - } - - std::istream &net::IP::operator >> (std::istream &i, net::IP::address &addr) { - parse::IP::address m {}; - auto result = parse::read_token (i, m); - if (i) addr = net::IP::address {result}; - return i; - } - - std::istream &net::operator >> (std::istream &i, net::URL &u) { - parse::URL::URI m {}; - auto result = parse::read_token (i, m); - if (i) u = net::URL {result}; - return i; - } - - std::istream &net::operator >> (std::istream &i, net::authority &a) { - parse::URL::authority m {}; - auto result = parse::read_token (i, m); - if (i) a = net::authority {result}; - return i; - } - - std::istream &net::operator >> (std::istream &i, net::target &targ) { - parse::URL::target m {}; - auto result = parse::read_token (i, m); - if (i) targ = net::target {result}; - return i; - } - - std::istream &net::IP::TCP::operator >> (std::istream &i, net::IP::TCP::endpoint &ep) { - parse::TCP::endpoint m {}; - auto result = parse::read_token (i, m); - if (i) ep = net::IP::TCP::endpoint {result}; - return i; - } - -} - -namespace data::net::IP { - - // write the ip address as a series of bites. - address::operator bytes () const { - auto v = version (); - if (v == -1) return {}; - - auto pi = this->begin (); - - // version 4 address - if (v == 4) { - bytes b (4); - auto bi = b.begin (); - byte octet = 0; - - while (true) { - if (pi == this->end () || *pi == '.') { - *bi = octet; - bi++; - - if (bi == b.end ()) return b; - - octet = 0; - } else { - octet *= 10; - octet += static_cast (*pi - '0'); - } - - // we know we are not at the end because we - // know that this is a vaild ipv4 address. - pi++; - } - } - - // otherwise, version 6 address. - - // an initial ':' must be an initial "::". We skip ahead. - if (*pi == ':') pi = pi += 1; - - // ports to the left of the "::". - list ports_left {}; - - // ports to the right of the "::" - list ports_right {}; - - list *ports = &ports_left; - - // list of octet bytes which are optionally at the end. - list octets; - - // whether we have encountered an IPV4 ending. - bool ipv4_ending = false; - while (true) { - - auto pb = pi; - - while (pi != this->end () && *pi != ':' && *pi != '.') pi++; - - auto end = pi == this->end (); - if (pi - pb == 0) { - - ports = &ports_right; - - // this means that the "::" we already found is at the end of the ip address. - if (end) break; - - // otherwise we have found a "::" - pi++; - continue; - } - - if (!end && *pi == '.') ipv4_ending = true; - - if (ipv4_ending) { - byte octet = 0; - - while (pb != pi) { - octet *= 10; - octet += static_cast (*pb - '0'); - pb++; - } - - octets <<= octet; - } else { - uint16_big port = 0; - - while (pb != pi) { - port <<= 4; - if (*pb <= 'F' && *pb >= 'A') port += static_cast (*pb - 'A' + 10); - else if (*pb <= 'f' && *pb >= 'a') port += static_cast (*pb - 'a' + 10); - else port += static_cast (*pb - '0'); - pb++; - } - - *ports <<= port; - } - - if (end) break; - pi++; - - } - - bytes b (16); - - // now we can figure out how many zeros we need to fill in. - int zeros = 8 - (data::size (ports_left) + data::size (ports_right) + data::size (octets) / 2); - - auto bi = b.begin (); - for (uint16_big upl : ports_left) { - *bi = upl[0]; - bi++; - *bi = upl[1]; - bi++; - } - - for (int i = 0; i < zeros; i++) { - *bi = 0; - bi++; - *bi = 0; - bi++; - } - - for (uint16_big upr : ports_right) { - *bi = upr[0]; - bi++; - *bi = upr[1]; - bi++; - } - - for (byte octet : octets) { - *bi = octet; - bi++; - } - - return b; - } - -} - diff --git a/src/data/net/beast/http.cpp b/src/data/net/beast/http.cpp deleted file mode 100644 index 1d8428a4..00000000 --- a/src/data/net/beast/http.cpp +++ /dev/null @@ -1,49 +0,0 @@ - -#include - -namespace data::net::HTTP::beast { - - template dispatch read_headers (const msg &m) { - dispatch x; - for (const auto &field : m) - x <<= entry {header {field.name_string ()}, ASCII {field.value ()}}; - return x; - } - - template void write_headers (msg &m, dispatch x) { - for (const auto &[h, v]: x) m.insert (h, v); - } - - // convert to beast format - request to (const HTTP::request &r) { - request req (r.Method, r.Target.c_str (), 11); - - write_headers (req, r.Headers); - - req.body () = string (r.Body); - req.prepare_payload (); - return req; - } - - // note: it is possible for a header to be unknown by boost::beast. In that case it gets deleted. Kind of dumb. - HTTP::response from (const response &res) { - return HTTP::response {res.base ().result (), read_headers (res), bytes (data::string (res.body ()))}; - } - - HTTP::request from (const request &req) { - return HTTP::request {req.method (), data::net::target {req.target ()}, read_headers (req), bytes (string (req.body ()))}; - } - - response to (const HTTP::response &r) { - response res {r.Status.Status, 11}; - - write_headers (res, r.Headers); - - res.body () = string (r.Body); - res.prepare_payload (); - return res; - } - -} - - diff --git a/src/data/net/email.cpp b/src/data/net/email.cpp deleted file mode 100644 index 8cfe0981..00000000 --- a/src/data/net/email.cpp +++ /dev/null @@ -1,1246 +0,0 @@ - -// Copyright (c) 2024 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - - -#include - -// https://www.rfc-editor.org/rfc/rfc5322 - -namespace data::net::email { - - char CRLF[] = {13, 10}; - - bool lines_lengths_exceed (string_view, size_t); - - // although not required, lines should be 78 chars or less. - bool inline lines_satisfy_78_char_limit (string_view x) { - return !lines_lengths_exceed (x, 78); - } - - // break lines up so that none is longer than the given limit (not including CRLFs) - // this may require inserting comments containing line breaks and whitespace. - // string is only returned if the limit could be satisfied. - maybe break_lines (const message &m, uint32 limit); - - ASCII remove_folded_whitespace (const ASCII &); - - maybe inline address::ip_address () const { - string_view x = ip_address (*this); - if (x.data () == nullptr) return {}; - return remove_folded_whitespace (data::ASCII {x}); - } - - ASCII field::to_string (name n) { - switch (n) { - case orig_date : return ASCII {"Date"}; - case from : return ASCII {"From"}; - case subject : return ASCII {"Subject"}; - case message_id : return ASCII {"Message-ID"}; - case sender : return ASCII {"Sender"}; - case reply_to : return ASCII {"Reply-To"}; - case to : return ASCII {"To"}; - case cc : return ASCII {"Cc"}; - case bcc : return ASCII {"Bcc"}; - case in_reply_to : return ASCII {"In-Reply-To"}; - case references : return ASCII {"References"}; - case comments : return ASCII {"Comments"}; - case keywords : return ASCII {"Keywords"}; - case return_path : return ASCII {"Return-Path"}; - case received : return ASCII {"Received"}; - case resent_date : return ASCII {"Resent-Date"}; - case resent_from : return ASCII {"Resent-From"}; - case resent_sender : return ASCII {"Resent-Sender"}; - case resent_to : return ASCII {"Resent-To"}; - case resent_cc : return ASCII {"Resent-Cc"}; - case resent_bcc : return ASCII {"Resent-Bcc"}; - case resent_msg_id : return ASCII {"Resent-Message-ID"}; - default: throw exception {} << "unknown field type."; - } - } - - list> message::headers (string_view x) { - char empty_line[] = {13, 10, 13, 10}; - list z = split (x, std::string {empty_line}); - if (z.size () != 2) throw exception {} << "invalid message"; - list hz = split (z[0], string_view {CRLF, 2}); - list> result; - for (string_view h : hz) result <<= std::pair {header::name (h), header::value (h)}; - return result; - } - - string_view message::body (string_view x) { - char empty_line[] = {13, 10, 13, 10}; - list z = split (x, string_view {empty_line, 4}); - if (z.size () != 2) throw exception {} << "invalid message"; - return z[1]; - } - - message::message (email::headers hz, const ASCII &body) { - std::stringstream ss; - for (const auto &h : hz) ss << h << CRLF; - ss << CRLF << body; - message m {ss.str ()}; - - // try to satisfy the recommended 78 character line limit. - if (auto mm = break_lines (m, 78); bool (mm)) { - *this = *mm; - return; - } - - // if not, try to satisfy the required 998 character line limit. - if (auto mm = break_lines (m, 998); bool (mm)) { - *this = *mm; - return; - } - - // if not, then this message is not valid and the user will have to - // fix it himself. - *this = m; - } - - list message::sent () const { - - string_view d = date (*this); - string_view f = from (*this); - string_view i = message_id (*this); - string_view s = sender (*this); - string_view t = to (*this); - string_view c = cc (*this); - string_view b = bcc (*this); - - list ddd = resent_date (*this); - list fff = resent_from (*this); - list iii = resent_message_id (*this); - list sss = resent_sender (*this); - list ttt = resent_to (*this); - list ccc = resent_cc (*this); - list bbb = resent_bcc (*this); - - if (d.data () == nullptr) throw exception {"invalid message"}; - if (f.data () == nullptr) throw exception {"invalid message"}; - - if (fff.size () != ddd.size ()) throw exception {"invalid message"}; - - if (i.data () == nullptr && iii.size () != 0 || i.data () != nullptr && iii.size () != fff.size ()) throw exception {"invalid message"}; - if (s.data () == nullptr && sss.size () != 0 || s.data () != nullptr && sss.size () != fff.size ()) throw exception {"invalid message"}; - if (t.data () == nullptr && ttt.size () != 0 || t.data () != nullptr && ttt.size () != fff.size ()) throw exception {"invalid message"}; - if (c.data () == nullptr && ccc.size () != 0 || c.data () != nullptr && ccc.size () != fff.size ()) throw exception {"invalid message"}; - if (b.data () == nullptr && bbb.size () != 0 || b.data () != nullptr && bbb.size () != fff.size ()) throw exception {"invalid message"}; - - list result; - while (fff.size () > 0) { - sent_info si {}; - si.Date = date_time {ddd.first ()}; - ddd = ddd.rest (); - si.From = address {fff.first ()}; - fff = fff.rest (); - - if (iii.size () > 0) { - si.MessageID = msg_id {iii.first ()}; - iii = iii.rest (); - } - - if (sss.size () > 0) { - si.Sender = address {sss.first ()}; - sss = sss.rest (); - } - - if (ttt.size () > 0) { - si.To = lift ([] (string_view x) -> address { - return address {x}; - }, split (ttt.first (), ",")); - ttt = ttt.rest (); - } - - if (ccc.size () > 0) { - si.Cc = lift ([] (string_view x) -> address { - return address {x}; - }, split (ccc.first (), ",")); - ccc = ccc.rest (); - } - - if (bbb.size () > 0) { - si.Bcc = lift ([] (string_view x) -> address { - return address {x}; - }, split (bbb.first (), ",")); - bbb = bbb.rest (); - } - - result <<= si; - } - - sent_info si {}; - si.Date = date_time {d}; - si.From = address {f}; - - if (i.data () != nullptr) si.MessageID = msg_id {i}; - if (s.data () != nullptr) si.Sender = address {s}; - if (t.data () != nullptr) si.To = lift ([] (string_view x) -> address { - return address {x}; - }, split (t, ",")); - if (c.data () != nullptr) si.Cc = lift ([] (string_view x) -> address { - return address {x}; - }, split (c, ",")); - if (b.data () != nullptr) si.Bcc = lift ([] (string_view x) -> address { - return address {x}; - }, split (b, ",")); - - result <<= si; - return result; - } - - maybe> message::reply_to () const { - string_view rt = reply_to (*this); - if (rt.data () == nullptr) return {}; - return lift ([] (string_view x) -> address { - return address {x}; - }, split (rt, ",")); - } - - maybe> message::in_reply_to () const { - string_view irt = in_reply_to (*this); - if (irt.data () == nullptr) return {}; - return lift ([] (string_view x) -> msg_id { - return msg_id {x}; - }, split (irt, ",")); - } - - maybe> message::references () const { - string_view r = references (*this); - if (r.data () == nullptr) return {}; - return lift ([] (string_view x) -> msg_id { - return msg_id {x}; - }, split (r, ",")); - } - - list> message::keywords () const { - list kkk = keywords (*this); - if (kkk.size () == 0) return {}; - return lift ([] (string_view k) -> list { - return lift ([] (string_view x) -> ASCII { - return ASCII {x}; - }, split (k, ",")); - }, kkk); - } - - message::message (const write &w) { - incomplete_message i {}; - i = i.attach (email::date {w.Sent.Date}); - i = i.attach (email::from {{w.Sent.From}}); - if (bool (w.Sent.MessageID)) i = i.attach (email::message_id (*w.Sent.MessageID)); - if (bool (w.Sent.Sender)) i = i.attach (email::sender (*w.Sent.Sender)); - if (bool (w.Sent.To)) i = i.attach (email::to (*w.Sent.To)); - if (bool (w.Sent.Cc)) i = i.attach (email::cc (*w.Sent.Cc)); - if (bool (w.Sent.Bcc)) i = i.attach (email::bcc (*w.Sent.Bcc)); - if (bool (w.Subject)) i = i.attach (email::subject (*w.Subject)); - for (auto &x : w.Comments) i = i.attach (email::comments {x}); - for (auto &x : w.Keywords) i = i.attach (email::keywords {x}); - if (bool (w.ReplyTo)) i = i.attach (email::reply_to (*w.ReplyTo)); - if (bool (w.InReplyTo)) i = i.attach (email::in_reply_to (*w.InReplyTo)); - if (bool (w.References)) i = i.attach (email::references (*w.References)); - for (auto &h : w.Optional) i = i.attach (h); - if (bool (w.Body)) i = i.attach (*w.Body); - *this = message (i); - } - -} - -#include - -namespace pegtl { - - using namespace tao::pegtl; - - struct US_ASCII : range<1, 127> {}; - - struct CLRF : seq, one<10>> {}; - - // printable ASCII characters - struct VCHAR : range<0x21, 0x7e> {}; - - // whitespace - struct WSP : sor, one<9>> {}; - - struct quoted_pair : seq, sor> {}; - - // folded whitespace - struct FWS : seq, CLRF>, plus> {}; - - // printable ascii characters other than '(', ')', and '\'. - struct ctext : sor, range<42, 91>, range<93, 126>> {}; - - struct ccontent; - - struct comment : seq, star, ccontent>>, opt, one<')'>> {}; - - struct ccontent : sor {}; - - // commented folded whitespace - struct CFWS : sor, comment>, opt>, FWS> {}; - - struct atext : sor, one<'#'>, one<'$'>, one<'%'>, one<'&'>, one<'\''>, one<'*'>, one<'+'>, - one<'-'>, one<'/'>, one<'='>, one<'?'>, one<'^'>, one<'_'>, one<'`'>, one<'{'>, one<'|'>, one<'}'>, one<'~'>> {}; - - struct atom : seq, plus, opt> {}; - - struct dot_atext : seq, star, plus>>> {}; - - struct dot_atom : seq, dot_atext, opt> {}; - - // printable ascii characters other than '"', and '\'. - struct qtext : sor, range<35, 91>, range<93, 126>> {}; - - struct qcontent : sor {}; - - struct quoted_string : seq, one<'"'>, star, qcontent>, opt, one<'"'>, opt> {}; - - struct word : sor {}; - - struct phrase : plus {}; - - struct unstructured : seq>, star> {}; - - struct local_part : sor {}; - - // printable characters other than '[', ']', or '\' - struct dtext : sor, range<94, 126>> {}; - - struct ip_address : seq, dtext>, opt> {}; - - struct domain_literal : seq, one<'['>, ip_address, one<']'>, opt> {}; - - struct domain_name : dot_atom {}; - - struct domain : sor {}; - - struct addr_spec : seq, domain> {}; - - struct addr_spec_whole : seq {}; - - struct angle_addr : seq, one<'<'>, addr_spec, one<'>'>, opt> {}; - - struct display_name : phrase {}; - - struct name_addr : seq, angle_addr> {}; - - struct mailbox : sor {}; - - struct mailbox_whole : seq {}; - - struct mailbox_list : seq, mailbox>> {}; - - struct group_list : sor {}; - - struct group : seq, opt, one<';'>, opt> {}; - - struct address : sor {}; - - struct address_whole : seq {}; - - struct address_list : seq, address>> {}; - - struct hour : seq {}; - - struct minute : seq {}; - - struct second : seq {}; - - struct time_of_day : seq, minute, opt, second>>> {}; - - struct zone : seq, one<'-'>>, seq> {}; - - struct time : seq {}; - - struct day : seq, plus>, FWS> {}; - - struct month : sor, string<'F', 'e', 'b'>, string<'M', 'a', 'r'>, string<'A', 'p', 'r'>, - string<'M', 'a', 'y'>, string<'J', 'u', 'n'>, string<'J', 'u', 'l'>, string<'A', 'u', 'g'>, - string<'S', 'e', 'p'>, string<'O', 'c', 't'>, string<'N', 'o', 'v'>, string<'D', 'e', 'c'>> {}; - - struct year : seq {}; - - struct date : seq {}; - - struct day_name : sor, string<'T', 'u', 'e'>, string<'W', 'e', 'd'>, - string<'T', 'h', 'u'>, string<'F', 'r', 'i'>, string<'S', 'a', 't'>, string<'S', 'u', 'n'>> {}; - - struct day_of_week : seq {}; - - struct date_time : seq>, date, time, CFWS> {}; - - struct date_time_whole : seq {}; - - struct header_orig_date : seq, date_time> {}; - - //updated in rfc6854 - // struct from : seq, mailbox_list> {}; - struct header_from : seq, sor> {}; - - // struct sender : seq, mailbox> {}; - struct header_sender : seq, address> {}; - - struct header_reply_to : seq, address_list> {}; - - struct header_to : seq, address_list> {}; - - struct header_cc : seq, address_list> {}; - - struct header_bcc : seq, sor> {}; - - struct no_fold_literal : seq, dtext, one<']'>> {}; - - struct msg_id : seq, dot_atext, one<'@'>, sor, one<'>'>, CFWS> {}; - - struct msg_id_whole : seq {}; - - struct header_message_id : seq, msg_id> {}; - - struct header_in_reply_to : seq, plus> {}; - - struct header_references : seq, plus> {}; - - struct header_subject : seq, unstructured> {}; - - struct header_comments : seq, unstructured> {}; - - struct header_keywords : seq, seq, phrase>>> {}; - - struct header_resent_date : seq, date_time> {}; - - struct header_resent_from : seq, address_list> {}; - - struct header_resent_sender : seq, address> {}; - - struct header_resent_to : seq, address_list> {}; - - struct header_resent_cc : seq, address_list> {}; - - struct header_resent_bcc : seq, sor> {}; - - struct header_resent_message_id : - seq, msg_id> {}; - - struct path : sor, CFWS, one<'>'>, CFWS>> {}; - - struct header_return_path : seq, path> {}; - - struct received_token : sor {}; - - struct header_received : seq, star, one<';'>, date_time> {}; - - struct trace : seq, plus> {}; - - // printable ascii characters other than ':'. - struct ftext : sor, range<5, 126>> {}; - - struct field_name : plus {}; - - struct field_name_whole : seq {}; - - struct field : seq, unstructured> {}; - - struct fields : plus> {}; - - struct optional_field : minus> {}; - - struct header : sor {}; - - struct header_whole : seq {}; - - struct resent_headers : sor {}; - - struct headers : seq, star>, - star>> {}; - - // an email message is divided into lines by CLRF - // in the body, CL and RF may not appear on their own in the body. - // this naturally also happens in header lines. - // These lines must not be more than 998 characters long. - struct line : star, one<10>>>> {}; - - struct body : star, line> {}; - - struct message : seq> {}; - - struct message_whole : seq {}; - - // TODO - struct message_obsolete : message {}; - - struct message_obsolete_whole : seq {}; - -} - -namespace data::net::email { - - bool address::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_address"); - return tao::pegtl::parse (in); - } - - namespace { - - template struct read_display_name : pegtl::nothing {}; - - template <> struct read_display_name { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - template struct read_local_part : pegtl::nothing {}; - - template <> struct read_local_part { - template - static void apply (const Input &in, string_view &lp) { - lp = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - template struct read_domain : pegtl::nothing {}; - - template <> struct read_domain { - template - static void apply (const Input &in, string_view &d) { - d = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - template struct read_domain_name : pegtl::nothing {}; - - template <> struct read_domain_name { - template - static void apply (const Input &in, string_view &d) { - d = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - template struct read_ip_address : pegtl::nothing {}; - - template <> struct read_ip_address { - template - static void apply (const Input &in, string_view &ip) { - ip = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - - template struct read_addr_spec : pegtl::nothing {}; - - template <> struct read_addr_spec { - template - static void apply (const Input &in, string_view &ad) { - ad = string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - } - - string_view address::display_name (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view name {}; - tao::pegtl::parse (in, name); - return name; - } - - string_view address::local_part (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view lp {}; - tao::pegtl::parse (in, lp); - return lp; - } - - string_view address::domain (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view d {}; - tao::pegtl::parse (in, d); - return d; - } - - string_view address::domain_name (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view d {}; - tao::pegtl::parse (in, d); - return d; - } - - string_view address::ip_address (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view ip {}; - tao::pegtl::parse (in, ip); - return ip; - } - - string_view address::addr_spec (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view ad {}; - tao::pegtl::parse (in, ad); - return ad; - } - - bool mailbox::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_address"); - return tao::pegtl::parse (in); - } - - bool field::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_field"); - return tao::pegtl::parse (in); - } - - bool header::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_header"); - return tao::pegtl::parse (in); - } - - bool date_time::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_date"); - return tao::pegtl::parse (in); - } - - bool msg_id::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_id"); - return tao::pegtl::parse (in); - } - - namespace { - - struct header_counter { - uint32 orig_date {0}; - uint32 message_id {0}; - uint32 from {0}; - uint32 sender {0}; - uint32 reply_to {0}; - uint32 to {0}; - uint32 cc {0}; - uint32 bcc {0}; - uint32 in_reply_to {0}; - uint32 references {0}; - uint32 subject {0}; - uint32 resent_date {0}; - uint32 resent_from {0}; - uint32 resent_sender {0}; - uint32 resent_to {0}; - uint32 resent_cc {0}; - uint32 resent_bcc {0}; - uint32 resent_message_id {0}; - uint32 trace {0}; - }; - - template struct count_headers : pegtl::nothing {}; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.from++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.orig_date++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.message_id++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.sender++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.reply_to++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.to++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.cc++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.bcc++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.in_reply_to++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.references++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.subject++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_date++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_from++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_sender++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_to++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_cc++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_bcc++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.resent_message_id++; - } - }; - - template <> struct count_headers { - template - static void apply (const Input &in, header_counter &h) { - h.trace++; - } - }; - - struct line_is_too_big {}; - - template struct check_line_limit : pegtl::nothing {}; - - template <> struct check_line_limit { - template - static void apply (const Input &in, size_t max_size) { - if (static_cast (in.end () - in.begin ()) > max_size) throw line_is_too_big {}; - } - }; - - template struct find_excessive_lines : pegtl::nothing {}; - - template <> struct find_excessive_lines { - template - static void apply (const Input &in, size_t max_size, list &x) { - if (static_cast (in.end () - in.begin ()) > max_size) - x <<= string_view {&*in.begin (), static_cast (in.end () - in.begin ())}; - } - }; - } - - bool lines_lengths_exceed (string_view x, size_t size) { - tao::pegtl::memory_input<> in (x, "email_message"); - try { - tao::pegtl::parse (in, size); - } catch (line_is_too_big) { - return true; - } - - return false; - } - - bool message::valid (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - header_counter c {}; - return tao::pegtl::parse (in, c) && - // check that every line is not greater than 998 characters. - !lines_lengths_exceed (x, 998) && - // there are also rules about how often certain headers may appear. - // there must be one orig_date, and one from field. - c.orig_date == 1 && c.from == 1 && c.message_id == 1 && - c.sender <= 1 && c.reply_to <= 1 && c.to <= 1 && c.cc <= 1 && c.bcc <= 1 && - c.in_reply_to <= 1 && c.references <= 1 && c.subject <= 1 && - // there must be one of to, cc, or bcc. - (c.to == 1 || c.cc == 1 || c.bcc == 1) && - // there must be a sender field with multi-addresses. - mailbox {from (x)}.valid () == (c.sender == 0) || - // resent fields must be present in a block preceeding the other fields. - c.resent_from == c.resent_date && - c.resent_sender == c.resent_from * c.sender && - c.resent_to == c.resent_to * c.to && - c.resent_cc == c.resent_cc * c.cc && - c.resent_bcc == c.resent_bcc * c.bcc && - c.resent_message_id == c.resent_message_id * c.message_id; - } - - bool message::standard (string_view x) { - return valid (x) && !lines_lengths_exceed (x, 78) && bool (message_id (x).data () != nullptr); - } - - struct insert { - string This; - size_t At; - - bool valid () const { - return This != ""; - } - - bool operator == (const insert &) const; - }; - - insert insert_CLRF_before_whitespace (string_view line, uint32 limit) { - for (int i = limit; i >= 0; i--) if (line[i] == 32 || line[i] == 9) return insert {string {CRLF}, static_cast (i)}; - return insert {"", 0}; - } - - // break lines up so that none is longer than the given limit (not including CRLFs) - // this may require inserting comments containing line breaks and whitespace. - // string is only returned if the limit could be satisfied. - maybe inline break_lines (const message &m, uint32 limit) { - tao::pegtl::memory_input<> in (m, "email_message"); - list big_lines; - tao::pegtl::parse (in, limit, big_lines); - if (data::empty (big_lines)) return {m}; - - // let's look at the lines. - auto lines = message::lines (m); - - list insertions; - - size_t begin_pos = 0; - for (string_view line : lines) { - if (line.size () > limit) { - if (insert i = insert_CLRF_before_whitespace (line, limit); i.valid ()) { - i.At += begin_pos; - insertions <<= i; - // TODO it is possible that we could try other things to insert a line break. - // for example, there may be places where we could insert a comment that - // contains whitespace, which means it can have a line break. - } else return {}; - } - begin_pos += line.size () + 2; - } - - begin_pos = 0; - std::stringstream ss; - for (const insert &i : insertions) { - ss << string_view {m.data () + begin_pos, i.At}; - ss << CRLF; - begin_pos += i.At; - } - - ss << string_view {m.data () + begin_pos, m.size () - begin_pos}; - return {message {ss.str ()}}; - } - - namespace { - template struct read_header_from : pegtl::nothing {}; - - template <> struct read_header_from { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 5, static_cast (in.end () - in.begin () - 5)}; - } - }; - - template struct read_header_date : pegtl::nothing {}; - - template <> struct read_header_date { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 5, static_cast (in.end () - in.begin () - 5)}; - } - }; - - template struct read_header_message_id : pegtl::nothing {}; - - template <> struct read_header_message_id { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 11, static_cast (in.end () - in.begin () - 11)}; - } - }; - - template struct read_header_subject : pegtl::nothing {}; - - template <> struct read_header_subject { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 8, static_cast (in.end () - in.begin () - 8)}; - } - }; - - template struct read_header_sender : pegtl::nothing {}; - - template <> struct read_header_sender { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 7, static_cast (in.end () - in.begin () - 7)}; - } - }; - - template struct read_header_in_reply_to : pegtl::nothing {}; - - template <> struct read_header_in_reply_to { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 12, static_cast (in.end () - in.begin () - 12)}; - } - }; - - template struct read_header_reply_to : pegtl::nothing {}; - - template <> struct read_header_reply_to { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_to : pegtl::nothing {}; - - template <> struct read_header_to { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 3, static_cast (in.end () - in.begin () - 3)}; - } - }; - - template struct read_header_cc : pegtl::nothing {}; - - template <> struct read_header_cc { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 3, static_cast (in.end () - in.begin () - 3)}; - } - }; - - template struct read_header_bcc : pegtl::nothing {}; - - template <> struct read_header_bcc { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 4, static_cast (in.end () - in.begin () - 4)}; - } - }; - - template struct read_header_references : pegtl::nothing {}; - - template <> struct read_header_references { - template - static void apply (const Input &in, string_view &name) { - name = string_view {&*in.begin () + 11, static_cast (in.end () - in.begin () - 11)}; - } - }; - - template struct read_header_comments : pegtl::nothing {}; - - template <> struct read_header_comments { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_keywords : pegtl::nothing {}; - - template <> struct read_header_keywords { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_return_path : pegtl::nothing {}; - - template <> struct read_header_return_path { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 12, static_cast (in.end () - in.begin () - 12)}; - } - }; - - template struct read_header_received : pegtl::nothing {}; - - template <> struct read_header_received { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - template struct read_header_resent_from : pegtl::nothing {}; - - template <> struct read_header_resent_from { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_date : pegtl::nothing {}; - - template <> struct read_header_resent_date { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_message_id : pegtl::nothing {}; - - template <> struct read_header_resent_message_id { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_sender : pegtl::nothing {}; - - template <> struct read_header_resent_sender { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_to : pegtl::nothing {}; - - template <> struct read_header_resent_to { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_cc : pegtl::nothing {}; - - template <> struct read_header_resent_cc { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - template struct read_header_resent_bcc : pegtl::nothing {}; - - template <> struct read_header_resent_bcc { - template - static void apply (const Input &in, list &xx) { - xx <<= string_view {&*in.begin () + 9, static_cast (in.end () - in.begin () - 9)}; - } - }; - - } - - string_view message::from (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::date (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::message_id (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::subject (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::sender (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::reply_to (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::to (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::cc (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::bcc (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::in_reply_to (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - string_view message::references (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - string_view value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::comments (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::keywords (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::return_path (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::received (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_date (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_from (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_message_id (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_sender (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_to (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_cc (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - - list message::resent_bcc (string_view x) { - tao::pegtl::memory_input<> in (x, "email_message"); - list value {}; - tao::pegtl::parse (in, value); - return value; - } - -} diff --git a/src/data/net/websocket.cpp b/src/data/net/websocket.cpp deleted file mode 100644 index db21e538..00000000 --- a/src/data/net/websocket.cpp +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include -#include - -namespace data::net::websocket { - - namespace beast = boost::beast; // from - namespace http = beast::http; // from - namespace io = boost::asio; // from - using tcp = boost::asio::ip::tcp; // from - using insecure_stream = beast::websocket::stream; - using secure_stream = beast::websocket::stream>; - - template - struct socket final : - data::net::socket, - std::enable_shared_from_this> { - - ptr Socket; - - handler OnError; - close_handler OnClose; - - beast::flat_buffer Buffer; - - bool Closed; - - socket (ptr socket, handler errors, close_handler on_close) : - Socket {socket}, OnError {errors}, OnClose {on_close}, Buffer {}, Closed {false} {} - - void write (const bytes &x, function on_complete) final override { - Socket->async_write (asio::buffer (x.data (), x.size ()), - [self = this->shared_from_this (), on_complete] (const asio::error& error, size_t bytes_transferred) -> void { - if (error == beast::websocket::error::closed) self->close (); - else if (error) self->OnError (error); - else on_complete (); - }); - } - - void close () final override { - if (Closed) return; - Closed = true; - Socket->async_close ( - beast::websocket::close_code::normal, - [on_close = OnClose, on_error = OnError] (beast::error_code e) -> void { - if (e) on_error (e); - on_close (); - }); - } - - bool closed () final override { - bool closed = !Socket->is_open (); - if (closed && !Closed) close (); - return closed; - } - - void read_result (function handle, beast::error_code error, std::size_t bytes_transfered) { - if (error == beast::websocket::error::closed) close (); - else if (error) OnError (error); - - handle (byte_slice {(const byte *) Buffer.data (), Buffer.size ()}); - Buffer.consume (bytes_transfered); - } - - void read (function handle) final override { - Buffer.clear (); - Socket->async_read (Buffer, boost::beast::bind_front_handler (&socket::read_result, this->shared_from_this (), handle)); - } - }; - - void insecure_open ( - asio::io_context &io, - const URL &url, - asio::error_handler error_handler, - interaction interact, - close_handler closed) { - // These objects perform our I/O - ptr ws = std::make_shared (io); - - asio::ip::tcp::resolver resolver (io); - auto hostname = url.domain_name (); - auto const results = resolver.resolve (hostname->c_str (), url.port_DNS ().c_str ()); - - // Make the connection on the IP address we get from a lookup - auto ep = io::connect (ws->next_layer (), results); - - // Update the host string. This will provide the value of the - // Host HTTP header during the WebSocket handshake. - // See https://tools.ietf.org/html/rfc7230#section-5.4 - string host = *hostname + ':' + std::to_string (ep.port ()); - - // Set a decorator to change the User-Agent of the handshake - ws->set_option (beast::websocket::stream_base::decorator ( - [] (beast::websocket::request_type &req) { - req.set (http::field::user_agent, - std::string (BOOST_BEAST_VERSION_STRING) + - " websocket-client-coro"); - })); - - // Perform the websocket handshake - ws->handshake (host, "/"); - - ptr> ss {new socket {ws, error_handler, closed}}; - - ptr> zz { - static_cast *> - (new async::message_queue { - std::static_pointer_cast> (ss)})}; - - async::wait_for_message (std::static_pointer_cast> (ss), - [xx = interact (zz)] (byte_slice z) -> void { - return xx (z); - }); - } - - void secure_open ( - asio::io_context &io, - const URL &url, - HTTP::SSL &ssl, - asio::error_handler error_handler, - interaction interact, - close_handler closed) { - // These objects perform our I/O - ptr ws = std::make_shared (io, ssl); - - asio::ip::tcp::resolver resolver (io); - // Look up the domain name - auto hostname = url.domain_name (); - auto const results = resolver.resolve (hostname->c_str (), url.port_DNS ().c_str ()); - - // Make the connection on the IP address we get from a lookup - auto ep = io::connect (get_lowest_layer (*ws), results); - - if (!SSL_set_tlsext_host_name (ws->next_layer ().native_handle (), hostname->c_str ())) - throw beast::system_error ( - beast::error_code ( - static_cast(::ERR_get_error ()), - io::error::get_ssl_category ()), - "Failed to set SNI Hostname"); - - // Update the host_ string. This will provide the value of the - // Host HTTP header during the WebSocket handshake. - // See https://tools.ietf.org/html/rfc7230#section-5.4 - string host = *hostname + ':' + std::to_string (ep.port ()); - - // Preform the SSL Handshke - ws->next_layer ().handshake (io::ssl::stream_base::client); - - // Set a decorator to change the User-Agent of the handshake - ws->set_option (beast::websocket::stream_base::decorator ( - [] (beast::websocket::request_type &req) { - req.set (http::field::user_agent, - std::string (BOOST_BEAST_VERSION_STRING) + - " websocket-client-coro"); - })); - - // Perform the websocket handshake - ws->handshake (host, "/"); - - ptr> ss {new socket {ws, error_handler, closed}}; - - ptr> zz {static_cast *> - (new async::message_queue { - std::static_pointer_cast> (ss)})}; - - async::wait_for_message (std::static_pointer_cast> (ss), - [xx = interact (zz)] (byte_slice z) -> void { - return xx (z); - }); - } - - void open ( - asio::io_context &io, - const URL &url, - HTTP::SSL *ssl, - asio::error_handler error_handler, - close_handler closed, - interaction interact) { - - if (url.protocol () != protocol::WS && url.protocol () != protocol::WSS) - throw exception {} << "protocol " << url.protocol () << " is not websockets"; - if (!ssl && url.protocol () == protocol::WSS) - throw exception {} << "Secure websocket requested when SSL Context not supplied"; - - if (!url.domain_name ()) throw exception {"Invalid URL: no host provided."}; - - try { - if (url.protocol () == protocol::WS) insecure_open (io, url, error_handler, interact, closed); - else secure_open (io, url, *ssl, error_handler, interact, closed); - } catch (boost::system::system_error err) { - error_handler (err.code ()); - } catch (const std::exception &err) { - closed (); - } - } - - void open_secure ( - asio::io_context &io, - const URL &url, - HTTP::SSL &ssl, - asio::error_handler error_handler, - close_handler closed, - interaction interact) { - - if (url.protocol () != protocol::WSS) throw exception {} << "expected protocol WSS, but got " << url.protocol (); - - if (!url.domain_name ()) throw exception {"Invalid URL: no host provided."}; - - try { - secure_open (io, url, ssl, error_handler, interact, closed); - } catch (boost::system::system_error err) { - error_handler (err.code ()); - } catch (const std::exception &err) { - closed (); - } - } - - void open_insecure ( - asio::io_context &io, - const URL &url, - asio::error_handler error_handler, - close_handler closed, - interaction interact) { - - if (url.protocol () != protocol::WS) throw exception {} << "expected protocol WS, but got " << url.protocol (); - - if (!url.domain_name ()) throw exception {"Invalid URL: no host provided."}; - - try { - insecure_open (io, url, error_handler, interact, closed); - } catch (boost::system::system_error err) { - error_handler (err.code ()); - } catch (const std::exception &err) { - closed (); - } - } - -} - diff --git a/src/data/tools/rate_limiter.cpp b/src/data/tools/rate_limiter.cpp deleted file mode 100644 index 376b6876..00000000 --- a/src/data/tools/rate_limiter.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/tools/rate_limiter.hpp" - -namespace data { - - millisecond rate_limiter::get_time () { - if (m_queue.size == 0) return millisecond {0}; - - std::scoped_lock lock (*mutex); - - using namespace std::chrono; - if (m_duration == millisecond {0}) return millisecond {0}; - - millisecond now = duration_cast (system_clock::now ().time_since_epoch ()); - - millisecond lastSent = m_queue.get (); - - if (now - lastSent < m_duration) { - millisecond wait_name = m_duration - (now - lastSent); - m_queue.set (now + wait_name); - return wait_name; - } - - m_queue.set (now); - - return millisecond {0}; - } -} - diff --git a/test/ASCII.cpp b/test/ASCII.cpp index a2303aac..a3253046 100644 --- a/test/ASCII.cpp +++ b/test/ASCII.cpp @@ -2,11 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/encoding/ascii.hpp" -#include "data/encoding/unicode.hpp" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" +#include +#include + +#include +#include +#include + namespace data { TEST (ASCII, Construct) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e655e98c..f881d105 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,7 +5,6 @@ if (${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy (VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) endif () -option (OPTIONAL_TESTS "Build optional Tests" OFF) include (GoogleTest) add_executable ( @@ -38,7 +37,7 @@ add_executable ( sort.cpp get.cpp for_each.cpp # TODO: could be more thorough - replace.cpp + replace.cpp # TODO: need more tests select.cpp lift.cpp # TODO: could be more thorough transpose.cpp @@ -46,13 +45,11 @@ add_executable ( permutation.cpp # TODO: Need to test each function more precisely, like in list transpose.cpp combinatorics.cpp - schema.cpp # string - parse.cpp integer_format.cpp bytes.cpp - write.cpp + write.cpp # TODO: there is a lot commented out. hex.cpp base64.cpp ASCII.cpp @@ -104,22 +101,10 @@ add_executable ( symmetric_crypto.cpp NIST_DRBG.cpp secret_share.cpp - - #async - async.cpp - - # net - IP.cpp - circular_queue.cpp - email.cpp - - # io - log.cpp - args_parser.cpp ) -target_link_libraries (unit_tests - PRIVATE net io crypto numbers gtest_main gmock) +target_link_libraries (unit_tests PRIVATE crypto numbers gtest_main gmock) + gtest_discover_tests (unit_tests) if (OPTIONAL_TESTS) diff --git a/test/IP.cpp b/test/IP.cpp deleted file mode 100644 index 3dd60417..00000000 --- a/test/IP.cpp +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright (c) 2019-2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/net/URL.hpp" -#include "data/encoding/hex.hpp" -#include "gtest/gtest.h" -#include - -namespace data::encoding::percent { - - TEST (IP, PercentEncoding) { - - struct positive_test_case { - data::UTF8 Valid; - string Additional; - data::encoding::percent::string Expected; - }; - - // for invalid percent encoded strings - struct negative_test_case { - string Invalid; - }; - - for (const positive_test_case &tt : list {{ - "Hello, World! Привет, мир!", "!,.", - R"(Hello%2C%20World%21%20%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C%20%D0%BC%D0%B8%D1%80%21)" - }, {"example.com/path?query=value#fragment", "/?=#", - "example.com%2Fpath%3Fquery%3Dvalue%23fragment" - - }}) { - auto encoded = encode (tt.Valid, tt.Additional); - EXPECT_EQ (encoded, tt.Expected) << "expected " << encoded << " == " << tt.Expected; - EXPECT_EQ (*percent::decode (encoded), tt.Valid); - } - - for (const negative_test_case &tt : list { - {R"(Hello%2GWorld)"}, - {R"(Hello%)"}, - {R"(Hello )"} - }) { - EXPECT_FALSE (bool (percent::decode (tt.Invalid))); - } - - struct test_equal { - string Left; - string Right; - bool Expected; - }; - - for (const test_equal &tt : list { - {"abc", "abc", true}, // No percent encoding - // the next several tests have to do with reserved characters. - // reserved characters that are percent encoded are not equal - // to their decoded form. - {"abc+", "abc%2B", false}, // '+' is a reserved character - {"abc/", "abc%2F", false}, // '/' is a reserved character - {"abc?", "abc%3F", false}, // '?' is a reserved character - {"abc#", "abc%23", false}, // '#' is a reserved character - {"abc[", "abc%5B", false}, // '[' is a reserved character - {"abc]", "abc%5D", false}, // ']' is a reserved character - {"abc@", "abc%40", false}, // '@' is a reserved character - {"abc!", "abc%21", false}, // '!' is a reserved character - {"abc$", "abc%24", false}, // '$' is a reserved character - {"abc&", "abc%26", false}, // '&' is a reserved character - {"%C3%A9", "%c3%a9", true}, - {"%20", " ", false}, // ' ' is a control character. - {"%41", "A", true} - }) { - EXPECT_EQ (tt.Left == tt.Right, tt.Expected) - << "expected " << tt.Left << " " << (tt.Expected ? "==" : "!=") << " " << tt.Right; - } - } - - // this test has to do with finding the right == operator in gcc vs clang. - TEST (IP, PercentEncodedStringEqual) { - // when we have an std::string and a percent encoded string, we should choose regular string == - // when we have a percent encoded string and a path, we should choose percent encoding equal. - - std::string encoded_string {"%41"}; - std::string decoded_string {"A"}; - string encoded_percent {"%41"}; - string decoded_percent {"A"}; - net::path encoded_path {"%41"}; - net::path decoded_path {"A"}; - - EXPECT_NE (encoded_string, decoded_string); - EXPECT_EQ (encoded_percent, decoded_percent); - EXPECT_EQ (encoded_path, decoded_path); - - EXPECT_NE (encoded_percent, decoded_string); - EXPECT_NE (decoded_percent, encoded_string); - - EXPECT_EQ (encoded_percent, encoded_string); - EXPECT_EQ (encoded_string, encoded_percent); - - EXPECT_EQ (encoded_percent, decoded_path); - EXPECT_EQ (decoded_percent, encoded_path); - - EXPECT_EQ (encoded_percent, encoded_path); - EXPECT_EQ (encoded_path, encoded_percent); - - } - -} - -namespace data::net::IP { - - TEST (IP, IPFormats) { - - EXPECT_EQ (protocol {"FTP"}, protocol {"ftp"}); - EXPECT_EQ (domain_name {"news.cat.web"}, domain_name {"NEWS.cAt.WEB"}); - - EXPECT_TRUE (protocol {"lgbtq++"}.valid ()); - EXPECT_FALSE (protocol {"122"}.valid ()); - EXPECT_FALSE (protocol {"abc&@"}.valid ()); - - } - - // TODO maximum size of the label is 63 characters. - // TODO maximum total length is 253 characters - // RFC 1035 - TEST (IP, DomainNameFormat) { - - struct test_case { - domain_name DomainName; - bool ExpectValid; - }; - - for (const test_case &t : list { - {"example.com", true}, // simple domain - {"www.example.com", true}, // with subdomain - {"subdomain.example.co.uk", true}, // multiple subdomains - {"example123.com", true}, // numeric characters in domain - {"example-name.com", true}, // hyphen in domain - {"example_name.org", false}, // underscore in domain - {"a.b.c.example.net", true}, // multiple subdomains - - {"example..com", false}, // consecutive periods - {"-example.com", false}, // starts with a hyphen - {"example-.com", false}, // ends with a hyphen - {"example..co.uk", false}, // consecutive periods - {"example._com", false}, // starts with an underscore - {"example_.com", false}, // ends with an underscore - {".example.com", false}, // starts with a period - {"example.com.", false}, // ends with a period - }) { - std::stringstream ss {t.DomainName}; - domain_name parsed {}; - EXPECT_NO_THROW (ss >> parsed); - - if (t.ExpectValid) { - EXPECT_TRUE (t.DomainName.valid ()) << "expected " << t.DomainName << " to be a valid domain, but it is not."; - EXPECT_TRUE (bool (ss)) << "expected to be able to read " << t.DomainName << " as a domain name."; - EXPECT_TRUE (ss.peek () == std::char_traits::eof ()); - EXPECT_EQ (parsed, t.DomainName); - } else { - EXPECT_FALSE (t.DomainName.valid ()) << "expected " << t.DomainName << " to be an invalid domain, but it is valid."; - EXPECT_TRUE (!bool (ss) || ss.peek () != std::char_traits::eof ()) << - "expected not to be able to read " << t.DomainName << " as a domain name."; - } - } - - } - - std::regex ip_v4_regex {"(((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4})"}; - std::regex ip_v6_regex {"((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?"}; - - // TODO try out the >> format with these. - // https://en.wikipedia.org/wiki/Module:IPAddress/testcases - TEST (IP, IPAddressFormat) { - - struct test_case { - address Address; - int32 Version; - string Bytes; - - void test () const { - - if (Version == 4) EXPECT_TRUE (std::regex_match (Address, ip_v4_regex)); - else if (Version == 6) EXPECT_TRUE (std::regex_match (Address, ip_v6_regex)); - - address parsed; - std::stringstream ss {Address}; - EXPECT_NO_THROW (ss >> parsed); - - if (Version == 4 || Version == 6) { - EXPECT_TRUE (bool (ss)) << "Expected to be able to parse " << Address << " as an IP address but failed."; - EXPECT_TRUE (bool (ss)); - EXPECT_TRUE (ss.peek () == std::char_traits::eof ()); - } else { - EXPECT_FALSE (bool (ss)) << "Expected not to be able to parse " << Address << " as an IP address but succeeded."; - EXPECT_TRUE (!bool (ss) || ss.peek () != std::char_traits::eof ()); - } - - EXPECT_EQ (Address.valid (), Version >= 0) - << "Address " << Address << " is" << (Version >= 0 ? " " : " not ") << "expected to be valid; version = " << Version; - - int32 version = Address.version (); - EXPECT_EQ (version, Version) << "expected " << Address << " to be version " << Version; - - uint16 port = 666; - - std::stringstream v6_format; - std::stringstream v4_format; - - v6_format << "[" << static_cast (Address) << "]:" << port; - v4_format << "" << static_cast (Address) << ":" << port; - - TCP::endpoint v6 {v6_format.str ()}; - TCP::endpoint v4 {v4_format.str ()}; - - EXPECT_EQ (v6.valid (), version == 6) << v6; - EXPECT_EQ (v4.valid (), version == 4) << v4; - - EXPECT_EQ (bytes (Address), *encoding::hex::read (Bytes)); - if (v6.valid ()) { - EXPECT_EQ (v6.address (), Address); - } else if (v4.valid ()) { - EXPECT_EQ (v4.address (), Address); - } - - } - }; - - test_case test_cases [] { - {"200.200.200.200", 4, "c8c8c8c8"}, - // whitespace not currently allowed - {" 200.200.200.200", -1, ""}, - // whitespace not currently allowed - {"200.200.200.200 ", -1, ""}, - {"200.200.256.200", -1, ""}, - {"200.200.200.200.", -1, ""}, - {"200.200.200", -1, ""}, - {"200.200.200.2d0", -1, ""}, - {"0.0.0.0", 4, "00000000"}, - // according to talkpage, leading zeroes unacceptable. - {"00.00.00.00", -1, ""}, - // according to talkpage, leading zeroes unacceptable. - {"100.100.020.100", -1, ""}, - {"255.255.255.255", 4, "ffffffff"}, - {"-1.0.0.0", -1, ""}, - {"200000000000000000000000000000000000000000000000000000000000000000000000000000.200.200.200", -1, ""}, - {"00000000000005.10.10.10", -1, ""}, - // full length - {"00AB:0002:3008:8CFD:00AB:0002:3008:8CFD", 6, "00AB000230088CFD00AB000230088CFD"}, - // lowercase - {"00ab:0002:3008:8cfd:00ab:0002:3008:8cfd", 6, "00AB000230088CFD00AB000230088CFD"}, - // mixed case - {"00aB:0002:3008:8cFd:00Ab:0002:3008:8cfD", 6, "00AB000230088CFD00AB000230088CFD"}, - // at most 4 digits per segment - {"00AB:00002:3008:8CFD:00AB:0002:3008:8CFD", -1, ""}, - // can"t remove all 0s from first segment unless using :: - {":0002:3008:8CFD:00AB:0002:3008:8CFD", -1, ""}, - // can"t remove all 0s from last segment unless using :: - {"00AB:0002:3008:8CFD:00AB:0002:3008:", -1, ""}, - // abbreviated - {"AB:02:3008:8CFD:AB:02:3008:8CFD", 6, "00AB000230088CFD00AB000230088CFD"}, - // too long - {"AB:02:3008:8CFD:AB:02:3008:8CFD:02", -1, ""}, - // correct use of :: - {"AB:02:3008:8CFD::02:3008:8CFD", 6, "00AB000230088CFD0000000230088CFD"}, - // too long - {"AB:02:3008:8CFD::02:3008:8CFD:02", -1, ""}, - // can"t have two ::s - {"AB:02:3008:8CFD::02::8CFD", -1, ""}, - // Invalid character G - {"GB:02:3008:8CFD:AB:02:3008:8CFD", -1, ""}, - // unassigned IPv6 address - {"::", 6, "00000000000000000000000000000000"}, - // loopback IPv6 address - // could be 0 to 7. - {"::1", 6, "00000000000000000000000000000001"}, - // another name for unassigned IPv6 address - {"0::", 6, "00000000000000000000000000000000"}, - // another name for unassigned IPv6 address - {"0::0", 6, "00000000000000000000000000000000"}, - // illegal: three colons - {"2:::3", -1, ""}, - // full form of IPv6 - {"fe80:0000:0000:0000:0204:61ff:fe9d:f156", 6, "fe80000000000000020461fffe9df156"}, - // drop leading zeroes - {"fe80:0:0:0:204:61ff:fe9d:f156", 6,"fe80000000000000020461fffe9df156"}, - // collapse multiple zeroes to :: in the IPv6 address - {"fe80::204:61ff:fe9d:f156", 6, "fe80000000000000020461fffe9df156"}, - // IPv4 dotted quad at the end"fe80000000000000020461fffe9df156" - {"fe80:0000:0000:0000:0204:61ff:254.157.241.86", 6, "fe80000000000000020461fffe9df156"}, - // drop leading zeroes, IPv4 dotted quad at the end - {"fe80:0:0:0:0204:61ff:254.157.241.86", 6, "fe80000000000000020461fffe9df156"}, - // dotted quad at the end, multiple zeroes collapsed - {"fe80::204:61ff:254.157.241.86", 6, "fe80000000000000020461fffe9df156"}, - // link-local prefix - {"fe80::", 6, "fe800000000000000000000000000000"}, - // global unicast prefix - {"2001::", 6, "20010000000000000000000000000000"} - }; - - for (const test_case &t : test_cases) t.test (); - } -} - -namespace data::net { - - TEST (IP, URLFormat) { - - struct positive_test_case { - - net::URL URL; - - data::ASCII Scheme; - maybe Authority; - data::ASCII Path; - maybe Query; - maybe Fragment; - - maybe UserInfo; - maybe Host; - maybe Port; - maybe> QueryMap; - - maybe PortNumber; - data::ASCII PortDNS; - maybe HostDNS; - maybe HostAddress; - - }; - - // valid URLs. - for (const positive_test_case &tt : list {{ - "ftp://ftp.is.co.za/rfc/rfc1808.txt", - "ftp", {"ftp.is.co.za"}, - "/rfc/rfc1808.txt", {}, {}, - {}, "ftp.is.co.za", {}, {}, - {}, "ftp", {"ftp.is.co.za"}, {} - }, { - "http://www.ietf.org/rfc/rfc2396.txt", - "http", {"www.ietf.org"}, "/rfc/rfc2396.txt", {}, {}, - {}, "www.ietf.org", {}, {}, - {}, "http", {"www.ietf.org"}, {} - }, { - "ldap://[2001:db8::7]/c=GB?objectClass?one", - "ldap", "[2001:db8::7]", "/c=GB", - "objectClass?one", {}, - {}, "[2001:db8::7]", {}, {}, - {}, "ldap", {}, {"2001:db8::7"} - }, { - "mailto:John.Doe@example.com", - "mailto", {}, "John.Doe@example.com", {}, {}, - {}, {}, {}, {}, - {}, "mailto", {}, {} - }, { - "news:comp.infosystems.www.servers.unix", - "news", {}, - "comp.infosystems.www.servers.unix", {}, {}, - {}, {}, {}, {}, - {}, "news", {}, {} - }, { - "tel:+1-816-555-1212", - "tel", {}, - "+1-816-555-1212", {}, {}, {}, {}, {}, {}, - {}, "tel", {}, {} - }, { - "telnet://192.0.2.16:80/", - "telnet", "192.0.2.16:80", "/", {}, {}, - {}, "192.0.2.16", "80", {}, - {80}, "80", {}, {"192.0.2.16"} - }, { - "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", - "urn", {}, "oasis:names:specification:docbook:dtd:xml:4.1.2", {}, {}, - {}, {}, {}, {}, - {}, "urn", {}, {} - }, { - "foo://example.com:8042/over/there?name=ferret&size=long#nose", - "foo", "example.com:8042", - "/over/there", "name=ferret&size=long", "nose", - {}, "example.com", "8042", {{{"name", "ferret"}, {"size", "long"}}}, - {8042}, "8042", {"example.com"}, {} - }, { - "urn:example:animal:ferret:nose", - "urn", {}, "example:animal:ferret:nose", {}, {}, - {}, {}, {}, {}, - {}, "urn", {}, {} - }, { - "http://www.example.com", - "http", "www.example.com", "", {}, {}, - {}, "www.example.com", {}, {}, - {}, "http", {"www.example.com"}, {} - }, { - "https://example.org:8080/path/to/resource", - "https", "example.org:8080", "/path/to/resource", {}, {}, - {}, "example.org", "8080", {}, - {8080}, "8080", {"example.org"}, {} - }, { - "ftp://example.com/resource?param1=value1¶m2=value2", - "ftp", "example.com", "/resource", - "param1=value1¶m2=value2", {}, - {}, "example.com", {}, {{{"param1", "value1"}, {"param2", "value2"}}}, - {}, "ftp", {"example.com"}, {} - }, { - "mailto:user@example.com", - "mailto", {}, "user@example.com", {}, {}, - {}, {}, {}, {}, - {}, "mailto", {}, {} - }, { - "https://example.com/path/to/resource#fragment", - "https", "example.com", "/path/to/resource", {}, "fragment", - {}, "example.com", {}, {}, - {}, "https", {"example.com"}, {} - }, { - "https://example.com/path?query#", "https", "example.com", "/path", "query", {""}, - {}, {"example.com"}, {}, {}, - {}, "https", {"example.com"}, {} - }, { - "http://", "http", {""}, "", {}, {}, - {}, {""}, {}, {}, - {}, "http", {}, {} - }, { - "http://www.ex_ample.com", "http", {"www.ex_ample.com"}, "", {}, {}, - {}, {"www.ex_ample.com"}, {}, {}, - {}, "http", {}, {} - } - }) { - - EXPECT_TRUE (tt.URL.valid ()) << "expected " << tt.URL << " to be a valid URL."; - - URL parsed; - std::stringstream ss {tt.URL}; - EXPECT_NO_THROW (ss >> parsed); - EXPECT_TRUE (bool (ss)); - EXPECT_TRUE (ss.peek () == std::char_traits::eof ()); - - EXPECT_EQ (tt.URL.scheme (), tt.Scheme); - EXPECT_EQ (tt.URL.authority (), tt.Authority) << "incorrect authority retrieved for " << tt.URL; - EXPECT_EQ (tt.URL.path (), tt.Path); - EXPECT_EQ (tt.URL.query (), tt.Query) << "incorrect query retrieved for " << tt.URL; - EXPECT_EQ (tt.URL.fragment (), tt.Fragment) << "incorrect fragment retrieved for " << tt.URL; - - auto make_url_1 = URL::make {}.scheme (tt.Scheme).path (tt.Path); - if (tt.Fragment) make_url_1 = make_url_1.fragment (*tt.Fragment); - if (tt.Query) make_url_1 = make_url_1.query (*tt.Query); - if (tt.Authority) make_url_1 = make_url_1.authority (*tt.Authority); - - auto url_1 = URL (make_url_1); - EXPECT_EQ (tt.URL, url_1) << "expected " << tt.URL << " == " << url_1 << std::endl; - - EXPECT_EQ (tt.URL.user_info (), tt.UserInfo) << "incorrect user_info retrieved for " << tt.URL; - EXPECT_EQ (tt.URL.host (), tt.Host) << "incorrect host retrieved for " << tt.URL; - EXPECT_EQ (tt.URL.port (), tt.Port) << "incorrect port retrieved for " << tt.URL; - - EXPECT_EQ (tt.URL.port_number (), tt.PortNumber); - EXPECT_EQ (tt.URL.port_DNS (), tt.PortDNS); - EXPECT_EQ (tt.URL.domain_name (), tt.HostDNS); - EXPECT_EQ (tt.URL.address (), tt.HostAddress) << "incorrect host address retrieved for " << tt.URL; - - auto make_url_2 = URL::make {}.scheme (tt.Scheme).path (tt.Path); - if (tt.Fragment) make_url_2 = make_url_2.fragment (*tt.Fragment); - if (tt.Query) make_url_2 = make_url_2.query (*tt.Query); - - if (tt.UserInfo) make_url_2 = make_url_2.user_info (*tt.UserInfo); - if (tt.HostAddress) make_url_2 = make_url_2.address (*tt.HostAddress); - else if (tt.HostDNS) make_url_2 = make_url_2.domain_name (*tt.HostDNS); - else if (tt.Host) make_url_2 = make_url_2.registered_name (*tt.Host); - if (tt.PortNumber) make_url_2 = make_url_2.port (*tt.PortNumber); - - auto url_2 = URL (make_url_2); - EXPECT_EQ (tt.URL, url_2); - - if (tt.QueryMap) { - EXPECT_EQ (tt.URL.query_map (), tt.QueryMap); - - auto make_url_3 = URL::make {}.scheme (tt.Scheme).path (tt.Path); - if (tt.Fragment) make_url_3 = make_url_3.fragment (*tt.Fragment); - if (tt.Query) make_url_3 = make_url_3.query_map (*tt.QueryMap); - if (tt.Authority) make_url_3 = make_url_3.authority (*tt.Authority); - } - - } - - struct negative_test_case { - net::URL URL; - }; - - for (const negative_test_case &tt : list { - {"https//example.org"}, // (Malformed scheme) - {"https&:/example.org"}, // (Malformed scheme) - }) { - EXPECT_FALSE (tt.URL.valid ()) << "expected " << tt.URL << " to be an invalid URL."; - } - - } - - TEST (IP, MakeURL) { - - EXPECT_EQ (URL {"http://example.com"}, URL (URL::make {"http://example.com"})); - EXPECT_EQ (URL {"http://example.com"}, URL (URL::make {}.protocol ("http").domain_name ("example.com"))); - EXPECT_EQ (URL {"http://example.com"}, URL (URL::make {}.scheme ("http").authority ("example.com"))); - - URL url_example_A {"http://example.com/test?query"}; - auto url_example_A_remade = URL (URL::make {}.protocol ("http").path ("/test").domain_name ("example.com").query ("query")); - EXPECT_EQ (url_example_A, url_example_A_remade) << "Expected " << url_example_A << " to equal " << url_example_A_remade; - - EXPECT_EQ (URL {"http://example.com/test?query"}, URL (URL::make {"http://example.com"}.query ("query").path ("/test"))); - - URL url_example_C {"zoom://moop:zoop@something.nothing:4321?weem=peen&zap=bap#floop"}; - URL url_example_C_remade = URL (URL::make {}.user_name_pass ("moop", "zoop").query_map ({{"weem", "peen"}, {"zap", "bap"}}).protocol - ("zoom").fragment ("floop").domain_name ("something.nothing").port (4321)); - EXPECT_EQ (url_example_C, url_example_C_remade) << "Expected " << url_example_C << " to equal " << url_example_C_remade;; - - } - - TEST (IP, URLEqual) { - struct test_equal { - URL Left; - URL Right; - bool Expected; - }; - - for (const test_equal &tt : list { - {"http://example.com", "http://example.com", true}, // Identical URLs - {"http://example.com", "HTTP://example.com", true}, // Case-insensitive scheme - // Hex encoding of non-reserved characters - {"http://example.com", "http://%65%78%61%6D%70%6C%65.com", true}, - {"http://example.com/test", "http://example.com/test", true}, // Identical path - {"http://example.com/test", "http://example.com/TEST", false}, // Case-sensitive path - {"http://example.com/test?query", "http://example.com/test?QUERY", false}, // Case-sensitive query - // Case-sensitive fragment - {"http://example.com/test#fragment", "http://example.com/test#FRAGMENT", false}, - // Mixed case-sensitivity - {"http://example.com/test?query#fragment", "http://example.com/test?query#FRAGMENT", false}, - // All case-sensitive parts - {"http://example.com/test?query#fragment", "http://example.com/test?QUERY#FRAGMENT", false}, - {"http://example.com:80", "http://example.com:80", true}, // Identical port - {"http://example.com:80", "http://example.com:8080", false}, // Different port - {"http://www.example.com", "http://WWW.EXAMPLE.COM", true}, // Domain is case-insensitive - {"http://www.example.com", "http://www.example.com:80", false}, // Default port for HTTP is 80 - {"https://www.example.com", "https://www.example.com:443", false}, // Default port for HTTPS is 443 - {"http://www.example.com", "http://www.example.com:81", false}, // Different ports - {"http://www.example.com/path", "http://www.example.com/PATH", false}, // Path is case-sensitive - {"http://www.example.com?query=test", "http://www.example.com?QUERY=TEST", false}, // Query is case-sensitive - {"http://www.example.com#fragment", "http://www.example.com#FRAGMENT", false}, // Fragment is case-sensitive - {"http://www.example.com/%7efoo", "http://www.example.com/~foo", true}, // Non-reserved character percent-encoded - {"http://www.example.com?query=%3D", "http://www.example.com?query==", false}, // Reserved character percent-encoded - {"http://www.example.com", "https://www.example.com", false}, // Different schemes - {"http://www.example.com", "http://www.example.org", false}, // Different domain names - {"http://www.example.com/path", "http://www.example.com/another-path", false}, // Different paths - {"http://www.example.com?query=test", "http://www.example.com?query=another-test", false}, // Different queries - {"http://www.example.com#fragment", "http://www.example.com#another-fragment", false} // Different fragments - }) { - EXPECT_EQ (tt.Left == tt.Right, tt.Expected) << "expected \"" << tt.Left << "\" == \"" << tt.Right << "\"" << std::endl; - } - - } - - TEST (IP, URLParameters) { - URL yes {"fnorp://zeep:zoop@moop.pim.pom?weep=warp&beep=boop"}; - URL no {"fnorp://zeep:zoop:zap@moop.pim.pom?weep&boop"}; - - EXPECT_TRUE (yes.user_name_pass ()); - EXPECT_TRUE (yes.query_map ()); - - EXPECT_FALSE (no.user_name_pass ()); - EXPECT_FALSE (no.query_map ()); - - } - - TEST (IP, Endpoint) { - IP::TCP::endpoint e {"0.0.0.0:1234"}; - EXPECT_EQ (e.address (), "0.0.0.0"); - EXPECT_EQ (e.port (), 1234); - } - -} diff --git a/test/abs.cpp b/test/abs.cpp index 612c0ed4..f8daf54d 100644 --- a/test/abs.cpp +++ b/test/abs.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/math.hpp" -#include "gtest/gtest.h" -#include +#include + +#include + namespace data { TEST (Abs, Decrement) { diff --git a/test/algebra.cpp b/test/algebra.cpp index fb73b9bd..0acc375f 100644 --- a/test/algebra.cpp +++ b/test/algebra.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "gtest/gtest.h" +#include namespace data::math::linear { diff --git a/test/args_parser.cpp b/test/args_parser.cpp deleted file mode 100644 index 2bfd4d26..00000000 --- a/test/args_parser.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2023 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/io/arg_parser.hpp" -#include "data/exception.hpp" -#include "gtest/gtest.h" - -namespace data::io::args { - - parsed parse (const std::vector &v) { - return parsed {static_cast (v.size ()), v.data ()}; - } - - TEST (Args, LongFlag1) { - auto parsed = parse ({"/prog", "--verbose"}); - - EXPECT_TRUE (parsed.has ("verbose")); - EXPECT_FALSE (parsed.has ("help")); - } - - TEST (Args, LongFlag2) { - auto parsed = parse ({"/prog", "--verbose", "--help"}); - - EXPECT_TRUE (parsed.has ("verbose")); - EXPECT_TRUE (parsed.has ("help")); - } - - TEST (Args, LongOptionEquals) { - auto parsed = parse ({"/prog", "--output=file.txt"}); - - maybe file; - parsed.get ("output", file); - EXPECT_TRUE (file.has_value ()); - EXPECT_EQ (*file, "file.txt"); - } - - // not a key value, but a flag and positional. - TEST (Args, NotKeyValue) { - auto parsed = parse ({"/prog", "--output", "file.txt"}); - - maybe file; - parsed.get ("output", file); - EXPECT_FALSE (file.has_value ()); - - parsed.get (1, file); - EXPECT_TRUE (file.has_value ()); - } - - TEST (Args, GetByKeySuccessInt) { - auto parsed = parse ({"/prog", "--count=42"}); - - maybe v; - parsed.get ("count", v); - EXPECT_TRUE (v.has_value ()); - EXPECT_EQ (*v, 42); - } - - TEST (Args, GetByKeyMissing) { - auto parsed = parse ({"/prog", "--verbose"}); - - maybe v; - parsed.get ("count", v); - EXPECT_FALSE (v.has_value ()); - - maybe file; - parsed.get ("verbose", file); - EXPECT_FALSE (file.has_value ()); - } - - TEST (Args, GetByKeyInvalidType) { - auto parsed = parse ({"/prog", "--count=abc"}); - - maybe v; - parsed.get ("count", v); - EXPECT_FALSE (v.has_value ()); - } - - TEST (Args, GetByKeyBoolFlag) { - auto parsed = parse ({"/prog", "--verbose=true"}); - - maybe v; - parsed.get ("verbose", v); - EXPECT_TRUE (v.has_value ()); - EXPECT_TRUE (*v); - } - - TEST (Args, GetByPositionSuccess) { - auto parsed = parse ({"/prog", "123"}); - - maybe v; - parsed.get (1, v); - EXPECT_TRUE (v.has_value ()); - EXPECT_EQ (*v, 123); - } - - TEST (Args, GetByPositionOutOfRange) { - auto parsed = parse ({"/prog", "123"}); - - maybe v; - parsed.get (2, v); - EXPECT_FALSE (v.has_value ()); - } - - TEST (Args, GetByPositionInvalidType) { - auto parsed = parse ({"/prog", "abc"}); - - maybe v; - parsed.get (1, v); - EXPECT_FALSE (v.has_value ()); - } - - TEST (Args, FlagOptionPos) { - auto parsed1 = parse ({"/prog", "123", "--zoob=noob", "--bervose"}); - auto parsed2 = parse ({"/prog", "--zoob=noob", "123", "--bervose"}); - auto parsed3 = parse ({"/prog", "--zoob=noob", "--bervose", "123"}); - - maybe v1; - parsed1.get (1, v1); - EXPECT_TRUE (v1.has_value ()); - - maybe v2; - parsed2.get (1, v2); - EXPECT_TRUE (v2.has_value ()); - - maybe v3; - parsed3.get (1, v3); - EXPECT_TRUE (v3.has_value ()); - - EXPECT_EQ (*v1, *v2); - EXPECT_EQ (*v2, *v3); - } - - // tests with abbeviated flags - TEST (Args, ShortFlag1) { - auto parsed = parse ({"/prog", "-abc"}); - - EXPECT_TRUE (parsed.has ("a")); - EXPECT_TRUE (parsed.has ("b")); - EXPECT_FALSE (parsed.has ("d")); - } - - TEST (Args, ShortFlag2) { - auto parsed = parse ({"/prog", "-ab", "-cd"}); - - EXPECT_TRUE (parsed.has ("a")); - EXPECT_TRUE (parsed.has ("b")); - EXPECT_TRUE (parsed.has ("d")); - } - - // this is really a positional argument, not a key value. - TEST (Args, ShortWithValue) { - auto parsed = parse ({"/prog", "-o=file.txt"}); - - maybe what; - - parsed.get ("o", what); - EXPECT_FALSE (what.has_value ()); - - parsed.get (1, what); - EXPECT_TRUE (what.has_value ()); - } - - TEST (Args, Repetitions) { - EXPECT_NO_THROW ((parse ({"/prog", "-ab", "--a"}))); - EXPECT_NO_THROW ((parse ({"/prog", "--option=value", "--option=value"}))); - } - - TEST (Args, FlagPresent) { - - const char *argv[] = {"/test", "--verbose"}; - - parsed p {2, argv}; - - command schema {set {"verbose"}, schema::list::value (), schema::map::empty ()}; - - EXPECT_NO_THROW (validate (p, schema)); - } - -} diff --git a/test/async.cpp b/test/async.cpp deleted file mode 100644 index 420f12fd..00000000 --- a/test/async.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2026 Katrina Swales -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/async.hpp" -#include "gtest/gtest.h" - -namespace data { - - awaitable async_return_void () { - co_return; - } - - awaitable async_return_int (int i) { - co_return i; - } - - // we had a problem earlier with synced not - // working on types that are not default - // initializable, so we have a test for it. - struct poop { - poop (int) {} - }; - - awaitable async_return_poop (int i) { - co_return poop {i}; - } - - TEST (Async, Construct) { - synced (async_return_void); - - synced ([] { return async_return_int (1); }); - - synced (async_return_int, 1); - synced (async_return_poop, 1); - } - -} - diff --git a/test/base64.cpp b/test/base64.cpp index 4d3e078c..bcc81a09 100644 --- a/test/base64.cpp +++ b/test/base64.cpp @@ -1,8 +1,9 @@ -#include "data/encoding/base64.hpp" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" +#include + +#include +#include +#include namespace data::encoding { diff --git a/test/bit_ops.cpp b/test/bit_ops.cpp index 00ca71a0..9420e864 100644 --- a/test/bit_ops.cpp +++ b/test/bit_ops.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" -#include -#include +#include +#include +#include namespace data { diff --git a/test/bounded.cpp b/test/bounded.cpp index d5f7fc11..1eb51144 100644 --- a/test/bounded.cpp +++ b/test/bounded.cpp @@ -2,13 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include #include -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" -#include + +#include +#include +#include namespace data { diff --git a/test/bytes.cpp b/test/bytes.cpp index d931caa8..d7d07159 100644 --- a/test/bytes.cpp +++ b/test/bytes.cpp @@ -6,7 +6,8 @@ #include #include #include -#include "gtest/gtest.h" + +#include namespace data { diff --git a/test/circular_queue.cpp b/test/circular_queue.cpp deleted file mode 100644 index d0e36909..00000000 --- a/test/circular_queue.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Copyright (c) 2021 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include "gtest/gtest.h" -namespace data::tools { - - TEST (CircularQueue, Initialize) { - circular_queue queue (5); - queue.set (5); - auto n = queue.get (); - ASSERT_EQ (n, 0) << "Expected to retrieve 5 from the queue, but got " << n; - queue.set (4); - queue.set (6); - queue.set (8); - queue.set (10); - auto m = queue.get (); - ASSERT_EQ (m, 5) << "Expected to retrieve 10 from the queue, but got " << m; - } -} diff --git a/test/email.cpp b/test/email.cpp deleted file mode 100644 index 092483b3..00000000 --- a/test/email.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include -#include "gtest/gtest.h" - -namespace data::net::email { - - TEST (Email, Address) { - for (const ASCII &addr : list { - "example@email.com", - "example.first.middle.lastname@email.com", - "example@subdomain.email.com", - "example+firstname+lastname@email.com", - "example@234.234.234.234", - "example@[234.234.234.234]", - R"("example"@email.com)", - "0987654321@example.com", - "example@email-one.com", - "_______@email.com", - "example@email.name", - "example@email.museum", - "example@email.co.jp", - "example.firstname-lastname@email.com", - "jon.o'conner@example.com", - R"("bob" )", - R"(Set Na )", - "Joomba loomba " - }) EXPECT_TRUE (address::valid (addr)); - - for (const ASCII &addr : list { - R"(“example”@email.com)", - R"(very."(),:;<>[]".VERY."very@\\ "very".unusual@strange.email.example.com)"}) EXPECT_FALSE (address::valid (addr)); - } -} diff --git a/test/empty.cpp b/test/empty.cpp index f80f61b0..55db3db5 100644 --- a/test/empty.cpp +++ b/test/empty.cpp @@ -2,141 +2,142 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/stack.hpp" -#include "data/list.hpp" -#include "data/tree.hpp" -#include "data/set.hpp" -#include "data/map.hpp" -#include "data/priority_queue.hpp" -#include "data/dispatch.hpp" -#include "data/cycle.hpp" -#include "data/cross.hpp" -#include "data/string.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include - TEST (Empty, Empty) { +#include - EXPECT_TRUE (empty (data::stack {})); - EXPECT_FALSE (empty (data::stack {1})); +TEST (Empty, Empty) { - EXPECT_TRUE (data::empty (data::stack {})); - EXPECT_FALSE (data::empty (data::stack {1})); + EXPECT_TRUE (empty (data::stack {})); + EXPECT_FALSE (empty (data::stack {1})); - EXPECT_TRUE (empty (data::list {})); - EXPECT_FALSE (empty (data::list {1})); + EXPECT_TRUE (data::empty (data::stack {})); + EXPECT_FALSE (data::empty (data::stack {1})); - EXPECT_TRUE (data::empty (data::list {})); - EXPECT_FALSE (data::empty (data::list {1})); + EXPECT_TRUE (empty (data::list {})); + EXPECT_FALSE (empty (data::list {1})); - EXPECT_TRUE (empty (data::tree {})); - EXPECT_FALSE (empty (data::tree {1})); + EXPECT_TRUE (data::empty (data::list {})); + EXPECT_FALSE (data::empty (data::list {1})); - EXPECT_TRUE (data::empty (data::tree {})); - EXPECT_FALSE (data::empty (data::tree {1})); + EXPECT_TRUE (empty (data::tree {})); + EXPECT_FALSE (empty (data::tree {1})); - EXPECT_TRUE (empty (data::set {})); - EXPECT_FALSE (empty (data::set {1})); + EXPECT_TRUE (data::empty (data::tree {})); + EXPECT_FALSE (data::empty (data::tree {1})); - EXPECT_TRUE (data::empty (data::set {})); - EXPECT_FALSE (data::empty (data::set {1})); + EXPECT_TRUE (empty (data::set {})); + EXPECT_FALSE (empty (data::set {1})); - EXPECT_TRUE (empty (data::ordered_sequence {})); - EXPECT_FALSE (empty (data::ordered_sequence {1})); + EXPECT_TRUE (data::empty (data::set {})); + EXPECT_FALSE (data::empty (data::set {1})); - EXPECT_TRUE (data::empty (data::ordered_sequence {})); - EXPECT_FALSE (data::empty (data::ordered_sequence {1})); + EXPECT_TRUE (empty (data::ordered_sequence {})); + EXPECT_FALSE (empty (data::ordered_sequence {1})); - EXPECT_TRUE (empty (data::priority_queue {})); - EXPECT_FALSE (empty (data::priority_queue {1})); + EXPECT_TRUE (data::empty (data::ordered_sequence {})); + EXPECT_FALSE (data::empty (data::ordered_sequence {1})); - EXPECT_TRUE (data::empty (data::priority_queue {})); - EXPECT_FALSE (data::empty (data::priority_queue {1})); + EXPECT_TRUE (empty (data::priority_queue {})); + EXPECT_FALSE (empty (data::priority_queue {1})); - EXPECT_TRUE (empty (data::map {})); - EXPECT_FALSE (empty (data::map {{1, 1}})); + EXPECT_TRUE (data::empty (data::priority_queue {})); + EXPECT_FALSE (data::empty (data::priority_queue {1})); - EXPECT_TRUE (data::empty (data::map {})); - EXPECT_FALSE (data::empty (data::map {{1, 1}})); + EXPECT_TRUE (empty (data::map {})); + EXPECT_FALSE (empty (data::map {{1, 1}})); - EXPECT_TRUE (empty (data::cycle {})); - EXPECT_FALSE (empty (data::cycle {1})); + EXPECT_TRUE (data::empty (data::map {})); + EXPECT_FALSE (data::empty (data::map {{1, 1}})); - EXPECT_TRUE (data::empty (data::cycle {})); - EXPECT_FALSE (data::empty (data::cycle {1})); + EXPECT_TRUE (empty (data::cycle {})); + EXPECT_FALSE (empty (data::cycle {1})); - EXPECT_TRUE (empty (data::dispatch {})); - EXPECT_FALSE (empty (data::dispatch {{1, 1}})); + EXPECT_TRUE (data::empty (data::cycle {})); + EXPECT_FALSE (data::empty (data::cycle {1})); - EXPECT_TRUE (data::empty (data::dispatch {})); - EXPECT_FALSE (data::empty (data::dispatch {{1, 1}})); + EXPECT_TRUE (empty (data::dispatch {})); + EXPECT_FALSE (empty (data::dispatch {{1, 1}})); - // TODO the reason we have to use data::empty is - // that these types inheret from std types, which - // is considered bad practice anyway. - EXPECT_TRUE (data::empty (data::cross {})); - EXPECT_TRUE (data::empty (data::string {})); + EXPECT_TRUE (data::empty (data::dispatch {})); + EXPECT_FALSE (data::empty (data::dispatch {{1, 1}})); - } + // TODO the reason we have to use data::empty is + // that these types inheret from std types, which + // is considered bad practice anyway. + EXPECT_TRUE (data::empty (data::cross {})); + EXPECT_TRUE (data::empty (data::string {})); - TEST (Empty, Size) { +} - EXPECT_EQ (size (data::stack {}), 0); - EXPECT_EQ (size (data::stack {1}), 1); +TEST (Empty, Size) { - EXPECT_EQ (data::size (data::stack {}), 0); - EXPECT_EQ (data::size (data::stack {1}), 1); + EXPECT_EQ (size (data::stack {}), 0); + EXPECT_EQ (size (data::stack {1}), 1); - EXPECT_EQ (size (data::list {}), 0); - EXPECT_EQ (size (data::list {1}), 1); + EXPECT_EQ (data::size (data::stack {}), 0); + EXPECT_EQ (data::size (data::stack {1}), 1); - EXPECT_EQ (data::size (data::list {}), 0); - EXPECT_EQ (data::size (data::list {1}), 1); + EXPECT_EQ (size (data::list {}), 0); + EXPECT_EQ (size (data::list {1}), 1); - EXPECT_EQ (size (data::tree {}), 0); - EXPECT_EQ (size (data::tree {1}), 1); + EXPECT_EQ (data::size (data::list {}), 0); + EXPECT_EQ (data::size (data::list {1}), 1); - EXPECT_EQ (data::size (data::tree {}), 0); - EXPECT_EQ (data::size (data::tree {1}), 1); + EXPECT_EQ (size (data::tree {}), 0); + EXPECT_EQ (size (data::tree {1}), 1); - EXPECT_EQ (size (data::set {}), 0); - EXPECT_EQ (size (data::set {1}), 1); + EXPECT_EQ (data::size (data::tree {}), 0); + EXPECT_EQ (data::size (data::tree {1}), 1); - EXPECT_EQ (data::size (data::set {}), 0); - EXPECT_EQ (data::size (data::set {1}), 1); + EXPECT_EQ (size (data::set {}), 0); + EXPECT_EQ (size (data::set {1}), 1); - EXPECT_EQ (size (data::ordered_sequence {}), 0); - EXPECT_EQ (size (data::ordered_sequence {1}), 1); + EXPECT_EQ (data::size (data::set {}), 0); + EXPECT_EQ (data::size (data::set {1}), 1); - EXPECT_EQ (data::size (data::ordered_sequence {}), 0); - EXPECT_EQ (data::size (data::ordered_sequence {1}), 1); + EXPECT_EQ (size (data::ordered_sequence {}), 0); + EXPECT_EQ (size (data::ordered_sequence {1}), 1); - EXPECT_EQ (size (data::priority_queue {}), 0); - EXPECT_EQ (size (data::priority_queue {1}), 1); + EXPECT_EQ (data::size (data::ordered_sequence {}), 0); + EXPECT_EQ (data::size (data::ordered_sequence {1}), 1); - EXPECT_EQ (data::size (data::priority_queue {}), 0); - EXPECT_EQ (data::size (data::priority_queue {1}), 1); + EXPECT_EQ (size (data::priority_queue {}), 0); + EXPECT_EQ (size (data::priority_queue {1}), 1); - EXPECT_EQ (size (data::map {}), 0); - EXPECT_EQ (size (data::map {{1, 1}}), 1); + EXPECT_EQ (data::size (data::priority_queue {}), 0); + EXPECT_EQ (data::size (data::priority_queue {1}), 1); - EXPECT_EQ (data::size (data::map {}), 0); - EXPECT_EQ (data::size (data::map {{1, 1}}), 1); + EXPECT_EQ (size (data::map {}), 0); + EXPECT_EQ (size (data::map {{1, 1}}), 1); - EXPECT_EQ (size (data::cycle {}), 0); - EXPECT_EQ (size (data::cycle {1}), 1); + EXPECT_EQ (data::size (data::map {}), 0); + EXPECT_EQ (data::size (data::map {{1, 1}}), 1); - EXPECT_EQ (data::size (data::cycle {}), 0); - EXPECT_EQ (data::size (data::cycle {1}), 1); + EXPECT_EQ (size (data::cycle {}), 0); + EXPECT_EQ (size (data::cycle {1}), 1); - EXPECT_EQ (size (data::dispatch {}), 0); - EXPECT_EQ (size (data::dispatch {{1, 1}}), 1); + EXPECT_EQ (data::size (data::cycle {}), 0); + EXPECT_EQ (data::size (data::cycle {1}), 1); - EXPECT_EQ (data::size (data::dispatch {}), 0); - EXPECT_EQ (data::size (data::dispatch {{1, 1}}), 1); + EXPECT_EQ (size (data::dispatch {}), 0); + EXPECT_EQ (size (data::dispatch {{1, 1}}), 1); - // TODO same problem here. - EXPECT_EQ (data::size (data::cross {}), 0); - EXPECT_EQ (data::size (data::string {}), 0); + EXPECT_EQ (data::size (data::dispatch {}), 0); + EXPECT_EQ (data::size (data::dispatch {{1, 1}}), 1); - } + // TODO same problem here. + EXPECT_EQ (data::size (data::cross {}), 0); + EXPECT_EQ (data::size (data::string {}), 0); + +} diff --git a/test/endian.cpp b/test/endian.cpp index c8bc3572..7d7c256f 100644 --- a/test/endian.cpp +++ b/test/endian.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include "gtest/gtest.h" +#include namespace data { diff --git a/test/exception.cpp b/test/exception.cpp index 40ba0ae6..b51d2bb9 100644 --- a/test/exception.cpp +++ b/test/exception.cpp @@ -4,7 +4,8 @@ #include #include -#include "gtest/gtest.h" + +#include namespace data { diff --git a/test/flatten.cpp b/test/flatten.cpp index 74eeb086..e5d76aa7 100644 --- a/test/flatten.cpp +++ b/test/flatten.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/flatten.hpp" -#include "data/list.hpp" -#include "gtest/gtest.h" +#include +#include +#include namespace data { diff --git a/test/for_each.cpp b/test/for_each.cpp index 452c4daa..f9cb0537 100644 --- a/test/for_each.cpp +++ b/test/for_each.cpp @@ -2,13 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/for_each.hpp" -#include "data/ordered_sequence.hpp" -#include "data/list.hpp" -#include "data/array.hpp" -#include "data/tree.hpp" -#include "data/string.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include + +#include namespace data { diff --git a/test/functional_interfaces.cpp b/test/functional_interfaces.cpp index 76fae6bb..16dbb3e3 100644 --- a/test/functional_interfaces.cpp +++ b/test/functional_interfaces.cpp @@ -11,6 +11,7 @@ #include "data/replace.hpp" #include "data/select.hpp" #include "data/lift.hpp" + #include "gtest/gtest.h" static_assert (std::convertible_to); diff --git a/test/get.cpp b/test/get.cpp index dc658258..982d3844 100644 --- a/test/get.cpp +++ b/test/get.cpp @@ -2,14 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/get.hpp" -#include "data/ordered_sequence.hpp" -#include "data/cross.hpp" -#include "data/array.hpp" -#include "data/list.hpp" -#include "data/stack.hpp" -#include "data/map.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include + +#include namespace data { diff --git a/test/hash.cpp b/test/hash.cpp index f04eb74b..e6966753 100644 --- a/test/hash.cpp +++ b/test/hash.cpp @@ -2,11 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/crypto/hash.hpp" -#include "data/math/number/bytes.hpp" -#include "data/list.hpp" -#include "data/encoding/endian.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include + +#include namespace data { diff --git a/test/hex.cpp b/test/hex.cpp index 16aef2f1..ee8699c9 100644 --- a/test/hex.cpp +++ b/test/hex.cpp @@ -2,10 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/bytes.hpp" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" +#include + +#include +#include +#include namespace data::encoding::hex { diff --git a/test/hexidecimal.cpp b/test/hexidecimal.cpp index a671c14f..22e4dc35 100644 --- a/test/hexidecimal.cpp +++ b/test/hexidecimal.cpp @@ -4,8 +4,7 @@ #include #include -#include -#include "gtest/gtest.h" +#include namespace data::math::number { diff --git a/test/integer_format.cpp b/test/integer_format.cpp index 0975a780..98f44fdc 100644 --- a/test/integer_format.cpp +++ b/test/integer_format.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "gtest/gtest.h" #include +#include + namespace data::encoding { TEST (IntegerFormat, IntegerFormat) { diff --git a/test/lift.cpp b/test/lift.cpp index f9eafad5..49f68a28 100644 --- a/test/lift.cpp +++ b/test/lift.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/lift.hpp" -#include "data/string.hpp" -#include "gtest/gtest.h" +#include +#include + +#include namespace data { diff --git a/test/linked_stack.cpp b/test/linked_stack.cpp index 819b153e..5248d563 100644 --- a/test/linked_stack.cpp +++ b/test/linked_stack.cpp @@ -3,16 +3,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include "data/remove.hpp" + +#include // TODO Right now numbers provides N and Z as well as Z_bytes and N_bytes // we don't like this because N has a dependency and Z_bytes does not. // We need a way to get Z_bytes without including N and Z. -#include "data/numbers.hpp" -#include "data/string.hpp" -#include "data/replace.hpp" -#include "data/lift.hpp" -#include "data/iterable.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include + +#include namespace data { diff --git a/test/list.cpp b/test/list.cpp index 22c3782b..7c5e73ae 100644 --- a/test/list.cpp +++ b/test/list.cpp @@ -7,7 +7,8 @@ #include #include #include -#include "gtest/gtest.h" + +#include namespace data { diff --git a/test/log.cpp b/test/log.cpp deleted file mode 100644 index 186304c6..00000000 --- a/test/log.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2021 Katrina Knight -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/io/log.hpp" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" - -namespace data::log { - - TEST (Log, Log) { - init (); - DATA_LOG_CHANNEL ("temp", normal) << "Hello, world!"; - indent x {}; - DATA_LOG_CHANNEL ("temp", normal) << "This line ought to be indented. "; - } -} diff --git a/test/maybe_either.cpp b/test/maybe_either.cpp index 46f62c89..36edd5f3 100644 --- a/test/maybe_either.cpp +++ b/test/maybe_either.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/maybe.hpp" -#include "data/either.hpp" -#include "gtest/gtest.h" +#include +#include + +#include namespace data { diff --git a/test/optional/CMakeLists.txt b/test/optional/CMakeLists.txt deleted file mode 100644 index d04ff385..00000000 --- a/test/optional/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required (VERSION 3.16) - -# Back compatibility for VERSION range -if (${CMAKE_VERSION} VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif () - -package_add_test (testHTTP testHTTP.cpp) -package_add_test (testWebsockets testWebsockets.cpp) diff --git a/test/optional/testHTTP.cpp b/test/optional/testHTTP.cpp deleted file mode 100644 index 7140ff73..00000000 --- a/test/optional/testHTTP.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2019 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/net/REST.hpp" -#include "data/net/HTTP_client.hpp" -#include "gtest/gtest.h" -#include -using json = nlohmann::json; - -namespace data { - - const std::string baseHttpUrl = "echo.jsontest.com"; - - TEST (HTTPTest, TestGetHttp) { - auto rest = data::net::HTTP::REST ("http", baseHttpUrl); - auto client = data::net::HTTP::client_blocking (rest); - auto request = rest.GET ("/key/pup/test/poppy"); - auto response = client (request); - json ex1 = json::parse (response.Body); - EXPECT_EQ (ex1["test"], "poppy") << "JSON mangled"; - EXPECT_EQ (ex1["key"], "pup") << "JSON mangled"; - } -} - diff --git a/test/optional/testWebsockets.cpp b/test/optional/testWebsockets.cpp deleted file mode 100644 index 38182709..00000000 --- a/test/optional/testWebsockets.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2019 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/net/websocket.hpp" - -#include "gtest/gtest.h" -#include -#include -#include -#include -#include "data/net/session.hpp" - -using json = nlohmann::json; -namespace ssl = boost::asio::ssl; // from - -namespace data { - - const std::string websocketUrl = "ws://localhost:8765/"; - - TEST (WebsocketsTest, TestWebsockets) { - boost::asio::io_context io; - ssl::context ctx {ssl::context::tlsv12_client}; - ctx.set_default_verify_paths (); - data::net::websocket::open (io, data::net::URL {"ws://localhost:8765/"}, &ctx, - [] (boost::system::error_code temp) { - FAIL () << "Failed with errror code: " << temp; - }, [] () {}, - [] (ptr> session) -> handler { - return [session] (string_view x) { - std::cout << "Received " << x << std::endl; - }; - }); - - using clock = std::chrono::system_clock; - clock::time_point nowp = clock::now (); - io.run (); - } -} - diff --git a/test/ordered_sequence.cpp b/test/ordered_sequence.cpp index 59e2c288..fb5ceee8 100644 --- a/test/ordered_sequence.cpp +++ b/test/ordered_sequence.cpp @@ -7,7 +7,8 @@ #include #include #include -#include "gtest/gtest.h" + +#include namespace data { diff --git a/test/permutation.cpp b/test/permutation.cpp index 2e65e5aa..3c9d22c6 100644 --- a/test/permutation.cpp +++ b/test/permutation.cpp @@ -3,8 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include "data/math/algebra/cyclic_group.hpp" -#include "gtest/gtest.h" +#include +#include namespace data { diff --git a/test/priority_queue.cpp b/test/priority_queue.cpp index c5d6105f..ed436fbb 100644 --- a/test/priority_queue.cpp +++ b/test/priority_queue.cpp @@ -2,10 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/string.hpp" -#include "data/container.hpp" -#include "data/priority_queue.hpp" -#include "gtest/gtest.h" +#include +#include +#include + +#include namespace data { static_assert (Sequence, int>); diff --git a/test/replace.cpp b/test/replace.cpp index 7d7b80ed..5685ad35 100644 --- a/test/replace.cpp +++ b/test/replace.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/replace.hpp" -#include "data/string.hpp" -#include "gtest/gtest.h" +#include +#include + +#include namespace data { diff --git a/test/reverse.cpp b/test/reverse.cpp index 1be5c0c5..85eb8085 100644 --- a/test/reverse.cpp +++ b/test/reverse.cpp @@ -2,15 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/list.hpp" -#include "data/stack.hpp" -#include "data/ordered_sequence.hpp" -#include "data/array.hpp" -#include "data/cycle.hpp" -#include "data/cross.hpp" -#include "gtest/gtest.h" #include +#include +#include +#include +#include +#include +#include + +#include + template void test_reverse () { list l {1, 2, 3}; list r {3, 2, 1}; diff --git a/test/schema.cpp b/test/schema.cpp deleted file mode 100644 index 8ce6f47a..00000000 --- a/test/schema.cpp +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright (c) 2024 Daniel Krawisz -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "data/encoding/unicode.hpp" -#include "data/tools/schema.hpp" -#include "data/math.hpp" -#include "data/net/URL.hpp" -#include "data/net/JSON.hpp" -#include "gtest/gtest.h" - -namespace data::schema::rule { - - static_assert (Same, decltype (-map {})>); - static_assert (Same, decltype (+map {})>); - - static_assert (Same>>, decltype (schema::map::key ("abcd"))>); - static_assert (Same>, decltype (+schema::map::key ("abcd"))>); - static_assert (Same>>, decltype (-+schema::map::key ("abcd"))>); - static_assert (Same>>>, decltype (*schema::map::key ("abcd"))>); - - static_assert (Same, value>>>, decltype (schema::map::key ("A") && schema::map::key ("B"))>); - static_assert (Same, value>>, decltype (schema::map::key ("A") && +schema::map::key ("B"))>); - static_assert (Same, optional>>>>, decltype (schema::map::key ("A") && *schema::map::key ("B"))>); - static_assert (Same, value>>, decltype (+(schema::map::key ("A") && schema::map::key ("B")))>); - - static_assert (Same ("A") || schema::map::key ("B")), - map, value>>>>); - - static_assert (Same, value, value>>>, - decltype (schema::map::key ("A") && schema::map::key ("B") && schema::map::key ("C"))>); - - static_assert (Same, optional>, optional>>>>, - decltype (schema::map::key ("A") && *schema::map::key ("B") && *schema::map::key ("C"))>); - - static_assert (Same, optional, value>>>>>, - decltype (schema::map::key ("A") && *(schema::map::key ("B") && schema::map::key ("C")))>); - - static_assert (Same< - map< - only< - all< - value, - optional< - all, value> - >, - optional> - > - > - >, - decltype (schema::map::key ("A") && *(schema::map::key ("B") && schema::map::key ("C")) && *schema::map::key ("D"))>); - - // this should mean that the map has a C and either and A or a B and nothing else. - static_assert (Same ("A") || schema::map::key ("B")) && schema::map::key ("C")), - map, value>, value>>>>); - - static_assert (Same ("A")), map>>>); - - //static_assert (Same ("A")), map>>>); - -} - -namespace data::schema::map { - - TEST (Schema, Valid) { - - EXPECT_TRUE (valid (empty ())); - EXPECT_TRUE (valid (key ("A"))); - EXPECT_FALSE (valid (key ("A") && key ("A"))); - EXPECT_TRUE (valid (key ("A") && key ("B"))); - EXPECT_FALSE (valid (key ("A") && (key ("B") || key ("A")))); - EXPECT_TRUE (valid (key ("A") && key ("B") || key ("A") && key ("C"))); - - } - - template void test_map_validate () { - - map map_empty {}; - map map_A {{"A", "23"}}; - map map_B {{"B", "567"}}; - map map_AB {{"A", "23"}, {"B", "567"}}; - map map_ABC {{"A", "23"}, {"B", "567"}, {"C", "1293554"}}; - - EXPECT_NO_THROW (validate<> (map_empty, empty ())); - EXPECT_THROW ((validate<> (map_A, empty ())), unknown_key); - EXPECT_THROW ((validate<> (map_B, empty ())), unknown_key); - EXPECT_THROW ((validate<> (map_AB, empty ())), unknown_key); - - auto blank = +empty (); - - EXPECT_NO_THROW ((validate<> (map_empty, blank))); - EXPECT_NO_THROW ((validate<> (map_A, blank))); - EXPECT_NO_THROW ((validate<> (map_B, blank))); - EXPECT_NO_THROW ((validate<> (map_AB, blank))); - - // this matches any map with A in it. - auto blank_key_A = rule::map> ("A"); - - EXPECT_THROW ((validate<> (map_empty, blank_key_A)), missing_key); - ASSERT_NO_THROW (EXPECT_EQ (23, (validate<> (map_A, blank_key_A)))); - EXPECT_THROW ((validate<> (map_B, blank_key_A)), missing_key); - EXPECT_NO_THROW (EXPECT_EQ (23, (validate<> (map_AB, blank_key_A)))); - - auto blank_key_B = rule::map> ("B"); - - EXPECT_THROW ((validate<> (map_empty, blank_key_B)), missing_key); - EXPECT_THROW ((validate<> (map_A, blank_key_B)), missing_key); - ASSERT_NO_THROW (EXPECT_EQ (567, (validate<> (map_B, blank_key_B)))); - EXPECT_NO_THROW (EXPECT_EQ (567, (validate<> (map_AB, blank_key_B)))); - - // match any map with exactly an A in it. - auto one_key_A = -rule::map> ("A"); - - EXPECT_THROW ((validate<> (map_empty, one_key_A)), missing_key); - ASSERT_NO_THROW (EXPECT_EQ (23, (validate<> (map_A, one_key_A)))); - EXPECT_THROW ((validate<> (map_B, one_key_A)), unknown_key); - EXPECT_THROW ((validate<> (map_AB, one_key_A)), unknown_key); - auto key_A_and_B = blank_key_A && blank_key_B; - EXPECT_THROW ((validate<> (map_empty, key_A_and_B)), missing_key); - EXPECT_THROW ((validate<> (map_A, key_A_and_B)), missing_key); - EXPECT_THROW ((validate<> (map_B, key_A_and_B)), missing_key); - EXPECT_NO_THROW ((validate<> (map_AB, key_A_and_B))); - EXPECT_NO_THROW ((validate<> (map_ABC, key_A_and_B))); - EXPECT_THROW ((validate<> (map_ABC, -key_A_and_B)), unknown_key); - - // this one matches empty or just A. - auto optional_key_A = *key ("A"); - EXPECT_NO_THROW (EXPECT_EQ (maybe {}, (validate<> (map_empty, optional_key_A)))); - EXPECT_NO_THROW (EXPECT_EQ (maybe {23}, (validate<> (map_A, optional_key_A)))); - EXPECT_THROW ((validate<> (map_B, optional_key_A)), unknown_key); - EXPECT_THROW ((validate<> (map_AB, optional_key_A)), unknown_key); - - // this will match any map but will only return a - // value of type uint32 if key A was present. - rule::map>> optional_blank_key_A {blank_key_A}; - EXPECT_NO_THROW (EXPECT_EQ (maybe {}, (validate<> (map_empty, optional_blank_key_A)))); - EXPECT_NO_THROW (EXPECT_EQ (maybe {23}, (validate<> (map_A, optional_blank_key_A)))); - EXPECT_NO_THROW (EXPECT_EQ (maybe {}, (validate<> (map_B, optional_blank_key_A)))); - EXPECT_NO_THROW (EXPECT_EQ (maybe {23}, (validate<> (map_AB, optional_blank_key_A)))); - - // match a map with A or a map with B. - auto key_A_or_key_B = -blank_key_A || -blank_key_B; - ASSERT_THROW ((validate<> (map_empty, key_A_or_key_B)), missing_key); - ASSERT_NO_THROW ((validate<> (map_A, key_A_or_key_B))); - EXPECT_NO_THROW ((validate<> (map_B, key_A_or_key_B))); - EXPECT_THROW ((validate<> (map_AB, key_A_or_key_B)), incomplete_match); - - auto key_A_opt_and_B = optional_key_A && blank_key_B; - EXPECT_THROW ((validate<> (map_empty, key_A_opt_and_B)), missing_key); - EXPECT_THROW ((validate<> (map_A, key_A_opt_and_B)), missing_key); - EXPECT_NO_THROW ((validate<> (map_B, key_A_opt_and_B))); - EXPECT_NO_THROW ((validate<> (map_AB, key_A_opt_and_B))); - - auto one_key_B = key ("B"); - auto optional_key_B = *one_key_B; - - auto blank_key_C = rule::map> ("C"); - auto one_key_C = key ("C"); - auto optional_key_C = *one_key_C; - - auto key_A_opt_and_key_B_opt = optional_key_A && optional_key_B; - EXPECT_NO_THROW ((validate<> (map_empty, key_A_opt_and_key_B_opt))); - ASSERT_NO_THROW ((validate<> (map_A, key_A_opt_and_key_B_opt))); - EXPECT_NO_THROW ((validate<> (map_B, key_A_opt_and_key_B_opt))); - EXPECT_NO_THROW ((validate<> (map_AB, key_A_opt_and_key_B_opt))); - - // this ought to match a map with one key B, an empty map, or one with key A. - auto key_A_opt_or_key_B = optional_key_A || one_key_B; - EXPECT_NO_THROW ((validate<> (map_empty, key_A_opt_or_key_B))); - EXPECT_NO_THROW ((validate<> (map_A, key_A_opt_or_key_B))); - ASSERT_NO_THROW ((validate<> (map_B, key_A_opt_or_key_B))); - ASSERT_THROW ((validate<> (map_AB, key_A_opt_or_key_B)), mismatch); - - map map_AC {{"A", "23"}, {"C", "1293554"}}; - map map_BC {{"B", "567"}, {"C", "1293554"}}; - - // basically, you either need to distribute any & operations - // over | or you need to put the | at the end or it won't work. - auto test_proof_of_design_issue = (one_key_A || one_key_B) && one_key_C; - auto test_resolution_of_issue = one_key_C && (one_key_A || one_key_B); - - EXPECT_THROW ((validate<> (map_empty, test_proof_of_design_issue)), missing_key); - EXPECT_THROW ((validate<> (map_A, test_proof_of_design_issue)), missing_key); - EXPECT_THROW ((validate<> (map_B, test_proof_of_design_issue)), missing_key); - ASSERT_THROW ((validate<> (map_AB, test_proof_of_design_issue)), incomplete_match); - EXPECT_NO_THROW ((validate<> (map_AC, test_proof_of_design_issue))); - EXPECT_NO_THROW ((validate<> (map_BC, test_proof_of_design_issue))); - - EXPECT_THROW ((validate<> (map_empty, test_resolution_of_issue)), missing_key); - EXPECT_THROW ((validate<> (map_A, test_resolution_of_issue)), missing_key); - EXPECT_THROW ((validate<> (map_B, test_resolution_of_issue)), missing_key); - EXPECT_THROW ((validate<> (map_AB, test_resolution_of_issue)), missing_key); - EXPECT_NO_THROW ((validate<> (map_AC, test_resolution_of_issue))); - EXPECT_NO_THROW ((validate<> (map_BC, test_resolution_of_issue))); - - // must have all three. - auto and_A_B_C = one_key_A && one_key_B && one_key_C; - ASSERT_THROW ((validate<> (map_empty, and_A_B_C)), missing_key); - EXPECT_THROW ((validate<> (map_A, and_A_B_C)), missing_key); - EXPECT_THROW ((validate<> (map_AB, and_A_B_C)), missing_key); - EXPECT_NO_THROW ((validate<> (map_ABC, and_A_B_C))); - - // must have A and B and may have C. - auto and_A_B_C_opt = one_key_A && one_key_B && optional_key_C; - ASSERT_THROW ((validate<> (map_empty, and_A_B_C)), missing_key); - EXPECT_THROW ((validate<> (map_A, and_A_B_C)), missing_key); - EXPECT_NO_THROW ((validate<> (map_AB, and_A_B_C_opt))); - EXPECT_NO_THROW ((validate<> (map_ABC, and_A_B_C_opt))); - - // any map with A or a map with only B. - auto key_A_blank_or_key_B = blank_key_A || one_key_B; - ASSERT_THROW ((validate<> (map_empty, key_A_blank_or_key_B)), missing_key); - EXPECT_NO_THROW ((validate<> (map_A, key_A_blank_or_key_B))); - EXPECT_NO_THROW ((validate<> (map_B, key_A_blank_or_key_B))); - EXPECT_NO_THROW ((validate<> (map_AB, key_A_blank_or_key_B))); - ASSERT_NO_THROW ((validate<> (map_AC, key_A_blank_or_key_B))); - - auto opt_A_or_B = *(one_key_A || one_key_B); - ASSERT_NO_THROW ((validate<> (map_empty, opt_A_or_B))); - ASSERT_NO_THROW ((validate<> (map_A, opt_A_or_B))); - ASSERT_NO_THROW ((validate<> (map_B, opt_A_or_B))); - EXPECT_THROW ((validate<> (map_AB, opt_A_or_B)), incomplete_match); - - // must have key A and may optionally have B and C. - auto and_A_opt_B_C = one_key_A && *(one_key_B && one_key_C); - ASSERT_NO_THROW ((validate<> (map_A, and_A_opt_B_C))); - EXPECT_NO_THROW (validate<> (map_A, and_A_opt_B_C)); - EXPECT_NO_THROW ((validate<> (map_ABC, and_A_opt_B_C))); - EXPECT_THROW ((validate<> (map_AB, and_A_opt_B_C)), mismatch); - - auto or_A_B_C = one_key_A || one_key_B || one_key_C; - ASSERT_THROW ((validate<> (map_empty, or_A_B_C)), missing_key); - ASSERT_NO_THROW ((validate<> (map_A, or_A_B_C))); - ASSERT_NO_THROW ((validate<> (map_B, or_A_B_C))); - - auto por_ABC = +or_A_B_C; - - } - - TEST (Schema, ValidateMap) { - - try { - test_map_validate> (); - test_map_validate> (); - test_map_validate> (); - test_map_validate> (); - test_map_validate> (); - test_map_validate> (); - } catch (unknown_key err) { - FAIL () << "Fail with unknown key " << err.Key; - } catch (missing_key err) { - FAIL () << "Fail with missing key " << err.Key; - } catch (invalid_entry err) { - FAIL () << "Fail with invalid value " << err.Key; - } catch (incomplete_match err) { - FAIL () << "Fail with incomplete match " << err.Key; - } - - } - - TEST (Schema, Default) { - EXPECT_EQ ((validate<> (data::map {}, key ("zoob", 13))), 13); - EXPECT_EQ ((validate<> (data::map {{"zoob", "92"}}, key ("zoob", 13))), 92); - } - - TEST (Schema, Optional) { - - data::map test_map_A {{"na", "Y"}, {"ne", "M"}, {"ty", "W"}}; - data::map test_map_B {{"na", "Y"}}; - - auto test_schema = key ("na") && - *(key ("ty") && - *key ("ne") && - *key ("co")); - - EXPECT_NO_THROW ((validate<> (test_map_A, test_schema))); - EXPECT_NO_THROW ((validate<> (test_map_B, test_schema))); - - } - - template void test_or () { - - auto test_A = key ("a") && key ("b"); - - // Here we test that unknown key gets thrown first out of other possible errors. - EXPECT_THROW ((validate<> (map {{"a", "A"}, {"d", "D"}}, test_A)), unknown_key); - - // We want to use this to test that both keys can be present when we don't use only. - auto test_B = +(key ("a") || key ("b")); - - EXPECT_NO_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}, {"c", "C"}}, test_B))); - - auto test_C = key ("a") || key ("b"); - - EXPECT_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}, {"c", "C"}}, test_C)), unknown_key); - EXPECT_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}, {"c", "C"}}, test_C)), mismatch); - - auto test_D = key ("a") && (key ("b") || key ("c")) - || key ("d") && (key ("e") || key ("f")); - - EXPECT_NO_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}}, test_D))); - EXPECT_NO_THROW ((validate<> (map {{"a", "A"}, {"c", "C"}}, test_D))); - EXPECT_NO_THROW ((validate<> (map {{"d", "D"}, {"e", "E"}}, test_D))); - EXPECT_NO_THROW ((validate<> (map {{"d", "D"}, {"f", "F"}}, test_D))); - - EXPECT_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}, {"d", "D"}, {"g", "G"}}, test_D)), unknown_key); - - EXPECT_THROW ((validate<> (map {{"a", "A"}, {"b", "B"}, {"d", "D"}}, test_D)), mismatch); - } - - TEST (Schema, Or) { - - try { - test_or> (); - test_or> (); - test_or> (); - } catch (const mismatch &) { - FAIL () << ""; - } - - } - - // this test has to do with a specific application that didn't work. - TEST (Schema, Endpoint) { - - auto endpoint_schema = schema::map::key ("domain") || - schema::map::key ("endpoint") || - (schema::map::key ("ip_address") && *schema::map::key ("port")); - - data::map empty_input {}; - - data::map ip_port_input { - {"ip_address", "123.23.3.2"}, - {"port", "4567"} - }; - - data::map ip_input { - {"ip_address", "123.23.3.2"} - }; - - data::map endpoint_input { - {"endpoint", "123.23.3.2:4567"} - }; - - data::map domain_input { - {"domain", "zoob.com"} - }; - - data::map authority_input { - {"authority", "localhost:8888"} - }; - - try { - EXPECT_THROW ((validate (empty_input, endpoint_schema)), missing_key); - EXPECT_NO_THROW (validate (ip_port_input, endpoint_schema)); - EXPECT_NO_THROW (validate (ip_input, endpoint_schema)); - EXPECT_NO_THROW (validate (endpoint_input, endpoint_schema)); - EXPECT_NO_THROW (validate (domain_input, endpoint_schema)); - } catch (mismatch) { - FAIL () << "mismatch caught"; - } - - auto optional_endpoint_schema = *endpoint_schema; - - try { - EXPECT_NO_THROW ((validate (empty_input, optional_endpoint_schema))); - EXPECT_NO_THROW (validate (ip_port_input, optional_endpoint_schema)); - EXPECT_NO_THROW (validate (ip_input, optional_endpoint_schema)); - EXPECT_NO_THROW (validate (endpoint_input, optional_endpoint_schema)); - EXPECT_NO_THROW (validate (domain_input, optional_endpoint_schema)); - } catch (mismatch) { - FAIL () << "mismatch caught"; - } - - auto alternate_schema = schema::map::key ("endpoint") || - schema::map::key ("port", 8000) && ( - schema::map::key ("ip_address") || - schema::map::key ("authority") || - schema::map::key ("domain", "localhost")); - - try { - validate (empty_input, alternate_schema); - validate (endpoint_input, alternate_schema); - validate (ip_port_input, alternate_schema); - validate (authority_input, alternate_schema); - validate (ip_input, alternate_schema); - validate (domain_input, alternate_schema); - } catch (incomplete_match m) { - FAIL () << "caught incomplete match at " << m.Key; - } catch (mismatch m) { - FAIL () << "caught mismatch"; - } - } - - TEST (Schema, Read) { - - data::map test_map { - {"A", "23"}, - {"B", "129"}, - {"C", "-3"}, - {"D", "1255566"}, - {"E", "true"}, - {"F", "\"true\""}, - {"G", "string with whitespace"} - }; - - try { - - EXPECT_EQ ((validate<> (test_map, +key ("A"))), "23"); - EXPECT_EQ ((validate<> (test_map, +key ("B"))), "129"); - EXPECT_EQ ((validate<> (test_map, +key ("C"))), "-3"); - EXPECT_EQ ((validate<> (test_map, +key ("F"))), R"("true")"); - EXPECT_EQ ((validate<> (test_map, +key ("G"))), "string with whitespace"); - - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_EQ ((validate<> (test_map, +key ("F"))), "true"); - - EXPECT_EQ (true, (validate<> (test_map, +key ("E")))); - EXPECT_THROW ((validate<> (test_map, +key ("B"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - - EXPECT_NO_THROW (validate<> (test_map, +key ("A"))); - EXPECT_NO_THROW (validate<> (test_map, +key ("E"))); - EXPECT_NO_THROW (validate<> (test_map, +key ("F"))); - - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - EXPECT_EQ (23, (validate<> (test_map, +key ("A")))); - - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_THROW ((validate<> (test_map, +key ("B"))), invalid_entry); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - EXPECT_EQ (129, (validate<> (test_map, +key ("B")))); - - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - ASSERT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - EXPECT_THROW ((validate<> (test_map, +key ("C"))), invalid_entry); - EXPECT_EQ (-3, (validate<> (test_map, +key ("C")))); - - EXPECT_THROW ((validate<> (test_map, +key ("D"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("D"))), invalid_entry); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - EXPECT_EQ (1255566, (validate<> (test_map, +key ("D")))); - - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - EXPECT_THROW ((validate<> (test_map, +key ("E"))), invalid_entry); - } catch (mismatch) { - FAIL () << "mismatch caught"; - } - - } -} - -namespace data::schema::list { - - static_assert (requires { - value (1); - value (1) + value (2); - value () + value (1); - equal (1) + value (2); - }); - - template void test_list_validate () { - - list empty_list {}; - list list_1 {"1"}; - - EXPECT_NO_THROW (validate<> (empty_list, empty ())); - - EXPECT_THROW ((validate<> (list_1, empty ())), end_of_sequence); - - EXPECT_NO_THROW (validate<> (list_1, value ())); - EXPECT_NO_THROW (validate<> (list_1, equal (1))); - EXPECT_THROW (validate<> (list_1, equal (2)), invalid_value_at); - - EXPECT_NO_THROW (validate<> (empty_list, value (3))); - EXPECT_NO_THROW (validate<> (empty_list, *value ())); - EXPECT_NO_THROW (validate<> (list_1, value (3))); - - EXPECT_THROW (validate<> (list_1, value (data::string {"huuub"})), invalid_value_at); - - list list_2 {"1", "2"}; - - EXPECT_THROW ((validate<> (list_2, value ())), no_end_of_sequence); - EXPECT_THROW ((validate<> (list_2, value (1))), no_end_of_sequence); - EXPECT_THROW ((validate<> (list_2, equal (1))), no_end_of_sequence); - - EXPECT_THROW ((validate<> (list_1, value () + value ())), end_of_sequence); - EXPECT_THROW ((validate<> (list_1, value () + equal (2))), end_of_sequence); - - EXPECT_NO_THROW ((validate<> (list_1, value () + *value ()))); - EXPECT_NO_THROW ((validate<> (list_1, value () + value (2)))); - - EXPECT_NO_THROW ((validate<> (list_2, value () + value ()))); - EXPECT_NO_THROW ((validate<> (list_2, value () + equal (2)))); - EXPECT_NO_THROW ((validate<> (list_2, value () + *value ()))); - - EXPECT_NO_THROW ((validate<> (empty_list, value (3) + value (4)))); - EXPECT_NO_THROW ((validate<> (empty_list, *value () + *value ()))); - EXPECT_NO_THROW ((validate<> (empty_list, value (2) + *value ()))); - - // need tests with blank. - - } - - TEST (Schema, ValidateList) { - - try { - - test_list_validate> (); - test_list_validate> (); - test_list_validate> (); - - } catch (invalid_value_at err) { - FAIL () << "Fail with invalid value at " << err.Position; - } catch (end_of_sequence err) { - FAIL () << "Fail with premature end of sequence " << err.Position; - } catch (no_end_of_sequence err) { - FAIL () << "Fail with no end of sequence " << err.Position; - } catch (mismatch &) { - FAIL () << "unexpected mismatch caught..."; - } catch (std::exception &e) { - FAIL () << "exception caught with msg " << e.what (); - } - - } -} diff --git a/test/select.cpp b/test/select.cpp index 9d05a17a..6b3957d5 100644 --- a/test/select.cpp +++ b/test/select.cpp @@ -10,6 +10,7 @@ #include "data/set.hpp" #include "data/list.hpp" #include "data/select.hpp" + #include "gtest/gtest.h" namespace data { diff --git a/test/store.cpp b/test/store.cpp index 69f40581..55ab9fd7 100644 --- a/test/store.cpp +++ b/test/store.cpp @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/tools/store.hpp" -#include "gtest/gtest.h" +#include +#include namespace data { diff --git a/test/stream.cpp b/test/stream.cpp index ef5e46b3..c49003db 100644 --- a/test/stream.cpp +++ b/test/stream.cpp @@ -2,11 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/encoding/endian.hpp" -#include "data/bytes.hpp" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "gmock/gmock-matchers.h" +#include +#include + +#include +#include +#include namespace { using namespace data; diff --git a/test/take.cpp b/test/take.cpp index ffddd122..db1634f0 100644 --- a/test/take.cpp +++ b/test/take.cpp @@ -2,11 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/stack.hpp" -#include "data/list.hpp" -#include "data/cross.hpp" -#include "data/ordered_sequence.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include + +#include namespace data { template using vector = std::vector; diff --git a/test/transpose.cpp b/test/transpose.cpp index c4f1c498..8ec3c4d2 100644 --- a/test/transpose.cpp +++ b/test/transpose.cpp @@ -2,8 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/transpose.hpp" -#include "gtest/gtest.h" +#include + +#include namespace data { diff --git a/test/tree.cpp b/test/tree.cpp index e8bba668..2e344f60 100644 --- a/test/tree.cpp +++ b/test/tree.cpp @@ -7,7 +7,8 @@ #include #include #include -#include "gtest/gtest.h" + +#include namespace data { diff --git a/test/write.cpp b/test/write.cpp index 192aa460..07ac3e7c 100644 --- a/test/write.cpp +++ b/test/write.cpp @@ -2,13 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/bytes.hpp" -#include "data/array.hpp" -#include "data/numbers.hpp" -#include "data/crypto/hash.hpp" -#include "data/crypto/MAC.hpp" -#include "data/crypto/MAC/HMAC.hpp" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include + +#include namespace data { @@ -98,8 +99,8 @@ namespace data { EXPECT_THROW ((write> (byte (0x76))), exception); EXPECT_THROW ((write> (byte (0x76))), exception); - } - + }*/ +/* TEST (Write, NBytes) { // write a list of bytes @@ -123,8 +124,8 @@ namespace data { // test that an exception gets thrown if we write too much or too little EXPECT_THROW ((write (0, byte (0x76))), exception); EXPECT_THROW ((write (2, byte (0x76))), exception); - } - + }*/ +/* TEST (Write, UInt) { EXPECT_EQ (write> (), uint_big<0> {});