diff --git a/.clang-tidy b/.clang-tidy index 6aaac0675bc92c0ea6f5d5e69653baacd4c8e9eb..f3693c27991647463d33f013e4a88ba354faca7d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -6,7 +6,6 @@ Checks: clang-diagnostic-*, -clang-analyzer-security.insecureAPI.rand, mpi-*, readability-braces-around-statements, - readability-avoid-const-params-in-decls, readability-redundant-string-init, readability-container-size-empty, readability-implicit-bool-conversion, @@ -16,7 +15,7 @@ WarningsAsErrors: '*' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: none -User: ExSeisDat +User: Deimos CheckOptions: - key: google-readability-braces-around-statements.ShortStatementLines value: '1' @@ -74,6 +73,6 @@ CheckOptions: - key: readability-identifier-naming.MacroDefinitionCase value: UPPER_CASE - key: readability-identifier-naming.MacroDefinitionPrefix - value: EXSEISDAT_ + value: DEIMOS_ ... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0a2f2bd0d3505475262962d6dde83ba18575d7f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +todo.txt +.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..0c34c0701987287a7de19b70273ec8e0a1e535b0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,132 @@ +image: ciaranorourke/fiphoboserver:debian + +.snippets: + - &start_phobos + /home/superfiphoboserver/tools/build_phobos.sh -r + - &configure + mkdir build + && pushd build + && ( + set -x; + cmake + -DCMAKE_VERBOSE_MAKEFILE=ON + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + -DCMAKE_C_COMPILER=${CC} + -DCMAKE_CXX_COMPILER=${CXX} + -DDEIMOS_BUILD_TESTS="${DEIMOS_BUILD_TESTS}" + -DCMAKE_PREFIX_PATH="${DEPS_DIR}" + -DCUSTOM_DOCU_PATH="${DEIMOS_DOC_DIR}" + -DDEIMOS_BUILD_DOCUMENTATION="${DEIMOS_BUILD_DOCUMENTATION}" + -DDEIMOS_AUTHORISATION="${DEIMOS_AUTHORISATION}" + .. + ) + && popd + - &build + make -C build + - &unit_test + make -C build test ARGS=-V + && pushd build/test/unit_tests + && ../tests + && popd + - &integration_test + pushd build/test/integration_tests + && python3 -m unittest client + && popd + +.cmake_variables: + default_cmake_variables: &default_cmake_variables + CC: gcc + CXX: g++ + DEPS_DIR: /home/superfiphoboserver/build/install + DEIMOS_DOC_DIR: "" + DEIMOS_BUILD_TESTS: "ON" + DEIMOS_AUTHORISATION: "ON" + +.default_job: &default_job + tags: + - docker + variables: + <<: *default_cmake_variables + before_script: + - apt-get update + - apt-get install -y + cmake + clang clang-tidy + libspdlog-dev + script: + - *start_phobos + - *configure + - *build + - *unit_test + - *integration_test + +stages: + - static analysis + - build + - test + +Format: + <<: *default_job + stage: static analysis + variables: + CC: clang + CXX: clang++ + before_script: + - apt-get update + - apt-get install -y + git + clang-format + - pip3 install yapf + script: + - ./tools/run_format.sh + && git status + && git diff-index --quiet HEAD + || { echo "This commit contains unformatted files! Run tools/run_format.sh on the project to format them correctly."; false; } + +Lint: + <<: *default_job + stage: static analysis + variables: + <<: *default_cmake_variables + CC: clang + CXX: clang++ + script: + - *configure + - *build + - ./tools/run_lint.sh build + +Documentation: + <<: *default_job + stage: build + variables: + <<: *default_cmake_variables + DEIMOS_BUILD_DOCUMENTATION: "ON" + DEIMOS_DOC_DIR: /home/superfiphoboserver/external/doxygen-xml-parser/src + CC: clang + CXX: clang++ + before_script: + - apt-get update + - apt-get install -y + cmake + doxygen + graphviz + clang + libspdlog-dev + script: + - *configure + - make -C build/doc + && git status + && git diff-index --quiet HEAD + || { echo "This commit contains changes to the documentation! Run doxygen-XML-parser on the project update the documentation."; false; } + + +Default: + <<: *default_job + stage: test + +Authorisation Disabled: + <<: *default_job + variables: + <<: *default_cmake_variables + DEIMOS_AUTHORISATION: "OFF" + stage: test diff --git a/CMakeLists.txt b/CMakeLists.txt index 39e0cb6b9af9a4cd9ba07636acb377d75a7ddf32..2cbc3e3bab985c9a58251be53a6386aeeb15afa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,128 @@ -project(fiphoboserver) +cmake_minimum_required(VERSION 3.0) + +project( + deimos + VERSION 0.0.1 + LANGUAGES C CXX +) + +# +# Set subdirectory options +# + +option( + DEIMOS_BUILD_TESTS + "build Deimos tests. Depends on Catch2." + OFF +) + +option( + DEIMOS_BUILD_DOCUMENTATION + "build Deimos Documentation. Depends on Doxygen." + OFF +) + + +# +# Setup installation path options. +# + +# Initialy set paths to GNU defaults. +# Users can specify directories using the various DEIMOS_INSTALL_XXX +# variables. +# eg. setting the include install dir +# cmake -DDEIMOS_INSTALL_INCLUDE_DIR=/path/to/include /path/to/deimos + +# Get some sane default install directories +include(GNUInstallDirs) + +set( + DEIMOS_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR} CACHE STRING + "The install directory for Deimos executables." +) + +set( + DEIMOS_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING + "The install directory for Deimos libraries." +) + +set( + DEIMOS_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING + "The install directory for Deimos headers." +) + +set( + DEIMOS_INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR} CACHE STRING + "The install directory for Deimos data (eg. cmake scripts)." +) + + +# +# Set build type options +# + +option( + DEIMOS_AUTHORISATION + "build Deimos with AWS authorisation enabled. Default is enabled" + ON +) + + +# +# Find needed libraries +# + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + +# set(Boost_DEBUG 1) + +find_package(proxygen REQUIRED) +find_package(phobos REQUIRED) +find_package(spdlog REQUIRED) + + +# +# Include optional subdirectories +# + +include_directories( + SYSTEM PUBLIC ${SPDLOG_INCLUDE_DIR} +) + +# +# Setup the Deimos library +# add_subdirectory(src) + + +# Output project build options +message(STATUS "Build options:") +message(STATUS " DEIMOS_AUTHORISATION = ${DEIMOS_AUTHORISATION}") +message(STATUS " DEIMOS_BUILD_DOCUMENTATION = ${DEIMOS_BUILD_DOCUMENTATION}") +message(STATUS " DEIMOS_BUILD_TESTS = ${DEIMOS_BUILD_TESTS}") + +# Build documentation +if(DEIMOS_BUILD_DOCUMENTATION) + add_subdirectory(doc) +endif(DEIMOS_BUILD_DOCUMENTATION) + +# Build tests +if(DEIMOS_BUILD_TESTS) + add_subdirectory(test) + enable_testing() +endif(DEIMOS_BUILD_TESTS) + + +# +# Set up installation directories +# + +install( + TARGETS deimos + EXPORT deimos-targets + RUNTIME DESTINATION ${DEIMOS_INSTALL_BINDIR} + LIBRARY DESTINATION ${DEIMOS_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${DEIMOS_INSTALL_LIBDIR} + INCLUDES DESTINATION ${DEIMOS_INSTALL_INCLUDEDIR} +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f204b9671f4154a6f6a2675438a7bab14ceff340 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Irish Centre for High End Computing (ICHEC), NUI Galway + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b4322f9d48ac95e37863fc3d29d84fd004e84cef..d39e905042d78b6e8038a3309bbd83548785a5f7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,136 @@ -# FIPhoboServer +# Deimos -Build with +Deimos (Delivery Endpoint Interface for Managing Object Storage) is a webserver used to archive data to a [Phobos](https://github.com/cea-hpc/phobos) managed object storage. +Deimos is implemented by using a large subset of the Amazon S3 REST API interface and therefore compatible with most software that uses S3 as an endpoint. + +Most importantly, it can be used as an endpoint for Lustre HSM archiving solutions using for example [Estuary](https://git.ichec.ie/oilgas/ltfs/estuary). + +## Dependencies Installation Instructions + +Deimos has dependencies that require a little patience to build. It may be simpler to invoke a prebuilt Docker image using a Dockerfile such as; + +``` +FROM ciaranorourke/deimos:centos + +RUN apt-get -y update && apt-get -y upgrade +RUN apt-get -y install \ + [personal installation preferences] +``` + +Alternatively, see [SuperDeimos](https://git.ichec.ie/oilgas/ltfs/superdeimos) for instructions on how to create your own build environment. + +It is recommended to run the Docker image mentioned above by mounting your local branch of the Deimos project with `docker run -it -v /path/to/local/deimos:/home/superdeimos/external/deimos [name]:[tag]` + +To use the Phobos raid1 layout which is compatible with FIFOs, the following environment variables must be set; + +``` +export PHOBOS_STORE_layout=raid1 +export PHOBOS_LAYOUT_RAID1_repl_count={1,2,3,...} +``` + +## Build Instructions + +To configure and build Deimos, a C++14 compatible compiler is required, and CMake >= 3.0. + +``` +# Make a directory to hold the temporary build files +mkdir -p build + +# Change directory to the build directory +cd build + +# Configure the Deimos build using CMake +cmake \ + -DCMAKE_PREFIX_PATH=/path/to/proxygen \ + [more options below] \ + /path/to/deimos/project + +cd /path/to/deimos/project + +# Build the Deimos project +make -C build + +# Install the Deimos executable +make -C build install ``` -cmake -DDEBUG=ON -DCMAKE_PREFIX_PATH=/path/to/proxygen /path/to/CMakeLists/file/ -make +A build script executing these commands and also building the dependencies can be found in the project [SuperDeimos](https://git.ichec.ie/oilgas/ltfs/superdeimos). + +### Useful Options + +#### Setting Compile/Link Flags +Option | Effect +------ | ------ +`-DCMAKE_CXX_COMPILER=...` | Set the C++ compiler. +`-DCMAKE_CXX_FLAGS=...` | Set the flags to pass to the C++ compiler. Overrides the default flags. +`-DCMAKE_BUILD_TYPE=...` | Set to `Release` or `Debug` for release or debug builds. Sets a number of flags by default. + +#### Enabling/Disabling Sections of Deimos +Option | Effect +------ | ------ +`-DDEIMOS_BUILD_TESTS=...` | Set to `ON` to build Deimos tests and enable the `make test` target, or `OFF` to skip (Default `OFF`). +`-DDEIMOS_AUTHORISATION=...` | Set to `OFF` to disable AWS V4 authorisation (= letting everything through), or `ON` to authorise requests (Default `ON`). +`-DDEIMOS_BUILD_DOCUMENTATION=...` | Set to `ON` to build Deimos Doxygen documentation, or `OFF` to skip (Default `OFF`). +`-DCUSTOM_DOCU_PATH=...` | Set to the path to the `src` folder in [DoxygenXMLParser](https://git.ichec.ie/sophie.wenzel-teuber/doxygen-xml-parser) to create the Markdown output from the Doxygen XML, that can also be found [here](doc/Markdown/deimos) or don't set to skip (Default `not set`). + +#### Setting installation directories +Option | Effect +------ | ------ +`-DDEIMOS_INSTALL_PREFIX=...` | Set to the root install directory for the compiled libraries and programs. +`-DDEIMOS_INSTALL_BINDIR=...` | Set the install directory for the `deimos` executable. Use a relative path to set the path relative to `${CMAKE_INSTALL_PREFIX}`. (Default `bin`). +`-DDEIMOS_INSTALL_LIBDIR=...` | Set the install directory for `deimos` libraries. Use a relative path to set the path relative to `${CMAKE_INSTALL_PREFIX}`. (Default `lib`). + +## Testing + +Units test can be run with `make -C build tests`, given `DEIMOS_BUILD_TESTS` was set to `ON` during the build process. +Integration tests can be run from the `test/integration_tests/` directory with `python -m unittest client`. + +## Documentation + +The Doxygen documentation can be found [here](doc/Markdown/deimos). + +## Usage + +The `deimos` executable requires exactly one subcommand. + +#### Subcommands +Subcommand | Effect +---------- | ------ +`start [hostname]` | Start the server daemon, listening to `hostname`. +`stop` | Stop the server daemon. + +The `start` subcommand supports a number of optional flags and options. + +#### Flags/Options +Flag/Option | Effect +----------- | ------ +`--http INT` | Set the port to listen on with HTTP protocol (Default `11000`). +`--http2 INT` | Set the port to listen on with HTTP2 protocol (Default `11002`). +`-t,--threads INT` | Set the number of threads to listen on, <= 0 will use number of available cores (Default `0`). +`-w,--write-config TEXT` | Write the configuration to `TEXT`. +`-l,--logfile` | Set the filename to write logs to (Default `/tmp/deimos_log.txt"). + +Options can also be given to the `start` subcommand using `--config [config_filename]`. Note that if `hostname` is specified in the configuration file then the `--config` options must be suppied before the `start` subcommand; +``` +deimos --config [config_filename] start +``` + +### Authentication +The server uses the [Amazon S3 Authentication](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) to ensure the requests that are reaching it are from legitimate users. + +Since we do not have a proper database for users yet, they are currently stored along with their keys in [users.txt](test/users.txt). This file is copied into the `/tmp` folder by CMake and then read from there for authentication. + +Since the authentication is checked for every request, you need to use one of the users and keys from that file for testing the server! + +Either you set the correct credentials using the SDK directly or let the SDK find them automatically using the file `~/.aws/credentials` similar to: + +``` +[default] +aws_access_key_id = OPEN_KEY +aws_secret_access_key = SECRET_KEY ``` -from any directory you want and hope for the best! ;) -Oh, make sure Phobos include directories are under `../phobos/src/include` from the main `CMakeLists.txt`, or change that path in there +--- +Copyright 2020 Irish Centre for High End Computing (ICHEC), NUI Galway. \ No newline at end of file diff --git a/cmake/Findphobos.cmake b/cmake/Findphobos.cmake new file mode 100644 index 0000000000000000000000000000000000000000..5f1ede791e6aaea66da6df4ef9296a75f946fffd --- /dev/null +++ b/cmake/Findphobos.cmake @@ -0,0 +1,89 @@ +include(FindPackageHandleStandardArgs) + +# message("Looking for Phobos") + +if(NOT PHOBOS_FOUND) + find_path( PHOBOS_INCLUDE_DIR + HINTS ./ + ../phobos/include/ + ../include/phobos/ + ../../phobos/include/ + ../../include/phobos/ + ../../../phobos/include/ + ../../../include/phobos/ + /usr/include/ + /usr/include/phobos/ + /usr/local/include/ + /usr/local/include/phobos/ + ${CMAKE_PREFIX_PATH}/include/phobos/ + ${CMAKE_PREFIX_PATH}/phobos/include/ + ${CMAKE_INSTALL_PREFIX}/../include/phobos/ + ${CMAKE_INSTALL_PREFIX}/../phobos/include/ + ${CMAKE_INSTALL_PREFIX}/include/phobos/ + ${CMAKE_INSTALL_PREFIX}/phobos/include/ + NAMES phobos_store.h pho_attrs.h + DOC "Phobos headers" + ) + + find_path( GLIB_INCLUDE_DIR + HINTS ./ + ../ + ../glib-2.0/ + ../include/glib-2.0/ + ../../ + ../../glib-2.0/ + ../../include/glib-2.0/ + /usr/include/ + /usr/include/glib-2.0/ + NAMES glib.h + DOC "Glib header" + ) + + find_path( GLIB_CONFIG_INCLUDE_DIR + HINTS ./ + ../ + ../glib-2.0/ + ../lib64/glib-2.0/ + ../../ + ../../glib-2.0/ + ../../lib64/glib-2.0/ + /usr/include/ + /usr/include/glib-2.0/ + /usr/lib64/ + /usr/lib64/glib-2.0/ + /usr/lib64/include/ + /usr/lib64/glib-2.0/include/ + NAMES glibconfig.h + DOC "Glib config" + ) + + find_path( PHOBOS_LIBRARY_DIR + HINTS ./ + ../phobos/lib/ + ../lib/phobos/ + ../../phobos/lib/ + ../../lib/phobos/ + ../../../phobos/lib/ + ../../../lib/phobos/ + /usr/lib/ + /usr/lib/phobos/ + /usr/local/lib/ + /usr/local/lib/phobos/ + ${CMAKE_PREFIX_PATH}/lib/phobos/ + ${CMAKE_PREFIX_PATH}/phobos/lib/ + ${CMAKE_INSTALL_PREFIX}/../lib/phobos/ + ${CMAKE_INSTALL_PREFIX}/../phobos/lib/ + ${CMAKE_INSTALL_PREFIX}/lib/phobos/ + ${CMAKE_INSTALL_PREFIX}/phobos/lib/ + NAMES libphobos_store.so libphobos_store.la + DOC "Phobos library" + ) + + find_package_handle_standard_args(phobos + REQUIRED_VARS PHOBOS_INCLUDE_DIR PHOBOS_LIBRARY_DIR GLIB_INCLUDE_DIR + GLIB_CONFIG_INCLUDE_DIR + HANDLE_COMPONENTS) + + set(GLIB_INCLUDE_DIR ${GLIB_INCLUDE_DIR} ${GLIB_CONFIG_INCLUDE_DIR}) + +endif() \ No newline at end of file diff --git a/cmake/Findspdlog.cmake b/cmake/Findspdlog.cmake new file mode 100644 index 0000000000000000000000000000000000000000..66bb445863fd7e5326c53b0825ede34314f3d756 --- /dev/null +++ b/cmake/Findspdlog.cmake @@ -0,0 +1,29 @@ +include(FindPackageHandleStandardArgs) + +# message("Looking for SpdLog") + +if(NOT SPDLOG_FOUND) + find_path( SPDLOG_INCLUDE_DIR + HINTS ./ + ./spdlog/include/ + ../spdlog/include/ + ../../spdlog/include/ + ../../../spdlog/include/ + /usr/spdlog/include/ + /usr/local/include/ + /usr/local/spdlog/include/ + ${CMAKE_PREFIX_PATH}/include/spdlog/include/ + ${CMAKE_PREFIX_PATH}/spdlog/include/ + ${CMAKE_INSTALL_PREFIX}/../include/spdlog/include/ + ${CMAKE_INSTALL_PREFIX}/../spdlog/include/ + ${CMAKE_INSTALL_PREFIX}/include/spdlog/include/ + ${CMAKE_INSTALL_PREFIX}/spdlog/include/ + NAMES spdlog/spdlog.h + DOC "SpdLog headers" + ) + + find_package_handle_standard_args(spdlog + REQUIRED_VARS SPDLOG_INCLUDE_DIR + HANDLE_COMPONENTS) + +endif() \ No newline at end of file diff --git a/cmake/development_flags.cmake b/cmake/development_flags.cmake new file mode 100644 index 0000000000000000000000000000000000000000..40a108959081050b81fd68744f17468419f5a3c0 --- /dev/null +++ b/cmake/development_flags.cmake @@ -0,0 +1,39 @@ +add_library(development_flags INTERFACE) + +option(DEIMOS_ENABLE_SANITIZERS "Enable address and undefined behavior sanitizers." ON) + +if(DEIMOS_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options( + development_flags + INTERFACE + -Wall -Wextra -Wpedantic -pedantic + -Wno-error=unknown-pragmas + -Wswitch-enum + -Wimplicit-fallthrough + -Werror + -0g + ) + + if(DEIMOS_ENABLE_SANITIZERS) + target_compile_options( + development_flags + INTERFACE + -fsanitize=address,undefined + -fno-sanitize=recover=all + -fno-omit-frame-pointer + ) + target_link_libraries( + development_flags + INTERFACE + -fsanitize=address,undefined + ) + endif(DEIMOS_ENABLE_SANITIZERS) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(development_flags INTERFACE -ferror-limit=1) +endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(development_flags INTERFACE -fmax-errors=1) +endif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e7b0a0831cc0454bb4ec1de7cf6c0da0b3e5ad8b --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,58 @@ +find_package(Doxygen REQUIRED) + +# Write a Doxyfile including the main doc/Doxyfile. +# This sets the @INCLUDE_PATH variable to the project root. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in + ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + @ONLY +) + +set(INCLUDE_DOXYFILE "@INCLUDE = ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") + +# Custom command to run doxygen on build/doc/Doxyfile in the build/doc dir. +# Running doxygen is tied to the html/index.html file. +# TODO: The main dependency is currently doc/Doxyfile, but there should be +# a depencency on *every* file being documented! +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml + COMMAND ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Building the documentation..." +) + +if(CUSTOM_DOCU_PATH) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/docu.json + COMMAND python3 + ARGS "${CUSTOM_DOCU_PATH}/xml_to_json_parser/xml_to_json_parser.py" "${CMAKE_CURRENT_BINARY_DIR}/xml" docu.json + MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Parsing the documentation XML..." + ) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Markdown/index.md + COMMAND python3 + ARGS "${CUSTOM_DOCU_PATH}/json_to_Markdown_converter/json_to_Markdown_converter.py" docu.json "${CMAKE_CURRENT_SOURCE_DIR}/Markdown" + MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/docu.json + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Creating the Markdown documentation..." + ) + # Target to wire up building the documentation to the ALL target. + add_custom_target( + doc ALL + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/Markdown/index.md + ) +else(CUSTOM_DOCU_PATH) + # Target to wire up building the documentation to the ALL target. + add_custom_target( + doc ALL + DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/xml/index.xml + ) + +endif(CUSTOM_DOCU_PATH) + + diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000000000000000000000000000000000000..0112ddcb98176eba5ae6d7e2551507f315554ed7 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,52 @@ +# Doxyfile 1.8.13 + +# +# This is a template file used for configuration in doc/CMakeLists.txt. +# + +PROJECT_NAME = Deimos + +# Extraction options +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = YES +EXCLUDE_SYMBOLS = detail + +# Generate case sensitive filenames +CASE_SENSE_NAMES = YES + +# Warning options +# Warn on everything possible, and fail on warning +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_AS_ERROR = YES + +# Input directories +INPUT = @CMAKE_SOURCE_DIR@/src + +EXCLUDE = @CMAKE_SOURCE_DIR@/src/main.cc +EXCLUDE_PATTERNS = */phobos_cpp_wrapper/* + +RECURSIVE = YES + +# HTML output options +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +GENERATE_TREEVIEW = YES + +# Latex font size +FORMULA_FONTSIZE = 10 + +# Macro options +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +PREDEFINED = __cplusplus +SEARCH_INCLUDES = YES + +GENERATE_XML = YES diff --git a/doc/Markdown/deimos/DeimosException.md b/doc/Markdown/deimos/DeimosException.md new file mode 100644 index 0000000000000000000000000000000000000000..230b043075f14bbe1944ad9bf079230a617d95a3 --- /dev/null +++ b/doc/Markdown/deimos/DeimosException.md @@ -0,0 +1,144 @@ +# public deimos::DeimosException + +exception class for all user defined exceptions in Deimos + + + + +## Inheritance: +Is inherited by [deimos::IOException][deimos-IOException], [deimos::storage::PhobosException][deimos-storage-PhobosException]. + +## Protected Attributes +| Name | Description | +| ---- | ---- | +| [m_message](#deimos-DeimosException-m_message) | the internal message | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [DeimosException](#deimos-DeimosException-DeimosException) | default constructor | +| [DeimosException](#deimos-DeimosException-DeimosException-1) | constructor for a message with information on the exception | +| [what](#deimos-DeimosException-what) | get information on this exception | +| [get_inner_error](#deimos-DeimosException-get_inner_error) | return the inner error value that caused this exception | + + + +## Protected Attributes +### protected deimos::DeimosException::m_message + +the internal message + + + + + + + + +[Go to Top](#deimos-DeimosException) + +## Public Functions +### public deimos::DeimosException::DeimosException () + +default constructor + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-DeimosException) + +### public deimos::DeimosException::DeimosException (const char *message) + +constructor for a message with information on the exception + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const char * | message | message explaining what went wrong such that this exception was thrown. | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-DeimosException) + +### public const char * deimos::DeimosException::what () const noexcept override + +get information on this exception + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| const char * | A message explaining what this exception is about | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline +* virtual + + +[Go to Top](#deimos-DeimosException) + +### public int deimos::DeimosException::get_inner_error () const noexcept + +return the inner error value that caused this exception + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| int | | + + + + + +#### Qualifiers: +* const +* inline +* virtual + + +[Go to Top](#deimos-DeimosException) + +[deimos-IOException]:./IOException.md +[deimos-storage-PhobosException]:./storage/PhobosException.md diff --git a/doc/Markdown/deimos/GetRequestHandler.md b/doc/Markdown/deimos/GetRequestHandler.md new file mode 100644 index 0000000000000000000000000000000000000000..c7db0f97dd822f2e03aa726da7be0d712139028a --- /dev/null +++ b/doc/Markdown/deimos/GetRequestHandler.md @@ -0,0 +1,232 @@ +# public deimos::GetRequestHandler + +proxygen class implementation for handling GET requests + + + + +## Inheritance: +Inherits from RequestHandler. + +## Public Functions +| Name | Description | +| ---- | ---- | +| [onRequest](#deimos-GetRequestHandler-onRequest) | first function to be called when a new request comes in | +| [onBody](#deimos-GetRequestHandler-onBody) | function called on every body chunk belonging to this message | +| [onEOM](#deimos-GetRequestHandler-onEOM) | function called when the incoming message is finished | +| [onUpgrade](#deimos-GetRequestHandler-onUpgrade) | function called on upgrade | +| [requestComplete](#deimos-GetRequestHandler-requestComplete) | function ... | +| [onError](#deimos-GetRequestHandler-onError) | function called when an error occurred | +| [onEgressPaused](#deimos-GetRequestHandler-onEgressPaused) | called when the queue is full. | +| [onEgressResumed](#deimos-GetRequestHandler-onEgressResumed) | called when the queue is not longer full. | +| [GetRequestHandler](#deimos-GetRequestHandler-GetRequestHandler) | Constructor for stream class initialization. | + + + +## Public Functions +### public void deimos::GetRequestHandler::onRequest (std::unique_ptr< proxygen::HTTPMessage > headers) noexcept override + +first function to be called when a new request comes in + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< proxygen::HTTPMessage > | headers | headers of the HTTP message this handler was created for | + + + + + + + + + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onBody (std::unique_ptr< folly::IOBuf > body) noexcept override + +function called on every body chunk belonging to this message + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< folly::IOBuf > | body | buffer for the body | + + + + + + + +This is not used in this case because GET requests don't usually have a body + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onEOM () noexcept override + +function called when the incoming message is finished + + + + + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onUpgrade (proxygen::UpgradeProtocol proto) noexcept override + +function called on upgrade + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::UpgradeProtocol | proto | the new protocol | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::requestComplete () noexcept override + +function ... + + + + + + + + + + +> **[Todo][todo]:** find out what this does? + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onError (proxygen::ProxygenError err) noexcept override + +function called when an error occurred + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::ProxygenError | err | the error | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onEgressPaused () noexcept override + +called when the queue is full. + + + + + + + + + + +Contents are copies from a proxygen example + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public void deimos::GetRequestHandler::onEgressResumed () noexcept override + +called when the queue is not longer full. + + + + + + + + + + +Contents are copies from a proxygen example + + + + +[Go to Top](#deimos-GetRequestHandler) + +### public deimos::GetRequestHandler::GetRequestHandler (std::unique_ptr< stream::Stream > input_stream) + +Constructor for stream class initialization. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< [stream::Stream][deimos-stream-Stream] > | input_stream | [stream::Stream][deimos-stream-Stream] class instance to initialize the server | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-GetRequestHandler) + +[deimos-stream-Stream]:./stream/Stream.md +[todo]:./../todo.md#todo diff --git a/doc/Markdown/deimos/GetmdRequestHandler.md b/doc/Markdown/deimos/GetmdRequestHandler.md new file mode 100644 index 0000000000000000000000000000000000000000..9f0cb1a304547971f66b77979bfab4f01d8ff166 --- /dev/null +++ b/doc/Markdown/deimos/GetmdRequestHandler.md @@ -0,0 +1,190 @@ +# public deimos::GetmdRequestHandler + +proxygen class implementation for handling HEAD requests + + + + +## Inheritance: +Inherits from RequestHandler. + +## Public Functions +| Name | Description | +| ---- | ---- | +| [onRequest](#deimos-GetmdRequestHandler-onRequest) | first function to be called when a new request comes in | +| [onBody](#deimos-GetmdRequestHandler-onBody) | function called on every body chunk belonging to this message | +| [onEOM](#deimos-GetmdRequestHandler-onEOM) | function called when the incoming message is finished | +| [onUpgrade](#deimos-GetmdRequestHandler-onUpgrade) | function called on upgrade | +| [requestComplete](#deimos-GetmdRequestHandler-requestComplete) | function ... | +| [onError](#deimos-GetmdRequestHandler-onError) | function called when an error occurred | +| [GetmdRequestHandler](#deimos-GetmdRequestHandler-GetmdRequestHandler) | Constructor for stream class initialization. | + + + +## Public Functions +### public void deimos::GetmdRequestHandler::onRequest (std::unique_ptr< proxygen::HTTPMessage > headers) noexcept override + +first function to be called when a new request comes in + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< proxygen::HTTPMessage > | headers | headers of the HTTP message this handler was created for | + + + + + + + + + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public void deimos::GetmdRequestHandler::onBody (std::unique_ptr< folly::IOBuf > body) noexcept override + +function called on every body chunk belonging to this message + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< folly::IOBuf > | body | buffer for the body | + + + + + + + +This is not used in this case because GET requests don't usually have a body + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public void deimos::GetmdRequestHandler::onEOM () noexcept override + +function called when the incoming message is finished + + + + + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public void deimos::GetmdRequestHandler::onUpgrade (proxygen::UpgradeProtocol proto) noexcept override + +function called on upgrade + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::UpgradeProtocol | proto | the new protocol | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public void deimos::GetmdRequestHandler::requestComplete () noexcept override + +function ... + + + + + + + + + + +> **[Todo][todo]:** find out what this does? + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public void deimos::GetmdRequestHandler::onError (proxygen::ProxygenError err) noexcept override + +function called when an error occurred + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::ProxygenError | err | the error | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-GetmdRequestHandler) + +### public deimos::GetmdRequestHandler::GetmdRequestHandler (std::unique_ptr< stream::Stream > input_stream) + +Constructor for stream class initialization. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< [stream::Stream][deimos-stream-Stream] > | input_stream | [stream::Stream][deimos-stream-Stream] class instance to initialize the server | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-GetmdRequestHandler) + +[deimos-stream-Stream]:./stream/Stream.md +[todo]:./../todo.md#todo diff --git a/doc/Markdown/deimos/HandlerFactory.md b/doc/Markdown/deimos/HandlerFactory.md new file mode 100644 index 0000000000000000000000000000000000000000..6217e359bf8ed73fa5cb7159ff58e5d9320ba244 --- /dev/null +++ b/doc/Markdown/deimos/HandlerFactory.md @@ -0,0 +1,107 @@ +# public deimos::HandlerFactory + +factory to create the handlers for incoming HTTP requests + + + +Depending on the method (GET, PUT, HEAD) this class creates an object of the corresponding request handler. + +All other requests result in an object of [UnsupportedRequestHandler][deimos-UnsupportedRequestHandler], that will then send an 400 Bad request response. + + + + +## Inheritance: +Inherits from RequestHandlerFactory. + +## Public Functions +| Name | Description | +| ---- | ---- | +| [onServerStart](#deimos-HandlerFactory-onServerStart) | function that gets called when the server starts | +| [onServerStop](#deimos-HandlerFactory-onServerStop) | function that gets called when the server stops | +| [onRequest](#deimos-HandlerFactory-onRequest) | this function gets called whenever a request reaches the server | + + + +## Public Functions +### public void deimos::HandlerFactory::onServerStart (folly::EventBase *) noexcept override + +function that gets called when the server starts + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-HandlerFactory) + +### public void deimos::HandlerFactory::onServerStop () noexcept override + +function that gets called when the server stops + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-HandlerFactory) + +### public proxygen::RequestHandler * deimos::HandlerFactory::onRequest (proxygen::RequestHandler *, proxygen::HTTPMessage *headers) noexcept override + +this function gets called whenever a request reaches the server + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::HTTPMessage * | headers | The headers of the HTTP message that reached the server | + +#### Returns: +| Type | Description | +| ---- | ---- | +| proxygen::RequestHandler * | A handler for the incoming request | + + + + + + + + +This function creates the follosing request handlers: + +- GET: [GetRequestHandler][deimos-GetRequestHandler] + +- PUT: [PutRequestHandler][deimos-PutRequestHandler] + +- HEAD: [GetmdRequestHandler][deimos-GetmdRequestHandler] + +- or [UnsupportedRequestHandler][deimos-UnsupportedRequestHandler] in all other cases + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-HandlerFactory) + +[deimos-GetRequestHandler]:./GetRequestHandler.md +[deimos-GetmdRequestHandler]:./GetmdRequestHandler.md +[deimos-PutRequestHandler]:./PutRequestHandler.md +[deimos-UnsupportedRequestHandler]:./UnsupportedRequestHandler.md diff --git a/doc/Markdown/deimos/IOException.md b/doc/Markdown/deimos/IOException.md new file mode 100644 index 0000000000000000000000000000000000000000..baa3e78fce2bb45a13ea9cb975f7a25dfc34bbfb --- /dev/null +++ b/doc/Markdown/deimos/IOException.md @@ -0,0 +1,95 @@ +# public deimos::IOException + +exceptions specifically for errors in I/O + + + + +## Inheritance: +Inherits from [deimos::DeimosException][deimos-DeimosException]. + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_inner_value](#deimos-IOException-m_inner_value) | the value of the error that caused this exception | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [IOException](#deimos-IOException-IOException) | constructor | +| [get_inner_error](#deimos-IOException-get_inner_error) | return the inner error value that caused this exception | + + + +## Private Attributes +### private deimos::IOException::m_inner_value = 0 + +the value of the error that caused this exception + + + + + + + + +[Go to Top](#deimos-IOException) + +## Public Functions +### public deimos::IOException::IOException (const char *caller, const char *function_name, int return_value) + +constructor + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const char * | caller | the function that threw this exception | +| const char * | function_name | the io function that returned an error | +| int | return_value | the return value of the io function | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-IOException) + +### public int deimos::IOException::get_inner_error () const noexcept override + +return the inner error value that caused this exception + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| int | | + + + + + +#### Qualifiers: +* const +* inline +* virtual + + +[Go to Top](#deimos-IOException) + +[deimos-DeimosException]:./DeimosException.md diff --git a/doc/Markdown/deimos/PutRequestHandler.md b/doc/Markdown/deimos/PutRequestHandler.md new file mode 100644 index 0000000000000000000000000000000000000000..da7f9bd59cdc1ec0b99f253cb655015c43b429b1 --- /dev/null +++ b/doc/Markdown/deimos/PutRequestHandler.md @@ -0,0 +1,190 @@ +# public deimos::PutRequestHandler + +proxygen class implementation for handling PUT requests + + + + +## Inheritance: +Inherits from RequestHandler. + +## Public Functions +| Name | Description | +| ---- | ---- | +| [onRequest](#deimos-PutRequestHandler-onRequest) | first function to be called when a new request comes in | +| [onBody](#deimos-PutRequestHandler-onBody) | ccalled with each chunk of the body coming in for the request | +| [onEOM](#deimos-PutRequestHandler-onEOM) | function called when the incoming message is finished | +| [onUpgrade](#deimos-PutRequestHandler-onUpgrade) | function called on upgrade | +| [requestComplete](#deimos-PutRequestHandler-requestComplete) | function ... | +| [onError](#deimos-PutRequestHandler-onError) | function called when an error occurred | +| [PutRequestHandler](#deimos-PutRequestHandler-PutRequestHandler) | Constructor for stream class initialization. | + + + +## Public Functions +### public void deimos::PutRequestHandler::onRequest (std::unique_ptr< proxygen::HTTPMessage > headers) noexcept override + +first function to be called when a new request comes in + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< proxygen::HTTPMessage > | headers | headers of the HTTP message this handler was created for | + + + + + + + + + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public void deimos::PutRequestHandler::onBody (std::unique_ptr< folly::IOBuf > body) noexcept override + +ccalled with each chunk of the body coming in for the request + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< folly::IOBuf > | body | chunk of data to be added to the file | + + + + + + + + + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public void deimos::PutRequestHandler::onEOM () noexcept override + +function called when the incoming message is finished + + + + + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public void deimos::PutRequestHandler::onUpgrade (proxygen::UpgradeProtocol proto) noexcept override + +function called on upgrade + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::UpgradeProtocol | proto | the new protocol | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public void deimos::PutRequestHandler::requestComplete () noexcept override + +function ... + + + + + + + + + + +> **[Todo][todo]:** find out what this does? + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public void deimos::PutRequestHandler::onError (proxygen::ProxygenError err) noexcept override + +function called when an error occurred + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| proxygen::ProxygenError | err | the error | + + + + + + + + +Not supported in our case! + + + + +[Go to Top](#deimos-PutRequestHandler) + +### public deimos::PutRequestHandler::PutRequestHandler (std::unique_ptr< stream::Stream > input_stream) + +Constructor for stream class initialization. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< [stream::Stream][deimos-stream-Stream] > | input_stream | [stream::Stream][deimos-stream-Stream] class instance to initialize the server | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-PutRequestHandler) + +[deimos-stream-Stream]:./stream/Stream.md +[todo]:./../todo.md#todo diff --git a/doc/Markdown/deimos/UnsupportedRequestHandler.md b/doc/Markdown/deimos/UnsupportedRequestHandler.md new file mode 100644 index 0000000000000000000000000000000000000000..cc4ce3a82701018941d39bf4e25b470e5dca9245 --- /dev/null +++ b/doc/Markdown/deimos/UnsupportedRequestHandler.md @@ -0,0 +1,11 @@ +# public deimos::UnsupportedRequestHandler + +proxygen class implementation for handling all requests that we don't support + + + + +## Inheritance: +Inherits from RequestHandler. + + diff --git a/doc/Markdown/deimos/index.md b/doc/Markdown/deimos/index.md new file mode 100644 index 0000000000000000000000000000000000000000..8679e030485c0ce1fe37b3d78126f92a60718d5f --- /dev/null +++ b/doc/Markdown/deimos/index.md @@ -0,0 +1,33 @@ +# deimos + + + + +## Classes +| Name | Description | +| ---- | ---- | +| [DeimosException](./DeimosException.md) | exception class for all user defined exceptions in Deimos | +| [GetmdRequestHandler](./GetmdRequestHandler.md) | proxygen class implementation for handling HEAD requests | +| [GetRequestHandler](./GetRequestHandler.md) | proxygen class implementation for handling GET requests | +| [HandlerFactory](./HandlerFactory.md) | factory to create the handlers for incoming HTTP requests | +| [IOException](./IOException.md) | exceptions specifically for errors in I/O | +| [PutRequestHandler](./PutRequestHandler.md) | proxygen class implementation for handling PUT requests | +| [UnsupportedRequestHandler](./UnsupportedRequestHandler.md) | proxygen class implementation for handling all requests that we don't support | + + +## Namespaces +| Name | Description | +| ---- | ---- | +| [s3_utilities](./s3_utilities/index.md) | namespace for S3 specific utilities | +| [storage](./storage/index.md) | namespace for storage classes that belong to / inherit from from [deimos::storage::Storage][deimos-storage-Storage] | +| [stream](./stream/index.md) | namespace for stream classes that belong to / inherit from from [deimos::stream::Stream][deimos-stream-Stream] | +| [util](./util/index.md) | namespace for utility classes | + + + +[deimos-DeimosException]:./DeimosException.md +[deimos-IOException]:./IOException.md +[deimos-UnsupportedRequestHandler]:./UnsupportedRequestHandler.md +[deimos-storage-PhobosException]:./storage/PhobosException.md +[deimos-storage-Storage]:./storage/Storage.md +[deimos-stream-Stream]:./stream/Stream.md diff --git a/doc/Markdown/deimos/s3_utilities/S3_authorisation.md b/doc/Markdown/deimos/s3_utilities/S3_authorisation.md new file mode 100644 index 0000000000000000000000000000000000000000..44891eaa4d40784cd0dd139514f331ab45f88660 --- /dev/null +++ b/doc/Markdown/deimos/s3_utilities/S3_authorisation.md @@ -0,0 +1,761 @@ +# public deimos::s3_utilities::S3_authorisation + +class to perform an authorisation of an S3 request. + + + +The implementation of this class follows the guidelines from [https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) + + + + + + +## Helper functions +| Name | Description | +| ---- | ---- | +| [search_for_user](#deimos-s3_utilities-S3_authorisation-search_for_user) | checks whether the user id saved in [m_user_identifier][deimos-s3_utilities-S3_authorisation-m_user_identifier] exists in the user database | +| [has_signed_payload](#deimos-s3_utilities-S3_authorisation-has_signed_payload) | check if this message has a signed content body | +| [print_info](#deimos-s3_utilities-S3_authorisation-print_info) | print current state of member variables | + + +## Extract header information +| Name | Description | +| ---- | ---- | +| [set_authorisation_info](#deimos-s3_utilities-S3_authorisation-set_authorisation_info) | get the information about authorisation from the header and save it to member variables | +| [extract_string_part](#deimos-s3_utilities-S3_authorisation-extract_string_part) | Extract information from a string. | +| [split_signed_headers](#deimos-s3_utilities-S3_authorisation-split_signed_headers) | split up the "SignedHeaders" value of the HTTP request | +| [split_queries](#deimos-s3_utilities-S3_authorisation-split_queries) | split up the queries string of the HTTP request (thats the part of the request path after '?') | +| [split_credentials](#deimos-s3_utilities-S3_authorisation-split_credentials) | split up the "Credential" value of the HTTP request | + + +## Signature creation +| Name | Description | +| ---- | ---- | +| [check](#deimos-s3_utilities-S3_authorisation-check) | checks the signature of the message | +| [create_canonical_request](#deimos-s3_utilities-S3_authorisation-create_canonical_request) | create a canonical request | +| [create_string_to_sign](#deimos-s3_utilities-S3_authorisation-create_string_to_sign) | create a string to sign | +| [get_signature](#deimos-s3_utilities-S3_authorisation-get_signature) | create the signature | + + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_status](#deimos-s3_utilities-S3_authorisation-m_status) | the current status of the authorisation | +| [m_user_identifier](#deimos-s3_utilities-S3_authorisation-m_user_identifier) | the public key / identifier of the user that send the request | +| [m_user_key](#deimos-s3_utilities-S3_authorisation-m_user_key) | the key that belongs to [m_user_identifier][deimos-s3_utilities-S3_authorisation-m_user_identifier] | +| [m_date](#deimos-s3_utilities-S3_authorisation-m_date) | the date as saved in the credentials of the request | +| [m_region](#deimos-s3_utilities-S3_authorisation-m_region) | the S3 region as saved in the credentials of the request | +| [m_signed_headers](#deimos-s3_utilities-S3_authorisation-m_signed_headers) | a (sorted) list of the headers that have been used to sign the request | +| [m_queries](#deimos-s3_utilities-S3_authorisation-m_queries) | a (sorted) list of all queries split up into key and value | +| [m_signature](#deimos-s3_utilities-S3_authorisation-m_signature) | the signature stored in the request | +| [m_payload](#deimos-s3_utilities-S3_authorisation-m_payload) | the payload of the request | +| [m_error](#deimos-s3_utilities-S3_authorisation-m_error) | the error description if one occurred | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [authorise](#deimos-s3_utilities-S3_authorisation-authorise) | main method to run the authorisation algorithm | +| [add_chunk](#deimos-s3_utilities-S3_authorisation-add_chunk) | add a chunk of data to the payload | +| [is_valid](#deimos-s3_utilities-S3_authorisation-is_valid) | checks if the authorisation was successful | +| [get_status](#deimos-s3_utilities-S3_authorisation-get_status) | returns the current status of the authorisation | +| [get_error](#deimos-s3_utilities-S3_authorisation-get_error) | returns the internal error | + + + +## Helper functions +### private bool deimos::s3_utilities::S3_authorisation::search_for_user () + +checks whether the user id saved in [m_user_identifier][deimos-s3_utilities-S3_authorisation-m_user_identifier] exists in the user database + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if the user has been found, false otherwise | + + + + + + + + + +Sets +[m_user_key][deimos-s3_utilities-S3_authorisation-m_user_key] on success + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private bool deimos::s3_utilities::S3_authorisation::has_signed_payload (const S3_header &headers) const + +check if this message has a signed content body + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | the headers of the message to check | + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if the message contains a signed body, false if it doesn't have any body of if it is unsigned | + + + + + + + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private void deimos::s3_utilities::S3_authorisation::print_info () const + +print current state of member variables + + + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +## Extract header information +### private bool deimos::s3_utilities::S3_authorisation::set_authorisation_info (const S3_header &headers) + +get the information about authorisation from the header and save it to member variables + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | the header to extract the information from | + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if all information was found, false if an error occurred | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private std::string deimos::s3_utilities::S3_authorisation::extract_string_part (std::string complete, std::string identifier, std::string delimiter) const + +Extract information from a string. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | complete | the complete string will all information | +| std::string | identifier | the identifier of the specific piece of information required | +| std::string | delimiter | the delimiter between the parts of information | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | required information as string | + + + + + + + + + + + +If there is for example a string of the form "id=info;id2=info2" then ';' is the delimiter, 'id' is the identifier and this function will return "info". + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private void deimos::s3_utilities::S3_authorisation::split_signed_headers (std::string all_signed_headers) + +split up the "SignedHeaders" value of the HTTP request + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | all_signed_headers | the value of the SignedHeaders header | + + + + + + + + +This function saves the headers into +[m_signed_headers][deimos-s3_utilities-S3_authorisation-m_signed_headers] + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private void deimos::s3_utilities::S3_authorisation::split_queries (std::string queries) + +split up the queries string of the HTTP request (thats the part of the request path after '?') + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | queries | the complete query string | + + + + + + + + +This function saves each query into +[m_queries][deimos-s3_utilities-S3_authorisation-m_queries] + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private void deimos::s3_utilities::S3_authorisation::split_credentials (std::string credentials) + +split up the "Credential" value of the HTTP request + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | credentials | the value of the Credential header | + + + + + + + + +This function saves the credentials into +[m_user_identifier][deimos-s3_utilities-S3_authorisation-m_user_identifier], +[m_date][deimos-s3_utilities-S3_authorisation-m_date] and +[m_region][deimos-s3_utilities-S3_authorisation-m_region] + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +## Signature creation +### private bool deimos::s3_utilities::S3_authorisation::check (const S3_header &headers) const + +checks the signature of the message + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | the headers of the message to check | + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if the signature was valid, false otherwise | + + + + + + + + + + + +This function splits the message's contents into the needed parts, creates a signature and compares it with the one saved in the headers + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private std::string deimos::s3_utilities::S3_authorisation::create_canonical_request (const S3_header &headers) const + +create a canonical request + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | the headers of the message to create the request from | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the canonical request | + + + + + + + + + + + +For more information what this means, see: +[https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private std::string deimos::s3_utilities::S3_authorisation::create_string_to_sign (const S3_header &headers, std::string canonical_request) const + +create a string to sign + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | the headers of the message to create the request from | +| std::string | canonical_request | the canonical request from [create_canonical_request][deimos-s3_utilities-S3_authorisation-create_canonical_request] | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the string to sign | + + + + + + + + + + + +For more information what this means, see: +[https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private std::string deimos::s3_utilities::S3_authorisation::get_signature (std::string string_to_sign) const + +create the signature + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | string_to_sign | the string to sign from [create_string_to_sign][deimos-s3_utilities-S3_authorisation-create_string_to_sign] | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the signature | + + + + + + + + + + + +For more information what this means, see: +[https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html) + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +## Private Attributes +### private deimos::s3_utilities::S3_authorisation::m_status = + +the current status of the authorisation + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_user_identifier + +the public key / identifier of the user that send the request + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_user_key + +the key that belongs to [m_user_identifier][deimos-s3_utilities-S3_authorisation-m_user_identifier] + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_date + +the date as saved in the credentials of the request + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_region + +the S3 region as saved in the credentials of the request + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_signed_headers + +a (sorted) list of the headers that have been used to sign the request + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_queries + +a (sorted) list of all queries split up into key and value + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_signature + +the signature stored in the request + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_payload = "" + +the payload of the request + + + + + + + + + + +This is the whole body since we do not support multiple chunked signatures + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### private deimos::s3_utilities::S3_authorisation::m_error = access_denied + +the error description if one occurred + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +## Public Functions +### public [Authorisation_status][deimos-s3_utilities-Authorisation_status] deimos::s3_utilities::S3_authorisation::authorise (const S3_header &headers) + +main method to run the authorisation algorithm + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [S3_header][deimos-s3_utilities-S3_header] & | headers | of the message to authenticate | + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Authorisation_status][deimos-s3_utilities-Authorisation_status] | status of the authorisation | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### public void deimos::s3_utilities::S3_authorisation::add_chunk (std::string chunk) + +add a chunk of data to the payload + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | chunk | the string to add | + + + + + + + + +Since the whole body has to be hashed in order to verify the signature of the message each chunk of data has to be added here in order. + +Otherwise the authentication will fail! + + + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### public bool deimos::s3_utilities::S3_authorisation::is_valid () const + +checks if the authorisation was successful + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if it has been valid, false if it hasn't started, it is in progress, or if an error occurred. | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### public [Authorisation_status][deimos-s3_utilities-Authorisation_status] deimos::s3_utilities::S3_authorisation::get_status () const + +returns the current status of the authorisation + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Authorisation_status][deimos-s3_utilities-Authorisation_status] | the current status of the authorisaton | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +### public [s3_error_info][deimos-s3_utilities-s3_error_info] deimos::s3_utilities::S3_authorisation::get_error () const + +returns the internal error + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| [s3_error_info][deimos-s3_utilities-s3_error_info] | the error that might have occurred in the process | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_authorisation) + +[deimos-s3_utilities-Authorisation_status]:./index.md#deimos-s3_utilities-Authorisation_status +[deimos-s3_utilities-S3_authorisation-create_canonical_request]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-create_canonical_request +[deimos-s3_utilities-S3_authorisation-create_string_to_sign]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-create_string_to_sign +[deimos-s3_utilities-S3_authorisation-m_date]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_date +[deimos-s3_utilities-S3_authorisation-m_queries]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_queries +[deimos-s3_utilities-S3_authorisation-m_region]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_region +[deimos-s3_utilities-S3_authorisation-m_signed_headers]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_signed_headers +[deimos-s3_utilities-S3_authorisation-m_user_identifier]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_user_identifier +[deimos-s3_utilities-S3_authorisation-m_user_key]:./S3_authorisation.md#deimos-s3_utilities-S3_authorisation-m_user_key +[deimos-s3_utilities-S3_header]:./S3_header.md +[deimos-s3_utilities-s3_error_info]:./s3_error_info.md diff --git a/doc/Markdown/deimos/s3_utilities/S3_header.md b/doc/Markdown/deimos/s3_utilities/S3_header.md new file mode 100644 index 0000000000000000000000000000000000000000..3f23d8b2885151c2541f07d35b52a35ac119a5c8 --- /dev/null +++ b/doc/Markdown/deimos/s3_utilities/S3_header.md @@ -0,0 +1,348 @@ +# public deimos::s3_utilities::S3_header + +class to extract the S3 specific information from proxygens HTTPMessage headers + + + + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_headers](#deimos-s3_utilities-S3_header-m_headers) | inner pointer to the proxygen header object | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [set_headers](#deimos-s3_utilities-S3_header-set_headers) | sets the internal header instance to the given header object | +| [get_bucket](#deimos-s3_utilities-S3_header-get_bucket) | extracts the name of the S3 bucket requested in the HTTP message | +| [get_key_without_bucket](#deimos-s3_utilities-S3_header-get_key_without_bucket) | extracts the name of the key id requested in the HTTP message | +| [get_key](#deimos-s3_utilities-S3_header-get_key) | extracts the name of the key id requested in the HTTP message | +| [is_bucket_only_request](#deimos-s3_utilities-S3_header-is_bucket_only_request) | checks if the message we got is only concerning a bucket or a key inside a bucket | +| [get_body_length](#deimos-s3_utilities-S3_header-get_body_length) | gets the total length of all body chunks that is expected | +| [get_meta_data](#deimos-s3_utilities-S3_header-get_meta_data) | gets the S3 metadata stored in the HTTP headers | +| [get_header_by_name](#deimos-s3_utilities-S3_header-get_header_by_name) | extract one header value from the headers | +| [get_queries](#deimos-s3_utilities-S3_header-get_queries) | extract the query string from the headers | +| [get_method](#deimos-s3_utilities-S3_header-get_method) | get the HTTP method the header belongs to | + + + +## Private Attributes +### private deimos::s3_utilities::S3_header::m_headers + +inner pointer to the proxygen header object + + + + + + + + +[Go to Top](#deimos-s3_utilities-S3_header) + +## Public Functions +### public void deimos::s3_utilities::S3_header::set_headers (std::unique_ptr< proxygen::HTTPMessage > new_m_headers) + +sets the internal header instance to the given header object + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< proxygen::HTTPMessage > | new_m_headers | headers that should be read out in this instance | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_bucket () const + +extracts the name of the S3 bucket requested in the HTTP message + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | name of the bucket | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_key_without_bucket () const + +extracts the name of the key id requested in the HTTP message + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | name of the key | + + + + + + + +This will only return the key itself without the leading bucket name! + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_key () const + +extracts the name of the key id requested in the HTTP message + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | name of the key | + + + + + + + +This is the whole path argument without the leading '/' but including the bucket name! + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public bool deimos::s3_utilities::S3_header::is_bucket_only_request () const + +checks if the message we got is only concerning a bucket or a key inside a bucket + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | true if the path in these headers only has a bucket name false if it contains a key | + + + + + + + +The way this is decided currently is by checking if there is a '/' in the path as this would split the bucket name from the key. This might not be ideal... + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public size_t deimos::s3_utilities::S3_header::get_body_length () const + +gets the total length of all body chunks that is expected + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| size_t | the length of the total body to be expected | + + + + + + + +This reads out the header "Content-Length" + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::map< std::string, std::string > deimos::s3_utilities::S3_header::get_meta_data () const + +gets the S3 metadata stored in the HTTP headers + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::map< std::string, std::string > | a map consisting of all key value pairs found in the header | + + + + + + + +In S3 arbitrary metadata can be defined. This has the form x-amx-meta-KEY: VALUE for for example a KEY:VALUE pair. + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_header_by_name (const std::string header_name) const + +extract one header value from the headers + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | header_name | the name of the header that should be extracted | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the value of the requested header or an empty_string if it is not found | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_queries () const + +extract the query string from the headers + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the query string of the request or an empty_string if it doesn't have one | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + +### public std::string deimos::s3_utilities::S3_header::get_method () const + +get the HTTP method the header belongs to + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the method as a string | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-S3_header) + diff --git a/doc/Markdown/deimos/s3_utilities/index.md b/doc/Markdown/deimos/s3_utilities/index.md new file mode 100644 index 0000000000000000000000000000000000000000..90c953c3eb27f8faaa70a982b92963b6546627a6 --- /dev/null +++ b/doc/Markdown/deimos/s3_utilities/index.md @@ -0,0 +1,242 @@ +# deimos::s3_utilities + +namespace for S3 specific utilities + + + + +## Classes +| Name | Description | +| ---- | ---- | +| [S3_authorisation](./S3_authorisation.md) | class to perform an authorisation of an S3 request. | +| [s3_error_info](./s3_error_info.md) | class to contain all the information needed to send a proper s3 error message | +| [S3_header](./S3_header.md) | class to extract the S3 specific information from proxygens HTTPMessage headers | + + +## Enumerations +| Name | Description | +| ---- | ---- | +| [Authorisation_status](#deimos-s3_utilities-Authorisation_status) | enum to store all the different states a [deimos::s3_utilities::S3_authorisation][deimos-s3_utilities-S3_authorisation] object can be in | + + +## Functions +| Name | Description | +| ---- | ---- | +| [create_s3_error](#deimos-s3_utilities-create_s3_error) | create a [s3_error_info][deimos-s3_utilities-s3_error_info] depending on the error value | +| [uri_encode](#deimos-s3_utilities-uri_encode) | create a uri encoding of a string | +| [openssl_sha256](#deimos-s3_utilities-openssl_sha256) | create a sha256 hash from the data | +| [openssl_sha256](#deimos-s3_utilities-openssl_sha256-1) | shortcut for openssl_sha256(void*, unsigned int) | +| [openssl_hmac](#deimos-s3_utilities-openssl_hmac) | create a key hashed message | +| [openssl_hmac_hex](#deimos-s3_utilities-openssl_hmac_hex) | create a key hashed message | + + + +## Enumerations +### public deimos::s3_utilities::Authorisation_status + +enum to store all the different states a [deimos::s3_utilities::S3_authorisation][deimos-s3_utilities-S3_authorisation] object can be in + + + + + + +#### Enum Values: +| Name | Description | Value | +| ---- | ---- | ---- | +| undefined | authorisation has not yet started | | +| waiting_for_payload | members have been set up and payload can be added | | +| failed | the authorisation was invalid or some error occurred | | +| valid | authorisation successful | | + + + +[Go to Top](#deimos-s3_utilities) + +## Functions +### public [s3_error_info][deimos-s3_utilities-s3_error_info] deimos::s3_utilities::create_s3_error (const int return_value) + +create a [s3_error_info][deimos-s3_utilities-s3_error_info] depending on the error value + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const int | return_value | the error code (errno, etc) that the function returned that raised the exception that preceded the call to this function | + +#### Returns: +| Type | Description | +| ---- | ---- | +| [s3_error_info][deimos-s3_utilities-s3_error_info] | an [s3_error_info][deimos-s3_utilities-s3_error_info] struct containing readable information on the error | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities) + +### public std::string deimos::s3_utilities::uri_encode (const std::string string_to_encode, const bool encode_slash) + +create a uri encoding of a string + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | string_to_encode | the string that should be encoded | +| const bool | encode_slash | bool to signalise if the / should be encoded | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the URI encoded string | + + + + + + + +for more information see [https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) + + + + +[Go to Top](#deimos-s3_utilities) + +### public std::string deimos::s3_utilities::openssl_sha256 (const void *data, const unsigned int length) + +create a sha256 hash from the data + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const void * | data | buffer to the data to hash | +| const unsigned int | length | number of byte in the buffer | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the resulting hash as string or "" in case of error | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities) + +### public std::string deimos::s3_utilities::openssl_sha256 (const std::string msg) + +shortcut for openssl_sha256(void*, unsigned int) + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | msg | the string to hash | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the resulting hash as string or "" in case of error | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities) + +### public std::string deimos::s3_utilities::openssl_hmac (const std::string key, const std::string msg) + +create a key hashed message + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | key | the key to sign the message with | +| const std::string | msg | the message to sign | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | a string representation of the signed message (unreadable) | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities) + +### public std::string deimos::s3_utilities::openssl_hmac_hex (const std::string key, const std::string msg) + +create a key hashed message + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | key | the key to sign the message with | +| const std::string | msg | the message to sign | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | the signed message in HEX format | + + + + + + + + + + + + +[Go to Top](#deimos-s3_utilities) + +[deimos-s3_utilities-S3_authorisation]:./S3_authorisation.md +[deimos-s3_utilities-s3_error_info]:./s3_error_info.md diff --git a/doc/Markdown/deimos/s3_utilities/s3_error_info.md b/doc/Markdown/deimos/s3_utilities/s3_error_info.md new file mode 100644 index 0000000000000000000000000000000000000000..2c74198b81476133ab8d0a692ee332f48bfca4a4 --- /dev/null +++ b/doc/Markdown/deimos/s3_utilities/s3_error_info.md @@ -0,0 +1,173 @@ +# public deimos::s3_utilities::s3_error_info + +class to contain all the information needed to send a proper s3 error message + + + + +## Public Attributes +| Name | Description | +| ---- | ---- | +| [https_error_code](#deimos-s3_utilities-s3_error_info-https_error_code) | https error code (400s or 500s) | +| [https_error_identifier](#deimos-s3_utilities-s3_error_info-https_error_identifier) | readable string for the code [https_error_code][deimos-s3_utilities-s3_error_info-https_error_code] | +| [https_error_message](#deimos-s3_utilities-s3_error_info-https_error_message) | human readable message with information on the error | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [get_xml](#deimos-s3_utilities-s3_error_info-get_xml) | created a XML message containing all the error information | +| [operator==](#deimos-s3_utilities-s3_error_info-operator==) | equality operator | +| [operator!=](#deimos-s3_utilities-s3_error_info-operator!=) | inequality operator | + + + +## Public Attributes +### public deimos::s3_utilities::s3_error_info::https_error_code + +https error code (400s or 500s) + + + + + + + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +### public deimos::s3_utilities::s3_error_info::https_error_identifier + +readable string for the code [https_error_code][deimos-s3_utilities-s3_error_info-https_error_code] + + + + + + + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +### public deimos::s3_utilities::s3_error_info::https_error_message + +human readable message with information on the error + + + + + + + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +## Public Functions +### public std::string deimos::s3_utilities::s3_error_info::get_xml (const std::string request) const + +created a XML message containing all the error information + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const std::string | request | the request that was processed when this error occurred | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::string | a string containg an S3 compatible XML error message | + + + + + + + + + + + + +#### Qualifiers: +* const + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +### public bool deimos::s3_utilities::s3_error_info::operator== (const s3_error_info &other) const + +equality operator + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [s3_error_info][deimos-s3_utilities-s3_error_info] & | other | the other error info object to compare | + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | | + + + + + + + + +return true if all inner members are equal, false otherwise + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +### public bool deimos::s3_utilities::s3_error_info::operator!= (const s3_error_info &other) const + +inequality operator + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const [s3_error_info][deimos-s3_utilities-s3_error_info] & | other | the other error info object to compare | + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | | + + + + + + + + +return true if any inner member is different, false otherwise (all equal) + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-s3_utilities-s3_error_info) + +[deimos-s3_utilities-s3_error_info]:./s3_error_info.md +[deimos-s3_utilities-s3_error_info-https_error_code]:./s3_error_info.md#deimos-s3_utilities-s3_error_info-https_error_code diff --git a/doc/Markdown/deimos/storage/PhobosException.md b/doc/Markdown/deimos/storage/PhobosException.md new file mode 100644 index 0000000000000000000000000000000000000000..937973d45a24b7404a4836736b6579c8550e754d --- /dev/null +++ b/doc/Markdown/deimos/storage/PhobosException.md @@ -0,0 +1,95 @@ +# public deimos::storage::PhobosException + +exceptions specifically for the phobos backend library + + + + +## Inheritance: +Inherits from [deimos::DeimosException][deimos-DeimosException]. + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_inner_value](#deimos-storage-PhobosException-m_inner_value) | the value of the error that caused this exception | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [PhobosException](#deimos-storage-PhobosException-PhobosException) | constructor | +| [get_inner_error](#deimos-storage-PhobosException-get_inner_error) | return the inner error value that caused this exception | + + + +## Private Attributes +### private deimos::storage::PhobosException::m_inner_value = 0 + +the value of the error that caused this exception + + + + + + + + +[Go to Top](#deimos-storage-PhobosException) + +## Public Functions +### public deimos::storage::PhobosException::PhobosException (const char *caller, const char *function_name, int return_value) + +constructor + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const char * | caller | the function that threw this exception | +| const char * | function_name | the phobos function that returned an error | +| int | return_value | the return value of the phobos function | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-storage-PhobosException) + +### public int deimos::storage::PhobosException::get_inner_error () const noexcept override + +return the inner error value that caused this exception + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| int | | + + + + + +#### Qualifiers: +* const +* inline +* virtual + + +[Go to Top](#deimos-storage-PhobosException) + +[deimos-DeimosException]:./../DeimosException.md diff --git a/doc/Markdown/deimos/storage/Phobos_file.md b/doc/Markdown/deimos/storage/Phobos_file.md new file mode 100644 index 0000000000000000000000000000000000000000..fc4af11848ab2005785b23f60beb26fcb5f34ebc --- /dev/null +++ b/doc/Markdown/deimos/storage/Phobos_file.md @@ -0,0 +1,393 @@ +# public deimos::storage::Phobos_file + +Phobos specific mplementation of the [Storage][deimos-storage-Storage] class. + + + +This class uses the phobos library as a backend to store and retrieve files in chunks. + + + + +## Inheritance: +Inherits from [deimos::storage::Storage][deimos-storage-Storage]. + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_descriptor](#deimos-storage-Phobos_file-m_descriptor) | struct to contain information for phobos | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [Phobos_file](#deimos-storage-Phobos_file-Phobos_file) | default constructor | +| [~Phobos_file](#deimos-storage-Phobos_file-~Phobos_file) | default destructor | +| [Phobos_file](#deimos-storage-Phobos_file-Phobos_file-1) | default move contructor | +| [operator=](#deimos-storage-Phobos_file-operator=) | default assignment operator for rvalue references | +| [Phobos_file](#deimos-storage-Phobos_file-Phobos_file-2) | copy constructor deleted | +| [operator=](#deimos-storage-Phobos_file-operator=-1) | assignment operator deleted | +| [set_meta_data](#deimos-storage-Phobos_file-set_meta_data) | set the metadata that an object that is added to the database should get | +| [get_meta_data](#deimos-storage-Phobos_file-get_meta_data) | get the metadata associated to the current object as a map of key:value pairs | +| [db_put](#deimos-storage-Phobos_file-db_put) | Puts data to the databse. | +| [db_get](#deimos-storage-Phobos_file-db_get) | Gets data from the databse. | +| [prepare_put](#deimos-storage-Phobos_file-prepare_put) | Starts a put operation to the database. | +| [prepare_get](#deimos-storage-Phobos_file-prepare_get) | Starts a get operation to the database. | + + +## Private Functions +| Name | Description | +| ---- | ---- | +| [set_bucket_name](#deimos-storage-Phobos_file-set_bucket_name) | set the name for the bucket an object blongs to | +| [close_file](#deimos-storage-Phobos_file-close_file) | close the file opened for phobos | + + + +## Private Attributes +### private deimos::storage::Phobos_file::m_descriptor = {0} + +struct to contain information for phobos + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +## Public Functions +### public deimos::storage::Phobos_file::Phobos_file () + +default constructor + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +### public deimos::storage::Phobos_file::~Phobos_file () + +default destructor + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +### public deimos::storage::Phobos_file::Phobos_file (Phobos_file &&input) + +default move contructor + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| [Phobos_file][deimos-storage-Phobos_file] && | input | | + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-storage-Phobos_file) + +### public [Phobos_file][deimos-storage-Phobos_file] & deimos::storage::Phobos_file::operator= (Phobos_file &&input) + +default assignment operator for rvalue references + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| [Phobos_file][deimos-storage-Phobos_file] && | input | | + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Phobos_file][deimos-storage-Phobos_file] & | | + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-storage-Phobos_file) + +### public deimos::storage::Phobos_file::Phobos_file (const Phobos_file &)=delete + +copy constructor deleted + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +### public [Phobos_file][deimos-storage-Phobos_file] & deimos::storage::Phobos_file::operator= (const Phobos_file &)=delete + +assignment operator deleted + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Phobos_file][deimos-storage-Phobos_file] & | | + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +### public void deimos::storage::Phobos_file::set_meta_data (std::map< std::string, std::string > meta_data) override + +set the metadata that an object that is added to the database should get + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::map< std::string, std::string > | meta_data | a map of all key:value pairs that should be added to the data | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +### public std::map< std::string, std::string > deimos::storage::Phobos_file::get_meta_data (std::string object_id) override + +get the metadata associated to the current object as a map of key:value pairs + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | The object id of the requested object | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::map< std::string, std::string > | a map of the key:value metadata pairs | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +### public ssize_t deimos::storage::Phobos_file::db_put (size_t size) override + +Puts data to the databse. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| size_t | size | the number of bytes to add to the file in the database | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +### public ssize_t deimos::storage::Phobos_file::db_get () override + +Gets data from the databse. + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the number of bytes read from the database | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +### public void deimos::storage::Phobos_file::prepare_put (int file_descriptor, std::string object_id) override + +Starts a put operation to the database. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| int | file_descriptor | the descriptor of the open file, which contents should be added to the database | +| std::string | object_id | the internal storage id the data should get | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +### public void deimos::storage::Phobos_file::prepare_get (int file_descriptor, std::string object_id) override + +Starts a get operation to the database. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| int | file_descriptor | the descriptor of the open file, to which the contents of the database should be written | +| std::string | object_id | the internal storage id from the data that should be retrieved | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Phobos_file) + +## Private Functions +### private void deimos::storage::Phobos_file::set_bucket_name (std::string bucket_name) + +set the name for the bucket an object blongs to + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | bucket_name | name of the bucket | + + + + + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +### private void deimos::storage::Phobos_file::close_file () + +close the file opened for phobos + + + + + + + + +[Go to Top](#deimos-storage-Phobos_file) + +[deimos-storage-Phobos_file]:./Phobos_file.md +[deimos-storage-Storage]:./Storage.md diff --git a/doc/Markdown/deimos/storage/Storage.md b/doc/Markdown/deimos/storage/Storage.md new file mode 100644 index 0000000000000000000000000000000000000000..4b7cd578478383180a7497ca5b8e7004a6c11517 --- /dev/null +++ b/doc/Markdown/deimos/storage/Storage.md @@ -0,0 +1,269 @@ +# public deimos::storage::Storage + +virtual storage class to be implemented by backend storage + + + +This class is a prototype for every class that handles to internal database that should be accessed by the Deimos. This class represents exactly one object file in the internal database + + + + +## Inheritance: +Is inherited by [deimos::storage::Phobos_file][deimos-storage-Phobos_file]. + +## Metadata functions +| Name | Description | +| ---- | ---- | +| [set_meta_data](#deimos-storage-Storage-set_meta_data) | set the metadata that an object that is added to the database should get | +| [get_meta_data](#deimos-storage-Storage-get_meta_data) | get the metadata associated to the current object as a map of key:value pairs | + + +## PUT functions +| Name | Description | +| ---- | ---- | +| [prepare_put](#deimos-storage-Storage-prepare_put) | Starts a put operation to the database. | +| [db_put](#deimos-storage-Storage-db_put) | Puts data to the databse. | + + +## GET functions +| Name | Description | +| ---- | ---- | +| [prepare_get](#deimos-storage-Storage-prepare_get) | Starts a get operation to the database. | +| [db_get](#deimos-storage-Storage-db_get) | Gets data from the databse. | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [Storage](#deimos-storage-Storage-Storage) | default constructor | +| [~Storage](#deimos-storage-Storage-~Storage) | default destructor | + + + +## Metadata functions +### public void deimos::storage::Storage::set_meta_data (std::map< std::string, std::string > meta_data)=0 + +set the metadata that an object that is added to the database should get + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::map< std::string, std::string > | meta_data | a map of all key:value pairs that should be added to the data | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +### public std::map< std::string, std::string > deimos::storage::Storage::get_meta_data (std::string object_id)=0 + +get the metadata associated to the current object as a map of key:value pairs + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | The object id of the requested object | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::map< std::string, std::string > | a map of the key:value metadata pairs | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +## PUT functions +### public void deimos::storage::Storage::prepare_put (int file_descriptor, std::string object_id)=0 + +Starts a put operation to the database. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| int | file_descriptor | the descriptor of the open file, which contents should be added to the database | +| std::string | object_id | the internal storage id the data should get | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +### public ssize_t deimos::storage::Storage::db_put (size_t size)=0 + +Puts data to the databse. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| size_t | size | the number of bytes to add to the file in the database | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +## GET functions +### public void deimos::storage::Storage::prepare_get (int file_descriptor, std::string object_id)=0 + +Starts a get operation to the database. + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| int | file_descriptor | the descriptor of the open file, to which the contents of the database should be written | +| std::string | object_id | the internal storage id from the data that should be retrieved | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +### public ssize_t deimos::storage::Storage::db_get ()=0 + +Gets data from the databse. + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the number of bytes read from the database | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-storage-Storage) + +## Public Functions +### public deimos::storage::Storage::Storage () + +default constructor + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-storage-Storage) + +### public deimos::storage::Storage::~Storage () + +default destructor + + + + + + + + +#### Qualifiers: +* inline +* virtual + + +[Go to Top](#deimos-storage-Storage) + +[deimos-storage-Phobos_file]:./Phobos_file.md diff --git a/doc/Markdown/deimos/storage/index.md b/doc/Markdown/deimos/storage/index.md new file mode 100644 index 0000000000000000000000000000000000000000..fabe07cabbae243d8e1f519c96bd55e998ac1000 --- /dev/null +++ b/doc/Markdown/deimos/storage/index.md @@ -0,0 +1,19 @@ +# deimos::storage + +namespace for storage classes that belong to / inherit from from [deimos::storage::Storage][deimos-storage-Storage] + + + + +## Classes +| Name | Description | +| ---- | ---- | +| [Phobos_file](./Phobos_file.md) | Phobos specific mplementation of the [Storage][deimos-storage-Storage] class. | +| [PhobosException](./PhobosException.md) | exceptions specifically for the phobos backend library | +| [Storage](./Storage.md) | virtual storage class to be implemented by backend storage | + + + +[deimos-DeimosException]:./../DeimosException.md +[deimos-storage-Phobos_file]:./Phobos_file.md +[deimos-storage-Storage]:./Storage.md diff --git a/doc/Markdown/deimos/stream/Fifo.md b/doc/Markdown/deimos/stream/Fifo.md new file mode 100644 index 0000000000000000000000000000000000000000..a9b2c395a40c7b1ec7417632052340141c227551 --- /dev/null +++ b/doc/Markdown/deimos/stream/Fifo.md @@ -0,0 +1,487 @@ +# public deimos::stream::Fifo + +FIFO specific implementation of the [Stream][deimos-stream-Stream] class. + + + +This class uses a fifo to stream data from the server to a backend storage class. Therefore, all data gets written to the fifo and the implementation of the internal [storage::Storage][deimos-storage-Storage] class can read and process it from the other end of the fifo. + + + + +## Inheritance: +Inherits from [deimos::stream::Stream][deimos-stream-Stream]. + +## Implementation specific details +| Name | Description | +| ---- | ---- | +| [get_fifo_descriptors](#deimos-stream-Fifo-get_fifo_descriptors) | get the descriptor of the fifo | + + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_fifo_descriptor[2]](#deimos-stream-Fifo-m_fifo_descriptor) | the file descriptors for the FIFO. | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [Fifo](#deimos-stream-Fifo-Fifo) | default constructor | +| [~Fifo](#deimos-stream-Fifo-~Fifo) | default destructor | +| [Fifo](#deimos-stream-Fifo-Fifo-1) | move contructor | +| [operator=](#deimos-stream-Fifo-operator=) | assignment operator for rvalue references | +| [Fifo](#deimos-stream-Fifo-Fifo-2) | copy constructor deleted | +| [operator=](#deimos-stream-Fifo-operator=-1) | assignment operator deleted | +| [set_storage_meta_data](#deimos-stream-Fifo-set_storage_meta_data) | setting the metadata that the created object should get | +| [get_meta_data](#deimos-stream-Fifo-get_meta_data) | get the metadata associated to the current object as a map of key:value pairs | +| [start_put](#deimos-stream-Fifo-start_put) | start a put operation | +| [put](#deimos-stream-Fifo-put) | add a chunk of data to the object | +| [start_get](#deimos-stream-Fifo-start_get) | start a gett operation | +| [get](#deimos-stream-Fifo-get) | gets a chunk of data of the object | +| [finish_io](#deimos-stream-Fifo-finish_io) | end an I/O operation | + + +## Private Functions +| Name | Description | +| ---- | ---- | +| [create_fifo](#deimos-stream-Fifo-create_fifo) | create a fifo by using the pipe command on the m_fifo_descriptor | + + + +## Implementation specific details +### public const int * deimos::stream::Fifo::get_fifo_descriptors () const + +get the descriptor of the fifo + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| const int * | the descriptor of the fifo | + + + + + + + + + + +> **[Todo][todo]:** Do we use this? should this really be public? + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-stream-Fifo) + +## Private Attributes +### private deimos::stream::Fifo::m_fifo_descriptor[2] [2] = {-1, -1} + +the file descriptors for the FIFO. + + + + + + + + + + + +- Index 0 for read end + +- Index 1 for write end + + + + +[Go to Top](#deimos-stream-Fifo) + +## Public Functions +### public deimos::stream::Fifo::Fifo (std::unique_ptr< storage::Storage > input_storage) + +default constructor + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< [storage::Storage][deimos-storage-Storage] > | input_storage | | + + + + + +[Go to Top](#deimos-stream-Fifo) + +### public deimos::stream::Fifo::~Fifo () + +default destructor + + + + + + + + +[Go to Top](#deimos-stream-Fifo) + +### public deimos::stream::Fifo::Fifo (Fifo &&input) + +move contructor + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| [Fifo][deimos-stream-Fifo] && | input | the other object to be moved | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-stream-Fifo) + +### public [Fifo][deimos-stream-Fifo] & deimos::stream::Fifo::operator= (Fifo &&input) + +assignment operator for rvalue references + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| [Fifo][deimos-stream-Fifo] && | input | the other object to assign | + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Fifo][deimos-stream-Fifo] & | reference to newly created object | + + + + + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-stream-Fifo) + +### public deimos::stream::Fifo::Fifo (const Fifo &)=delete + +copy constructor deleted + + + + + + + + +[Go to Top](#deimos-stream-Fifo) + +### public [Fifo][deimos-stream-Fifo] & deimos::stream::Fifo::operator= (const Fifo &)=delete + +assignment operator deleted + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| [Fifo][deimos-stream-Fifo] & | | + + + + + +[Go to Top](#deimos-stream-Fifo) + +### public void deimos::stream::Fifo::set_storage_meta_data (std::map< std::string, std::string > meta_data) const override + +setting the metadata that the created object should get + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::map< std::string, std::string > | meta_data | a map of all key:value pairs that should be added to the data | + + + + + + + + + +> **warning:** this function is responsible for getting metadata to the storage implementation + + + + +Just passes the data along to the backend + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public std::map< std::string, std::string > deimos::stream::Fifo::get_meta_data (std::string object_id) const override + +get the metadata associated to the current object as a map of key:value pairs + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | The object id of the requested object | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::map< std::string, std::string > | a map of the key:value metadata pairs | + + + + + + + + + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public void deimos::stream::Fifo::start_put (ssize_t size, std::string object_id) override + +start a put operation + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| ssize_t | size | amount of bytes that shall be added to the internal storage | +| std::string | object_id | the identifier the object should get | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public ssize_t deimos::stream::Fifo::put (const void *data, size_t count) const override + +add a chunk of data to the object + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const void * | data | the chunked data to add | +| size_t | count | the size of the buffer / chunk | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the amount of bytes written to internal storage | + + + + + + + +This function can be called repeatedly until the server's message body is depleted + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public void deimos::stream::Fifo::start_get (std::string object_id) override + +start a gett operation + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | the identifier of the object to retrieve | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public ssize_t deimos::stream::Fifo::get (void *buf, size_t count) const override + +gets a chunk of data of the object + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| void * | buf | a buffer for the data to be filled in this function | +| size_t | count | the size of the buffer / chunk | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the number of bytes the buffer has been filled with | + + + + + + + + + + + +Repeatedly called to read from FIFO into buffer until the FIFO is empty and closed on the writing end + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +### public void deimos::stream::Fifo::finish_io () override + +end an I/O operation + + + + + + + + + + +This will throw a [storage::PhobosException][deimos-storage-PhobosException] in case an error occurred in +[storage::Phobos_file][deimos-storage-Phobos_file] during I/O. + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Fifo) + +## Private Functions +### private void deimos::stream::Fifo::create_fifo () + +create a fifo by using the pipe command on the m_fifo_descriptor + + + + + + + + +[Go to Top](#deimos-stream-Fifo) + +[deimos-storage-PhobosException]:./../storage/PhobosException.md +[deimos-storage-Phobos_file]:./../storage/Phobos_file.md +[deimos-storage-Storage]:./../storage/Storage.md +[deimos-stream-Fifo]:./Fifo.md +[deimos-stream-Stream]:./Stream.md +[todo]:./../../todo.md#todo diff --git a/doc/Markdown/deimos/stream/Stream.md b/doc/Markdown/deimos/stream/Stream.md new file mode 100644 index 0000000000000000000000000000000000000000..3b727a3d0b7502c7be51996f016ff47de309bd94 --- /dev/null +++ b/doc/Markdown/deimos/stream/Stream.md @@ -0,0 +1,360 @@ +# public deimos::stream::Stream + +virtual [Stream][deimos-stream-Stream] class to be implemented for streaming chunks of data between the server and a backend +[storage::Storage][deimos-storage-Storage] class + + + + +## Inheritance: +Is inherited by [deimos::stream::Fifo][deimos-stream-Fifo]. + +## PUT functions +| Name | Description | +| ---- | ---- | +| [start_put](#deimos-stream-Stream-start_put) | start a put operation | +| [put](#deimos-stream-Stream-put) | add a chunk of data to the object | +| [start_get](#deimos-stream-Stream-start_get) | start a gett operation | +| [get](#deimos-stream-Stream-get) | gets a chunk of data of the object | +| [finish_io](#deimos-stream-Stream-finish_io) | end an I/O operation | + + +## Protected Attributes +| Name | Description | +| ---- | ---- | +| [m_storage](#deimos-stream-Stream-m_storage) | an implementation of [storage::Storage][deimos-storage-Storage] that will be used as a database for the objects | +| [m_db_result](#deimos-stream-Stream-m_db_result) | a future object that can be used to get the results from the asynchronous backend | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [Stream](#deimos-stream-Stream-Stream) | default constructor | +| [Stream](#deimos-stream-Stream-Stream-1) | constructor with storage implementation | +| [~Stream](#deimos-stream-Stream-~Stream) | default destructor | +| [set_storage_meta_data](#deimos-stream-Stream-set_storage_meta_data) | setting the metadata that the created object should get | +| [get_meta_data](#deimos-stream-Stream-get_meta_data) | get the metadata associated to the current object as a map of key:value pairs | + + + +## PUT functions +### public void deimos::stream::Stream::start_put (ssize_t size, std::string object_id)=0 + +start a put operation + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| ssize_t | size | amount of bytes that shall be added to the internal storage | +| std::string | object_id | the identifier the object should get | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public ssize_t deimos::stream::Stream::put (const void *data, size_t count) const =0 + +add a chunk of data to the object + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| const void * | data | the chunked data to add | +| size_t | count | the size of the buffer / chunk | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the amount of bytes written to internal storage | + + + + + + + +This function can be called repeatedly until the server's message body is depleted + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public void deimos::stream::Stream::start_get (std::string object_id)=0 + +start a gett operation + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | the identifier of the object to retrieve | + + + + + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public ssize_t deimos::stream::Stream::get (void *buf, size_t count) const =0 + +gets a chunk of data of the object + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| void * | buf | a buffer for the data to be filled in this function | +| size_t | count | the size of the buffer / chunk | + +#### Returns: +| Type | Description | +| ---- | ---- | +| ssize_t | the number of bytes the buffer has been filled with | + + + + + + + + + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public void deimos::stream::Stream::finish_io ()=0 + +end an I/O operation + + + + + + + + +#### Qualifiers: +* virtual + + +[Go to Top](#deimos-stream-Stream) + +## Protected Attributes +### protected deimos::stream::Stream::m_storage + +an implementation of [storage::Storage][deimos-storage-Storage] that will be used as a database for the objects + + + + + + + + +[Go to Top](#deimos-stream-Stream) + +### protected deimos::stream::Stream::m_db_result + +a future object that can be used to get the results from the asynchronous backend + + + + + + + + + + +> **[Todo][todo]:** should this really be a protected member in the interface? Isn't it the decision of the implementation to make this asynchronous? + + + + +[Go to Top](#deimos-stream-Stream) + +## Public Functions +### public deimos::stream::Stream::Stream () + +default constructor + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-stream-Stream) + +### public deimos::stream::Stream::Stream (std::unique_ptr< storage::Storage > input_storage) + +constructor with storage implementation + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::unique_ptr< [storage::Storage][deimos-storage-Storage] > | input_storage | pointer to a implementation of the [storage::Storage][deimos-storage-Storage] class that will be used to store the data | + + + + + + + + + +> **warning:** every class that inherits this interface must have an internal [storage::Storage][deimos-storage-Storage] implementation and initialize this here. + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-stream-Stream) + +### public deimos::stream::Stream::~Stream () + +default destructor + + + + + + + + +#### Qualifiers: +* inline +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public void deimos::stream::Stream::set_storage_meta_data (std::map< std::string, std::string > meta_data) const =0 + +setting the metadata that the created object should get + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::map< std::string, std::string > | meta_data | a map of all key:value pairs that should be added to the data | + + + + + + + + + +> **warning:** this function is responsible for getting metadata to the storage implementation + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Stream) + +### public std::map< std::string, std::string > deimos::stream::Stream::get_meta_data (std::string object_id) const =0 + +get the metadata associated to the current object as a map of key:value pairs + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| std::string | object_id | The object id of the requested object | + +#### Returns: +| Type | Description | +| ---- | ---- | +| std::map< std::string, std::string > | a map of the key:value metadata pairs | + + + + + + + + + + + + +#### Qualifiers: +* const +* virtual + + +[Go to Top](#deimos-stream-Stream) + +[deimos-storage-Storage]:./../storage/Storage.md +[deimos-stream-Fifo]:./Fifo.md +[deimos-stream-Stream]:./Stream.md +[todo]:./../../todo.md#todo diff --git a/doc/Markdown/deimos/stream/index.md b/doc/Markdown/deimos/stream/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ae0a04490d32174e50126d1f7f1dd2070a1030ff --- /dev/null +++ b/doc/Markdown/deimos/stream/index.md @@ -0,0 +1,19 @@ +# deimos::stream + +namespace for stream classes that belong to / inherit from from [deimos::stream::Stream][deimos-stream-Stream] + + + + +## Classes +| Name | Description | +| ---- | ---- | +| [Fifo](./Fifo.md) | FIFO specific implementation of the [Stream][deimos-stream-Stream] class. | +| [Stream](./Stream.md) | virtual [Stream][deimos-stream-Stream] class to be implemented for streaming chunks of data between the server and a backend +[storage::Storage][deimos-storage-Storage] class | + + + +[deimos-storage-Storage]:./../storage/Storage.md +[deimos-stream-Fifo]:./Fifo.md +[deimos-stream-Stream]:./Stream.md diff --git a/doc/Markdown/deimos/util/Config.md b/doc/Markdown/deimos/util/Config.md new file mode 100644 index 0000000000000000000000000000000000000000..8a3e1c9f117f91ee73e04b3ef844cb93df02f2bb --- /dev/null +++ b/doc/Markdown/deimos/util/Config.md @@ -0,0 +1,376 @@ +# public deimos::util::Config + +class for handling command line arguments and configuration files + + + + +## Private Attributes +| Name | Description | +| ---- | ---- | +| [m_parser](#deimos-util-Config-m_parser) | CLI11 class to handle parsing. | +| [m_start](#deimos-util-Config-m_start) | CLI11 class for start subcommand. | +| [m_stop](#deimos-util-Config-m_stop) | CLI11 class for stop subcommand. | +| [m_restart](#deimos-util-Config-m_restart) | CLI11 class for restart subcommand. | +| [m_http_port](#deimos-util-Config-m_http_port) | port to listen on with HTTP protocol | +| [m_http2_port](#deimos-util-Config-m_http2_port) | port to listen on with HTTP2 protocol | +| [m_hostname](#deimos-util-Config-m_hostname) | IP/Hostname to bind to. | +| [m_thread_count](#deimos-util-Config-m_thread_count) | Number of threads to listen on. | +| [m_config_filename](#deimos-util-Config-m_config_filename) | filename to write configuration options to | +| [m_log_filename](#deimos-util-Config-m_log_filename) | filename to write logs to | + + +## Public Functions +| Name | Description | +| ---- | ---- | +| [Config](#deimos-util-Config-Config) | default constructor | +| [~Config](#deimos-util-Config-~Config) | default destructor | +| [parse](#deimos-util-Config-parse) | parse command line arguments | +| [start_daemon](#deimos-util-Config-start_daemon) | query if the server daemon should be started | +| [stop_daemon](#deimos-util-Config-stop_daemon) | query if the server daemon should be stopped | +| [restart_daemon](#deimos-util-Config-restart_daemon) | query if the server daemon should be restarted | + + +## Private Functions +| Name | Description | +| ---- | ---- | +| [define_options](#deimos-util-Config-define_options) | define the supported command line options | +| [handle_arguments](#deimos-util-Config-handle_arguments) | perform any required adjustments to options | + + + +## Private Attributes +### private deimos::util::Config::m_parser + +CLI11 class to handle parsing. + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_start + +CLI11 class for start subcommand. + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_stop + +CLI11 class for stop subcommand. + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_restart + +CLI11 class for restart subcommand. + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_http_port = 11000 + +port to listen on with HTTP protocol + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_http2_port = 11002 + +port to listen on with HTTP2 protocol + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_hostname + +IP/Hostname to bind to. + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_thread_count = 0 + +Number of threads to listen on. + + + + + + + + + + + +- default 0 uses the number of available cores + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_config_filename + +filename to write configuration options to + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private deimos::util::Config::m_log_filename = "/tmp/deimos_log.txt" + +filename to write logs to + + + + + + + + +[Go to Top](#deimos-util-Config) + +## Public Functions +### public deimos::util::Config::Config () + +default constructor + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-util-Config) + +### public deimos::util::Config::~Config () + +default destructor + + + + + + + + +#### Qualifiers: +* inline + + +[Go to Top](#deimos-util-Config) + +### public [cli_options][deimos-util-cli_options] deimos::util::Config::parse (int argc, char *argv[]) + +parse command line arguments + + + + +#### Parameters: +| Type | Name | Description | +| ---- | ---- | ---- | +| int | argc | number of command line arguments | +| char * | argv | array of command line arguments | + +#### Returns: +| Type | Description | +| ---- | ---- | +| [cli_options][deimos-util-cli_options] | struct of command line options or error code | + + + + + + + + + + + + +[Go to Top](#deimos-util-Config) + +### public bool deimos::util::Config::start_daemon () const + +query if the server daemon should be started + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | whether the start subcommand has been parsed | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-util-Config) + +### public bool deimos::util::Config::stop_daemon () const + +query if the server daemon should be stopped + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | whether the stop subcommand has been parsed | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-util-Config) + +### public bool deimos::util::Config::restart_daemon () const + +query if the server daemon should be restarted + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| bool | whether the restart subcommand has been parsed | + + + + + + + + + + + + +#### Qualifiers: +* const +* inline + + +[Go to Top](#deimos-util-Config) + +## Private Functions +### private void deimos::util::Config::define_options () + +define the supported command line options + + + + + + + + +[Go to Top](#deimos-util-Config) + +### private [cli_options][deimos-util-cli_options] deimos::util::Config::handle_arguments () + +perform any required adjustments to options + + + + +#### Returns: +| Type | Description | +| ---- | ---- | +| [cli_options][deimos-util-cli_options] | struct of command line options or error code | + + + + + + + + + + + + +[Go to Top](#deimos-util-Config) + +[deimos-util-cli_options]:./cli_options.md#deimos-util-cli_options diff --git a/doc/Markdown/deimos/util/cli_options.md b/doc/Markdown/deimos/util/cli_options.md new file mode 100644 index 0000000000000000000000000000000000000000..642bda407c4f8408d0e1372c1a0ba144c72715a9 --- /dev/null +++ b/doc/Markdown/deimos/util/cli_options.md @@ -0,0 +1,8 @@ +# public deimos::util::cli_options + +struct for conveniently passing command line options to main program + + + + + diff --git a/doc/Markdown/deimos/util/index.md b/doc/Markdown/deimos/util/index.md new file mode 100644 index 0000000000000000000000000000000000000000..563f112c254b7dedace331d1f7dfcb5ff4a4a987 --- /dev/null +++ b/doc/Markdown/deimos/util/index.md @@ -0,0 +1,15 @@ +# deimos::util + +namespace for utility classes + + + + +## Classes +| Name | Description | +| ---- | ---- | +| [cli_options](./cli_options.md) | struct for conveniently passing command line options to main program | +| [Config](./Config.md) | class for handling command line arguments and configuration files | + + + diff --git a/doc/Markdown/todo.md b/doc/Markdown/todo.md new file mode 100644 index 0000000000000000000000000000000000000000..3002ed7e89654c3bc1e748db25f812735edb4474 --- /dev/null +++ b/doc/Markdown/todo.md @@ -0,0 +1,21 @@ +# Todo List + + + +* Member +[deimos::GetRequestHandler::requestComplete][deimos-GetRequestHandler-requestComplete] () noexcept override + * find out what this does? +* Member +[deimos::stream::Fifo::get_fifo_descriptors][deimos-stream-Fifo-get_fifo_descriptors] () const + * Do we use this? should this really be public? +* Member +[deimos::stream::Stream::m_db_result][deimos-stream-Stream-m_db_result] + * should this really be a protected member in the interface? Isn't it the decision of the implementation to make this asynchronous? + + + + + +[deimos-GetRequestHandler-requestComplete]:./deimos/GetRequestHandler.md#deimos-GetRequestHandler-requestComplete +[deimos-stream-Fifo-get_fifo_descriptors]:./deimos/stream/Fifo.md#deimos-stream-Fifo-get_fifo_descriptors +[deimos-stream-Stream-m_db_result]:./deimos/stream/Stream.md#deimos-stream-Stream-m_db_result diff --git a/doc/openapi.yaml b/doc/openapi.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b40be5ed5a806b95da72c0a37213e86a9dabd817 --- /dev/null +++ b/doc/openapi.yaml @@ -0,0 +1,283 @@ +openapi: 3.0.1 +info: + title: Deimos + description: >- + Server to accept S3 requests to store and retrieve data and use the phobos + tape library as storage. + version: 1.0.1 +tags: + - name: bucket + description: Operations on the buckets + - name: object + description: Operations on the objects inside buckets +servers: + - url: 'http://localhost:11000/' + description: Local host server for testing purposes +paths: + '/{bucket}': + parameters: + - name: bucket + in: path + description: Name of the bucket + required: true + schema: + type: string + minimum: 1 + get: + tags: + - bucket + summary: Lists all objects in the bucket (not yet implemented) + operationId: getBucket + responses: + '200': + description: Success + content: + application/xml: + schema: + $ref: '#/components/schemas/ListObjectsV2Output' + '403': + description: Access denied + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + + '404': + description: No such bucket + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + put: + tags: + - bucket + summary: Creates a new bucket (not yet implemented) + operationId: putBucket + requestBody: + description: Information on the bucket that should be created + content: + application/xml: + schema: + $ref: '#/components/schemas/CreateBucketInfo' + required: true + responses: + '200': + description: Success + '403': + description: Access denied + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '/{bucket}/{key}': + parameters: + - name: bucket + in: path + description: Name of the bucket + required: true + schema: + type: string + minimum: 1 + - name: key + in: path + description: Identifier of the object to store + required: true + schema: + type: string + minimum: 1 + get: + tags: + - object + summary: Retrieves an object from the storage + operationId: getObject + description: >- + The object `key` will be searched in bucket `bucket` and the contents + returned in chunks to the user + responses: + '200': + description: Success + content: + application/text: + schema: + type: string + '403': + description: Access denied + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not found (No such key) + content: + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - example: + Code: NoSuchKey + Message: The resource you requested does not exist! + Resource: key + '500': + description: Internal Server Error + content: + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - example: + Code: InternalServerPhobosError + Message: The Phobos database is not running correctly on the server + Resource: key + put: + tags: + - object + summary: Add an object to the storage + operationId: putObject + description: >- + The contents of the body will be added as a new object with id `key` to + the bucket `bucket` + parameters: + - in: header + name: Content-Length + schema: + type: integer + required: true + requestBody: + description: File contents to add + content: + application/text: + schema: + type: string + required: true + responses: + '100': + description: Continue + '200': + description: Success + '400': + description: Bad Request + content: + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - example: + Code: InvalidArgument + Message: Some arguments given to the storage were invalid! + Resource: key + '403': + description: Access denied + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - example: + Code: InternalServerPhobosError + Message: The Phobos database is not running correctly on the server + Resource: key + delete: + tags: + - object + summary: Delete an object + operationId: deleteObject + description: Deletes an object from the internal storage (not yet implemented) + responses: + '204': + description: No Content + '403': + description: Access denied + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' + '501': + description: Not Implemented + content: + application/xml: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + CreateBucketInfo: + type: object + properties: + xmlns: + type: string + default: 'http://s3.amazonaws.com/doc/2006-03-01/' + xml: + attribute: true + LocationContraint: + type: string + xml: + name: CreateBucketConfiguration + Error: + type: object + properties: + Code: + type: string + Message: + type: string + Resouce: + type: string + xml: + name: Error + ListObjectsV2Output: + type: object + properties: + IsTruncated: + type: boolean + Contents: + type: array + items: + properties: + ETag: + type: string + Key: + type: string + LastModified: + type: string + Owner: + properties: + DisplayName: + type: string + ID: + type: string + Size: + type: integer + Name: + type: string + Delimiter: + type: string + MaxKeys: + type: string + EncodingType: + type: string + KeyCount: + type: string + xml: + name: ListObjectsV2Output \ No newline at end of file diff --git a/include/CLI/CLI11.hpp b/include/CLI/CLI11.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8f958076a811278c37860a3b64dd03b62ad07c6d --- /dev/null +++ b/include/CLI/CLI11.hpp @@ -0,0 +1,8223 @@ +#pragma once + +// CLI11: Version 1.9.0 +// Originally designed by Henry Schreiner +// https://github.com/CLIUtils/CLI11 +// +// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts +// from: v1.9.0 +// +// From LICENSE: +// +// CLI11 1.8 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry +// Schreiner under NSF AWARD 1414736. All rights reserved. +// +// Redistribution and use in source and binary forms of CLI11, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Standard combined includes: + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Verbatim copy from CLI/Version.hpp: + + +#define CLI11_VERSION_MAJOR 1 +#define CLI11_VERSION_MINOR 9 +#define CLI11_VERSION_PATCH 0 +#define CLI11_VERSION "1.9.0" + + + + +// Verbatim copy from CLI/Macros.hpp: + + +// The following version macro is very similar to the one in PyBind11 +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +#if __cplusplus >= 201402L +#define CLI11_CPP14 +#if __cplusplus >= 201703L +#define CLI11_CPP17 +#if __cplusplus > 201703L +#define CLI11_CPP20 +#endif +#endif +#endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +#if _MSVC_LANG >= 201402L +#define CLI11_CPP14 +#if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +#define CLI11_CPP17 +#if __MSVC_LANG > 201703L && _MSC_VER >= 1910 +#define CLI11_CPP20 +#endif +#endif +#endif +#endif + +#if defined(CLI11_CPP14) +#define CLI11_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(_MSC_VER) +#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + + + + +// Verbatim copy from CLI/Validators.hpp: + + +// C standard library +// Only needed for existence checking +#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM +#if __has_include() +// Filesystem cannot be used if targeting macOS < 10.15 +#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 +#define CLI11_HAS_FILESYSTEM 0 +#else +#include +#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703 +#define CLI11_HAS_FILESYSTEM 1 +#else +#define CLI11_HAS_FILESYSTEM 0 +#endif +#endif +#endif +#endif + +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +#include // NOLINT(build/include) +#else +#include +#include +#endif + + + +// From CLI/Version.hpp: + + + +// From CLI/Macros.hpp: + + + +// From CLI/StringTools.hpp: + +namespace CLI { + +/// Include the items in this namespace to get free conversion of enums to/from streams. +/// (This is available inside CLI as well, so CLI11 will use this without a using statement). +namespace enums { + +/// output streaming for enumerations +template ::value>::type> +std::ostream &operator<<(std::ostream &in, const T &item) { + // make sure this is out of the detail namespace otherwise it won't be found when needed + return in << static_cast::type>(item); +} + +} // namespace enums + +/// Export to CLI namespace +using enums::operator<<; + +namespace detail { +/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not +/// produce overflow for some expected uses +constexpr int expected_max_vector_size{1 << 29}; +// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c +/// Split a string by a delim +inline std::vector split(const std::string &s, char delim) { + std::vector elems; + // Check to see if empty string, give consistent result + if(s.empty()) + elems.emplace_back(); + else { + std::stringstream ss; + ss.str(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); + } + } + return elems; +} + +/// Simple function to join a string +template std::string join(const T &v, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + if(beg != end) + s << *beg++; + while(beg != end) { + s << delim << *beg++; + } + return s.str(); +} + +/// Simple function to join a string from processed elements +template ::value>::type> +std::string join(const T &v, Callable func, std::string delim = ",") { + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + if(beg != end) + s << func(*beg++); + while(beg != end) { + s << delim << func(*beg++); + } + return s.str(); +} + +/// Join a string in reverse order +template std::string rjoin(const T &v, std::string delim = ",") { + std::ostringstream s; + for(std::size_t start = 0; start < v.size(); start++) { + if(start > 0) + s << delim; + s << v[v.size() - start - 1]; + } + return s.str(); +} + +// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string + +/// Trim whitespace from left of string +inline std::string <rim(std::string &str) { + auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(str.begin(), it); + return str; +} + +/// Trim anything from left of string +inline std::string <rim(std::string &str, const std::string &filter) { + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(str.begin(), it); + return str; +} + +/// Trim whitespace from right of string +inline std::string &rtrim(std::string &str) { + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(it.base(), str.end()); + return str; +} + +/// Trim anything from right of string +inline std::string &rtrim(std::string &str, const std::string &filter) { + auto it = + std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(it.base(), str.end()); + return str; +} + +/// Trim whitespace from string +inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); } + +/// Trim anything from string +inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } + +/// Make a copy of the string and then trim it +inline std::string trim_copy(const std::string &str) { + std::string s = str; + return trim(s); +} + +/// remove quotes at the front and back of a string either '"' or '\'' +inline std::string &remove_quotes(std::string &str) { + if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); + } + } + return str; +} + +/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) +inline std::string trim_copy(const std::string &str, const std::string &filter) { + std::string s = str; + return trim(s, filter); +} +/// Print a two part "help" string +inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, std::size_t wid) { + name = " " + name; + out << std::setw(static_cast(wid)) << std::left << name; + if(!description.empty()) { + if(name.length() >= wid) + out << "\n" << std::setw(static_cast(wid)) << ""; + for(const char c : description) { + out.put(c); + if(c == '\n') { + out << std::setw(static_cast(wid)) << ""; + } + } + } + out << "\n"; + return out; +} + +/// Verify the first character of an option +template bool valid_first_char(T c) { + return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@'; +} + +/// Verify following characters of an option +template bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; } + +/// Verify an option name +inline bool valid_name_string(const std::string &str) { + if(str.empty() || !valid_first_char(str[0])) + return false; + for(auto c : str.substr(1)) + if(!valid_later_char(c)) + return false; + return true; +} + +/// Verify that str consists of letters only +inline bool isalpha(const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); }); +} + +/// Return a lower case version of a string +inline std::string to_lower(std::string str) { + std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) { + return std::tolower(x, std::locale()); + }); + return str; +} + +/// remove underscores from a string +inline std::string remove_underscore(std::string str) { + str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); + return str; +} + +/// Find and replace a substring with another substring +inline std::string find_and_replace(std::string str, std::string from, std::string to) { + + std::size_t start_pos = 0; + + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + + return str; +} + +/// check if the flag definitions has possible false flags +inline bool has_default_flag_values(const std::string &flags) { + return (flags.find_first_of("{!") != std::string::npos); +} + +inline void remove_default_flag_values(std::string &flags) { + auto loc = flags.find_first_of('{'); + while(loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), + flags.begin() + static_cast(finish) + 1); + } + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); +} + +/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores +inline std::ptrdiff_t find_member(std::string name, + const std::vector names, + bool ignore_case = false, + bool ignore_underscore = false) { + auto it = std::end(names); + if(ignore_case) { + if(ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == name; + }); + } else { + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); + } + + } else if(ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } else + it = std::find(std::begin(names), std::end(names), name); + + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); +} + +/// Find a trigger string and call a modify callable function that takes the current string and starting position of the +/// trigger and returns the position in the string to search for the next trigger string +template inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) { + std::size_t start_pos = 0; + while((start_pos = str.find(trigger, start_pos)) != std::string::npos) { + start_pos = modify(str, start_pos); + } + return str; +} + +/// Split a string '"one two" "three"' into 'one two', 'three' +/// Quote characters can be ` ' or " +inline std::vector split_up(std::string str, char delimiter = '\0') { + + const std::string delims("\'\"`"); + auto find_ws = [delimiter](char ch) { + return (delimiter == '\0') ? (std::isspace(ch, std::locale()) != 0) : (ch == delimiter); + }; + trim(str); + + std::vector output; + bool embeddedQuote = false; + char keyChar = ' '; + while(!str.empty()) { + if(delims.find_first_of(str[0]) != std::string::npos) { + keyChar = str[0]; + auto end = str.find_first_of(keyChar, 1); + while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes + end = str.find_first_of(keyChar, end + 1); + embeddedQuote = true; + } + if(end != std::string::npos) { + output.push_back(str.substr(1, end - 1)); + str = str.substr(end + 1); + } else { + output.push_back(str.substr(1)); + str = ""; + } + } else { + auto it = std::find_if(std::begin(str), std::end(str), find_ws); + if(it != std::end(str)) { + std::string value = std::string(str.begin(), it); + output.push_back(value); + str = std::string(it + 1, str.end()); + } else { + output.push_back(str); + str = ""; + } + } + // transform any embedded quotes into the regular character + if(embeddedQuote) { + output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar)); + embeddedQuote = false; + } + trim(str); + } + return output; +} + +/// Add a leader to the beginning of all new lines (nothing is added +/// at the start of the first line). `"; "` would be for ini files +/// +/// Can't use Regex, or this would be a subs. +inline std::string fix_newlines(const std::string &leader, std::string input) { + std::string::size_type n = 0; + while(n != std::string::npos && n < input.size()) { + n = input.find('\n', n); + if(n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); + } + } + return input; +} + +/// This function detects an equal or colon followed by an escaped quote after an argument +/// then modifies the string to replace the equality with a space. This is needed +/// to allow the split up function to work properly and is intended to be used with the find_and_modify function +/// the return value is the offset+1 which is required by the find_and_modify function. +inline std::size_t escape_detect(std::string &str, std::size_t offset) { + auto next = str[offset + 1]; + if((next == '\"') || (next == '\'') || (next == '`')) { + auto astart = str.find_last_of("-/ \"\'`", offset - 1); + if(astart != std::string::npos) { + if(str[astart] == ((str[offset] == '=') ? '-' : '/')) + str[offset] = ' '; // interpret this as a space so the split_up works properly + } + } + return offset + 1; +} + +/// Add quotes if the string contains spaces +inline std::string &add_quotes_if_needed(std::string &str) { + if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) { + char quote = str.find('"') < str.find('\'') ? '\'' : '"'; + if(str.find(' ') != std::string::npos) { + str.insert(0, 1, quote); + str.append(1, quote); + } + } + return str; +} + +} // namespace detail + +} // namespace CLI + +// From CLI/Error.hpp: + +namespace CLI { + +// Use one of these on all error classes. +// These are temporary and are undef'd at the end of this file. +#define CLI11_ERROR_DEF(parent, name) \ + protected: \ + name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ + name(std::string ename, std::string msg, ExitCodes exit_code) \ + : parent(std::move(ename), std::move(msg), exit_code) {} \ + \ + public: \ + name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ + name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} + +// This is added after the one above if a class is used directly and builds its own message +#define CLI11_ERROR_SIMPLE(name) \ + explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} + +/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, +/// int values from e.get_error_code(). +enum class ExitCodes { + Success = 0, + IncorrectConstruction = 100, + BadNameString, + OptionAlreadyAdded, + FileError, + ConversionError, + ValidationError, + RequiredError, + RequiresError, + ExcludesError, + ExtrasError, + ConfigError, + InvalidError, + HorribleError, + OptionNotFound, + ArgumentMismatch, + BaseClass = 127 +}; + +// Error definitions + +/// @defgroup error_group Errors +/// @brief Errors thrown by CLI11 +/// +/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. +/// @{ + +/// All errors derive from this one +class Error : public std::runtime_error { + int actual_exit_code; + std::string error_name{"Error"}; + + public: + int get_exit_code() const { return actual_exit_code; } + + std::string get_name() const { return error_name; } + + Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) + : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} + + Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast(exit_code)) {} +}; + +// Note: Using Error::Error constructors does not work on GCC 4.7 + +/// Construction errors (not in parsing) +class ConstructionError : public Error { + CLI11_ERROR_DEF(Error, ConstructionError) +}; + +/// Thrown when an option is set to conflicting values (non-vector and multi args, for example) +class IncorrectConstruction : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) + CLI11_ERROR_SIMPLE(IncorrectConstruction) + static IncorrectConstruction PositionalFlag(std::string name) { + return IncorrectConstruction(name + ": Flags cannot be positional"); + } + static IncorrectConstruction Set0Opt(std::string name) { + return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); + } + static IncorrectConstruction SetFlag(std::string name) { + return IncorrectConstruction(name + ": Cannot set an expected number for flags"); + } + static IncorrectConstruction ChangeNotVector(std::string name) { + return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); + } + static IncorrectConstruction AfterMultiOpt(std::string name) { + return IncorrectConstruction( + name + ": You can't change expected arguments after you've changed the multi option policy!"); + } + static IncorrectConstruction MissingOption(std::string name) { + return IncorrectConstruction("Option " + name + " is not defined"); + } + static IncorrectConstruction MultiOptionPolicy(std::string name) { + return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); + } +}; + +/// Thrown on construction of a bad name +class BadNameString : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, BadNameString) + CLI11_ERROR_SIMPLE(BadNameString) + static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } + static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } + static BadNameString DashesOnly(std::string name) { + return BadNameString("Must have a name, not just dashes: " + name); + } + static BadNameString MultiPositionalNames(std::string name) { + return BadNameString("Only one positional name allowed, remove: " + name); + } +}; + +/// Thrown when an option already exists +class OptionAlreadyAdded : public ConstructionError { + CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) + explicit OptionAlreadyAdded(std::string name) + : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} + static OptionAlreadyAdded Requires(std::string name, std::string other) { + return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); + } + static OptionAlreadyAdded Excludes(std::string name, std::string other) { + return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); + } +}; + +// Parsing errors + +/// Anything that can error in Parse +class ParseError : public Error { + CLI11_ERROR_DEF(Error, ParseError) +}; + +// Not really "errors" + +/// This is a successful completion on parsing, supposed to exit +class Success : public ParseError { + CLI11_ERROR_DEF(ParseError, Success) + Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} +}; + +/// -h or --help on command line +class CallForHelp : public ParseError { + CLI11_ERROR_DEF(ParseError, CallForHelp) + CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Usually something like --help-all on command line +class CallForAllHelp : public ParseError { + CLI11_ERROR_DEF(ParseError, CallForAllHelp) + CallForAllHelp() + : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} +}; + +/// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. +class RuntimeError : public ParseError { + CLI11_ERROR_DEF(ParseError, RuntimeError) + explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} +}; + +/// Thrown when parsing an INI file and it is missing +class FileError : public ParseError { + CLI11_ERROR_DEF(ParseError, FileError) + CLI11_ERROR_SIMPLE(FileError) + static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } +}; + +/// Thrown when conversion call back fails, such as when an int fails to coerce to a string +class ConversionError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConversionError) + CLI11_ERROR_SIMPLE(ConversionError) + ConversionError(std::string member, std::string name) + : ConversionError("The value " + member + " is not an allowed value for " + name) {} + ConversionError(std::string name, std::vector results) + : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} + static ConversionError TooManyInputsFlag(std::string name) { + return ConversionError(name + ": too many inputs for a flag"); + } + static ConversionError TrueFalse(std::string name) { + return ConversionError(name + ": Should be true/false or a number"); + } +}; + +/// Thrown when validation of results fails +class ValidationError : public ParseError { + CLI11_ERROR_DEF(ParseError, ValidationError) + CLI11_ERROR_SIMPLE(ValidationError) + explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} +}; + +/// Thrown when a required option is missing +class RequiredError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiredError) + explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} + static RequiredError Subcommand(std::size_t min_subcom) { + if(min_subcom == 1) { + return RequiredError("A subcommand"); + } + return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", + ExitCodes::RequiredError); + } + static RequiredError + Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) { + if((min_option == 1) && (max_option == 1) && (used == 0)) + return RequiredError("Exactly 1 option from [" + option_list + "]"); + if((min_option == 1) && (max_option == 1) && (used > 1)) { + return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + + " were given", + ExitCodes::RequiredError); + } + if((min_option == 1) && (used == 0)) + return RequiredError("At least 1 option from [" + option_list + "]"); + if(used < min_option) { + return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " + + std::to_string(used) + "were given from [" + option_list + "]", + ExitCodes::RequiredError); + } + if(max_option == 1) + return RequiredError("Requires at most 1 options be given from [" + option_list + "]", + ExitCodes::RequiredError); + + return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " + + std::to_string(used) + "were given from [" + option_list + "]", + ExitCodes::RequiredError); + } +}; + +/// Thrown when the wrong number of arguments has been received +class ArgumentMismatch : public ParseError { + CLI11_ERROR_DEF(ParseError, ArgumentMismatch) + CLI11_ERROR_SIMPLE(ArgumentMismatch) + ArgumentMismatch(std::string name, int expected, std::size_t recieved) + : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + + ", got " + std::to_string(recieved)) + : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + + ", got " + std::to_string(recieved)), + ExitCodes::ArgumentMismatch) {} + + static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) { + return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); + } + static ArgumentMismatch FlagOverride(std::string name) { + return ArgumentMismatch(name + " was given a disallowed flag override"); + } +}; + +/// Thrown when a requires option is missing +class RequiresError : public ParseError { + CLI11_ERROR_DEF(ParseError, RequiresError) + RequiresError(std::string curname, std::string subname) + : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} +}; + +/// Thrown when an excludes option is present +class ExcludesError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExcludesError) + ExcludesError(std::string curname, std::string subname) + : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} +}; + +/// Thrown when too many positionals or options are found +class ExtrasError : public ParseError { + CLI11_ERROR_DEF(ParseError, ExtrasError) + explicit ExtrasError(std::vector args) + : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} + ExtrasError(const std::string &name, std::vector args) + : ExtrasError(name, + (args.size() > 1 ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} +}; + +/// Thrown when extra values are found in an INI file +class ConfigError : public ParseError { + CLI11_ERROR_DEF(ParseError, ConfigError) + CLI11_ERROR_SIMPLE(ConfigError) + static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } + static ConfigError NotConfigurable(std::string item) { + return ConfigError(item + ": This option is not allowed in a configuration file"); + } +}; + +/// Thrown when validation fails before parsing +class InvalidError : public ParseError { + CLI11_ERROR_DEF(ParseError, InvalidError) + explicit InvalidError(std::string name) + : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { + } +}; + +/// This is just a safety check to verify selection and parsing match - you should not ever see it +/// Strings are directly added to this error, but again, it should never be seen. +class HorribleError : public ParseError { + CLI11_ERROR_DEF(ParseError, HorribleError) + CLI11_ERROR_SIMPLE(HorribleError) +}; + +// After parsing + +/// Thrown when counting a non-existent option +class OptionNotFound : public Error { + CLI11_ERROR_DEF(Error, OptionNotFound) + explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} +}; + +#undef CLI11_ERROR_DEF +#undef CLI11_ERROR_SIMPLE + +/// @} + +} // namespace CLI + +// From CLI/TypeTools.hpp: + +namespace CLI { + +// Type tools + +// Utilities for type enabling +namespace detail { +// Based generally on https://rmf.io/cxx11/almost-static-if +/// Simple empty scoped class +enum class enabler {}; + +/// An instance to use in EnableIf +constexpr enabler dummy = {}; +} // namespace detail + +/// A copy of enable_if_t from C++14, compatible with C++11. +/// +/// We could check to see if C++14 is being used, but it does not hurt to redefine this +/// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h) +/// It is not in the std namespace anyway, so no harm done. +template using enable_if_t = typename std::enable_if::type; + +/// A copy of std::void_t from C++17 (helper for C++11 and C++14) +template struct make_void { using type = void; }; + +/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine +template using void_t = typename make_void::type; + +/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine +template using conditional_t = typename std::conditional::type; + +/// Check to see if something is a vector (fail check by default) +template struct is_vector : std::false_type {}; + +/// Check to see if something is a vector (true if actually a vector) +template struct is_vector> : std::true_type {}; + +/// Check to see if something is a vector (true if actually a const vector) +template struct is_vector> : std::true_type {}; + +/// Check to see if something is bool (fail check by default) +template struct is_bool : std::false_type {}; + +/// Check to see if something is bool (true if actually a bool) +template <> struct is_bool : std::true_type {}; + +/// Check to see if something is a shared pointer +template struct is_shared_ptr : std::false_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is a shared pointer (True if really a shared pointer) +template struct is_shared_ptr> : std::true_type {}; + +/// Check to see if something is copyable pointer +template struct is_copyable_ptr { + static bool const value = is_shared_ptr::value || std::is_pointer::value; +}; + +/// This can be specialized to override the type deduction for IsMember. +template struct IsMemberType { using type = T; }; + +/// The main custom type needed here is const char * should be a string. +template <> struct IsMemberType { using type = std::string; }; + +namespace detail { + +// These are utilities for IsMember and other transforming objects + +/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that +/// pointer_traits be valid. + +/// not a pointer +template struct element_type { using type = T; }; + +template struct element_type::value>::type> { + using type = typename std::pointer_traits::element_type; +}; + +/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of +/// the container +template struct element_value_type { using type = typename element_type::type::value_type; }; + +/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. +template struct pair_adaptor : std::false_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } +}; + +/// Adaptor for map-like structure (true version, must have key_type and mapped_type). +/// This wraps a mapped container in a few utilities access it in a general way. +template +struct pair_adaptor< + T, + conditional_t, void>> + : std::true_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward(pair_value))) { + return std::get<0>(std::forward(pair_value)); + } + /// Get the second value (really just the underlying value) + template static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward(pair_value))) { + return std::get<1>(std::forward(pair_value)); + } +}; + +// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning +// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in +// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a +// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. +// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be +// suppressed +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" +#endif +// check for constructibility from a specific type and copy assignable used in the parse detection +template class is_direct_constructible { + template + static auto test(int, std::true_type) -> decltype( +// NVCC warns about narrowing conversions here +#ifdef __CUDACC__ +#pragma diag_suppress 2361 +#endif + TT { std::declval() } +#ifdef __CUDACC__ +#pragma diag_default 2361 +#endif + , + std::is_move_assignable()); + + template static auto test(int, std::false_type) -> std::false_type; + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; +}; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// Check for output streamability +// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream + +template class is_ostreamable { + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Check for input streamability +template class is_istreamable { + template + static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Templated operation to get a value from a stream +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string &istring, T &obj) { + std::istringstream is; + is.str(istring); + is >> obj; + return !is.fail() && !is.rdbuf()->in_avail(); +} + +template ::value, detail::enabler> = detail::dummy> +bool from_stream(const std::string & /*istring*/, T & /*obj*/) { + return false; +} + +// Check for tuple like types, as in classes with a tuple_size type trait +template class is_tuple_like { + template + // static auto test(int) + // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); + static auto test(int) -> decltype(std::tuple_size::value, std::true_type{}); + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; + +/// Convert an object to a string (directly forward if this can become a string) +template ::value, detail::enabler> = detail::dummy> +auto to_string(T &&value) -> decltype(std::forward(value)) { + return std::forward(value); +} + +/// Convert an object to a string (streaming must be supported for that type) +template ::value && is_ostreamable::value, detail::enabler> = + detail::dummy> +std::string to_string(T &&value) { + std::stringstream stream; + stream << value; + return stream.str(); +} + +/// If conversion is not supported, return an empty string (streaming is not supported for that type) +template ::value && !is_ostreamable::value && + !is_vector::type>::type>::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&) { + return std::string{}; +} + +/// convert a vector to a string +template ::value && !is_ostreamable::value && + is_vector::type>::type>::value, + detail::enabler> = detail::dummy> +std::string to_string(T &&variable) { + std::vector defaults; + defaults.reserve(variable.size()); + auto cval = variable.begin(); + auto end = variable.end(); + while(cval != end) { + defaults.emplace_back(CLI::detail::to_string(*cval)); + ++cval; + } + return std::string("[" + detail::join(defaults) + "]"); +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +auto checked_to_string(T &&value) -> decltype(to_string(std::forward(value))) { + return to_string(std::forward(value)); +} + +/// special template overload +template ::value, detail::enabler> = detail::dummy> +std::string checked_to_string(T &&) { + return std::string{}; +} +/// get a string as a convertible value for arithmetic types +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(value); +} +/// get a string as a convertible value for enumerations +template ::value, detail::enabler> = detail::dummy> +std::string value_string(const T &value) { + return std::to_string(static_cast::type>(value)); +} +/// for other types just use the regular to_string function +template ::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> +auto value_string(const T &value) -> decltype(to_string(value)) { + return to_string(value); +} + +/// This will only trigger for actual void type +template struct type_count { static const int value{0}; }; + +/// Set of overloads to get the type size of an object +template struct type_count::value>::type> { + static constexpr int value{std::tuple_size::value}; +}; +/// Type size for regular object types that do not look like a tuple +template +struct type_count< + T, + typename std::enable_if::value && !is_tuple_like::value && !std::is_void::value>::type> { + static constexpr int value{1}; +}; + +/// Type size of types that look like a vector +template struct type_count::value>::type> { + static constexpr int value{is_vector::value ? expected_max_vector_size + : type_count::value}; +}; + +/// This will only trigger for actual void type +template struct expected_count { static const int value{0}; }; + +/// For most types the number of expected items is 1 +template +struct expected_count::value && !std::is_void::value>::type> { + static constexpr int value{1}; +}; +/// number of expected items in a vector +template struct expected_count::value>::type> { + static constexpr int value{expected_max_vector_size}; +}; + +// Enumeration of the different supported categorizations of objects +enum class object_category : int { + integral_value = 2, + unsigned_integral = 4, + enumeration = 6, + boolean_value = 8, + floating_point = 10, + number_constructible = 12, + double_constructible = 14, + integer_constructible = 16, + vector_value = 30, + tuple_value = 35, + // string assignable or greater used in a condition so anything string like must come last + string_assignable = 50, + string_constructible = 60, + other = 200, + +}; + +/// some type that is not otherwise recognized +template struct classify_object { + static constexpr object_category value{object_category::other}; +}; + +/// Set of overloads to classify an object according to type +template +struct classify_object::value && std::is_signed::value && + !is_bool::value && !std::is_enum::value>::type> { + static constexpr object_category value{object_category::integral_value}; +}; + +/// Unsigned integers +template +struct classify_object< + T, + typename std::enable_if::value && std::is_unsigned::value && !is_bool::value>::type> { + static constexpr object_category value{object_category::unsigned_integral}; +}; + +/// Boolean values +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::boolean_value}; +}; + +/// Floats +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::floating_point}; +}; + +/// String and similar direct assignment +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + std::is_assignable::value && !is_vector::value>::type> { + static constexpr object_category value{object_category::string_assignable}; +}; + +/// String and similar constructible and copy assignment +template +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + !std::is_assignable::value && + std::is_constructible::value && !is_vector::value>::type> { + static constexpr object_category value{object_category::string_constructible}; +}; + +/// Enumerations +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::enumeration}; +}; + +/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, +/// vectors, and enumerations +template struct uncommon_type { + using type = typename std::conditional::value && !std::is_integral::value && + !std::is_assignable::value && + !std::is_constructible::value && !is_vector::value && + !std::is_enum::value, + std::true_type, + std::false_type>::type; + static constexpr bool value = type::value; +}; + +/// Assignable from double or int +template +struct classify_object::value && type_count::value == 1 && + is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::number_constructible}; +}; + +/// Assignable from int +template +struct classify_object::value && type_count::value == 1 && + !is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::integer_constructible}; +}; + +/// Assignable from double +template +struct classify_object::value && type_count::value == 1 && + is_direct_constructible::value && + !is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::double_constructible}; +}; + +/// Tuple type +template +struct classify_object::value >= 2 && !is_vector::value) || + (is_tuple_like::value && uncommon_type::value && + !is_direct_constructible::value && + !is_direct_constructible::value)>::type> { + static constexpr object_category value{object_category::tuple_value}; +}; + +/// Vector type +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::vector_value}; +}; + +// Type name print + +/// Was going to be based on +/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template +/// But this is cleaner and works better in this case + +template ::value == object_category::integral_value || + classify_object::value == object_category::integer_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "INT"; +} + +template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "UINT"; +} + +template ::value == object_category::floating_point || + classify_object::value == object_category::number_constructible || + classify_object::value == object_category::double_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "FLOAT"; +} + +/// Print name for enumeration types +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "ENUM"; +} + +/// Print name for enumeration types +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "BOOLEAN"; +} + +/// Print for all other types +template ::value >= object_category::string_assignable, detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "TEXT"; +} + +/// Print name for single element tuple types +template ::value == object_category::tuple_value && type_count::value == 1, + detail::enabler> = detail::dummy> +inline std::string type_name() { + return type_name::type>(); +} + +/// Empty string if the index > tuple size +template +inline typename std::enable_if::value, std::string>::type tuple_name() { + return std::string{}; +} + +/// Recursively generate the tuple type name +template + inline typename std::enable_if < I::value, std::string>::type tuple_name() { + std::string str = std::string(type_name::type>()) + ',' + tuple_name(); + if(str.back() == ',') + str.pop_back(); + return str; +} + +/// Print type name for tuples with 2 or more elements +template ::value == object_category::tuple_value && type_count::value >= 2, + detail::enabler> = detail::dummy> +std::string type_name() { + auto tname = std::string(1, '[') + tuple_name(); + tname.push_back(']'); + return tname; +} + +/// This one should not be used normally, since vector types print the internal type +template ::value == object_category::vector_value, detail::enabler> = detail::dummy> +inline std::string type_name() { + return type_name(); +} + +// Lexical cast + +/// Convert a flag into an integer value typically binary flags +inline int64_t to_flag_value(std::string val) { + static const std::string trueString("true"); + static const std::string falseString("false"); + if(val == trueString) { + return 1; + } + if(val == falseString) { + return -1; + } + val = detail::to_lower(val); + int64_t ret; + if(val.size() == 1) { + if(val[0] >= '1' && val[0] <= '9') { + return (static_cast(val[0]) - '0'); + } + switch(val[0]) { + case '0': + case 'f': + case 'n': + case '-': + ret = -1; + break; + case 't': + case 'y': + case '+': + ret = 1; + break; + default: + throw std::invalid_argument("unrecognized character"); + } + return ret; + } + if(val == trueString || val == "on" || val == "yes" || val == "enable") { + ret = 1; + } else if(val == falseString || val == "off" || val == "no" || val == "disable") { + ret = -1; + } else { + ret = std::stoll(val); + } + return ret; +} + +/// Signed integers +template ::value == object_category::integral_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + try { + std::size_t n = 0; + std::int64_t output_ll = std::stoll(input, &n, 0); + output = static_cast(output_ll); + return n == input.size() && static_cast(output) == output_ll; + } catch(const std::invalid_argument &) { + return false; + } catch(const std::out_of_range &) { + return false; + } +} + +/// Unsigned integers +template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + if(!input.empty() && input.front() == '-') + return false; // std::stoull happily converts negative values to junk without any errors. + + try { + std::size_t n = 0; + std::uint64_t output_ll = std::stoull(input, &n, 0); + output = static_cast(output_ll); + return n == input.size() && static_cast(output) == output_ll; + } catch(const std::invalid_argument &) { + return false; + } catch(const std::out_of_range &) { + return false; + } +} + +/// Boolean values +template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + try { + auto out = to_flag_value(input); + output = (out > 0); + return true; + } catch(const std::invalid_argument &) { + return false; + } catch(const std::out_of_range &) { + // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still + // valid all we care about the sign + output = (input[0] != '-'); + return true; + } +} + +/// Floats +template ::value == object_category::floating_point, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + try { + std::size_t n = 0; + output = static_cast(std::stold(input, &n)); + return n == input.size(); + } catch(const std::invalid_argument &) { + return false; + } catch(const std::out_of_range &) { + return false; + } +} + +/// String and similar direct assignment +template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = input; + return true; +} + +/// String and similar constructible and copy assignment +template < + typename T, + enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + output = T(input); + return true; +} + +/// Enumerations +template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + typename std::underlying_type::type val; + bool retval = detail::lexical_cast(input, val); + if(!retval) { + return false; + } + output = static_cast(val); + return true; +} + +/// Assignable from double or int +template < + typename T, + enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val; + if(lexical_cast(input, val)) { + output = T(val); + return true; + } else { + double dval; + if(lexical_cast(input, dval)) { + output = T{dval}; + return true; + } + } + return from_stream(input, output); +} + +/// Assignable from int +template < + typename T, + enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + int val; + if(lexical_cast(input, val)) { + output = T(val); + return true; + } + return from_stream(input, output); +} + +/// Assignable from double +template < + typename T, + enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + double val; + if(lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); +} + +/// Non-string parsable by a stream +template ::value == object_category::other, detail::enabler> = detail::dummy> +bool lexical_cast(const std::string &input, T &output) { + static_assert(is_istreamable::value, + "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " + "is convertible from another type use the add_option(...) with XC being the known type"); + return from_stream(input, output); +} + +/// Assign a value through lexical cast operations +template < + typename T, + typename XC, + enable_if_t::value && (classify_object::value == object_category::string_assignable || + classify_object::value == object_category::string_constructible), + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + return lexical_cast(input, output); +} + +/// Assign a value through lexical cast operations +template ::value && classify_object::value != object_category::string_assignable && + classify_object::value != object_category::string_constructible, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + if(input.empty()) { + output = T{}; + return true; + } + return lexical_cast(input, output); +} + +/// Assign a value converted from a string in lexical cast to the output value directly +template < + typename T, + typename XC, + enable_if_t::value && std::is_assignable::value, detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + XC val{}; + bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; + if(parse_result) { + output = val; + } + return parse_result; +} + +/// Assign a value from a lexical cast through constructing a value and move assigning it +template ::value && !std::is_assignable::value && + std::is_move_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + XC val{}; + bool parse_result = input.empty() ? true : lexical_cast(input, val); + if(parse_result) { + output = T(val); // use () form of constructor to allow some implicit conversions + } + return parse_result; +} +/// Lexical conversion if there is only one element +template < + typename T, + typename XC, + enable_if_t::value && !is_tuple_like::value && !is_vector::value && !is_vector::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + return lexical_assign(strings[0], output); +} + +/// Lexical conversion if there is only one element but the conversion type is for two call a two element constructor +template ::value == 1 && type_count::value == 2, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + typename std::tuple_element<0, XC>::type v1; + typename std::tuple_element<1, XC>::type v2; + bool retval = lexical_assign(strings[0], v1); + if(strings.size() > 1) { + retval = retval && lexical_assign(strings[1], v2); + } + if(retval) { + output = T{v1, v2}; + } + return retval; +} + +/// Lexical conversion of a vector types +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && type_count::value == 1, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + output.clear(); + output.reserve(strings.size()); + for(const auto &elem : strings) { + + output.emplace_back(); + bool retval = lexical_assign(elem, output.back()); + if(!retval) { + return false; + } + } + return (!output.empty()); +} + +/// Lexical conversion of a vector types with type size of two +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && type_count::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + output.clear(); + for(std::size_t ii = 0; ii < strings.size(); ii += 2) { + + typename std::tuple_element<0, typename XC::value_type>::type v1; + typename std::tuple_element<1, typename XC::value_type>::type v2; + bool retval = lexical_assign(strings[ii], v1); + if(strings.size() > ii + 1) { + retval = retval && lexical_assign(strings[ii + 1], v2); + } + if(retval) { + output.emplace_back(v1, v2); + } else { + return false; + } + } + return (!output.empty()); +} + +/// Conversion to a vector type using a particular single type as the conversion type +template ::value == expected_max_vector_size) && (expected_count::value == 1) && + (type_count::value == 1), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + bool retval = true; + output.clear(); + output.reserve(strings.size()); + for(const auto &elem : strings) { + + output.emplace_back(); + retval = retval && lexical_assign(elem, output.back()); + } + return (!output.empty()) && retval; +} +// This one is last since it can call other lexical_conversion functions +/// Lexical conversion if there is only one element but the conversion type is a vector +template ::value && !is_vector::value && is_vector::value, detail::enabler> = + detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + + if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { + XC val; + auto retval = lexical_conversion(strings, val); + output = T{val}; + return retval; + } + output = T{}; + return true; +} + +/// function template for converting tuples if the static Index is greater than the tuple size +template +inline typename std::enable_if= type_count::value, bool>::type tuple_conversion(const std::vector &, + T &) { + return true; +} +/// Tuple conversion operation +template + inline typename std::enable_if < + I::value, bool>::type tuple_conversion(const std::vector &strings, T &output) { + bool retval = true; + if(strings.size() > I) { + retval = retval && lexical_assign::type, + typename std::conditional::value, + typename std::tuple_element::type, + XC>::type>(strings[I], std::get(output)); + } + retval = retval && tuple_conversion(strings, output); + return retval; +} + +/// Conversion for tuples +template ::value, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + static_assert( + !is_tuple_like::value || type_count::value == type_count::value, + "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); + return tuple_conversion(strings, output); +} + +/// Lexical conversion of a vector types with type_size >2 +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && (type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + bool retval = true; + output.clear(); + std::vector temp; + std::size_t ii = 0; + std::size_t icount = 0; + std::size_t xcm = type_count::value; + while(ii < strings.size()) { + temp.push_back(strings[ii]); + ++ii; + ++icount; + if(icount == xcm || temp.back().empty()) { + if(static_cast(xcm) == expected_max_vector_size) { + temp.pop_back(); + } + output.emplace_back(); + retval = retval && lexical_conversion(temp, output.back()); + temp.clear(); + if(!retval) { + return false; + } + icount = 0; + } + } + return retval; +} +/// Sum a vector of flag representations +/// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is +/// by +/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most +/// common true and false strings then uses stoll to convert the rest for summing +template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> +void sum_flag_vector(const std::vector &flags, T &output) { + int64_t count{0}; + for(auto &flag : flags) { + count += detail::to_flag_value(flag); + } + output = (count > 0) ? static_cast(count) : T{0}; +} + +/// Sum a vector of flag representations +/// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is +/// by +/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most +/// common true and false strings then uses stoll to convert the rest for summing +template ::value && std::is_signed::value, detail::enabler> = detail::dummy> +void sum_flag_vector(const std::vector &flags, T &output) { + int64_t count{0}; + for(auto &flag : flags) { + count += detail::to_flag_value(flag); + } + output = static_cast(count); +} + +} // namespace detail +} // namespace CLI + +// From CLI/Split.hpp: + +namespace CLI { +namespace detail { + +// Returns false if not a short option. Otherwise, sets opt name and rest and returns true +inline bool split_short(const std::string ¤t, std::string &name, std::string &rest) { + if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { + name = current.substr(1, 1); + rest = current.substr(2); + return true; + } + return false; +} + +// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true +inline bool split_long(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) { + auto loc = current.find_first_of('='); + if(loc != std::string::npos) { + name = current.substr(2, loc - 2); + value = current.substr(loc + 1); + } else { + name = current.substr(2); + value = ""; + } + return true; + } + return false; +} + +// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true +inline bool split_windows_style(const std::string ¤t, std::string &name, std::string &value) { + if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { + auto loc = current.find_first_of(':'); + if(loc != std::string::npos) { + name = current.substr(1, loc - 1); + value = current.substr(loc + 1); + } else { + name = current.substr(1); + value = ""; + } + return true; + } + return false; +} + +// Splits a string into multiple long and short names +inline std::vector split_names(std::string current) { + std::vector output; + std::size_t val; + while((val = current.find(",")) != std::string::npos) { + output.push_back(trim_copy(current.substr(0, val))); + current = current.substr(val + 1); + } + output.push_back(trim_copy(current)); + return output; +} + +/// extract default flag values either {def} or starting with a ! +inline std::vector> get_default_flag_values(const std::string &str) { + std::vector flags = split_names(str); + flags.erase(std::remove_if(flags.begin(), + flags.end(), + [](const std::string &name) { + return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) && + (name.back() == '}')) || + (name[0] == '!')))); + }), + flags.end()); + std::vector> output; + output.reserve(flags.size()); + for(auto &flag : flags) { + auto def_start = flag.find_first_of('{'); + std::string defval = "false"; + if((def_start != std::string::npos) && (flag.back() == '}')) { + defval = flag.substr(def_start + 1); + defval.pop_back(); + flag.erase(def_start, std::string::npos); + } + flag.erase(0, flag.find_first_not_of("-!")); + output.emplace_back(flag, defval); + } + return output; +} + +/// Get a vector of short names, one of long names, and a single name +inline std::tuple, std::vector, std::string> +get_names(const std::vector &input) { + + std::vector short_names; + std::vector long_names; + std::string pos_name; + + for(std::string name : input) { + if(name.length() == 0) { + continue; + } + if(name.length() > 1 && name[0] == '-' && name[1] != '-') { + if(name.length() == 2 && valid_first_char(name[1])) + short_names.emplace_back(1, name[1]); + else + throw BadNameString::OneCharName(name); + } else if(name.length() > 2 && name.substr(0, 2) == "--") { + name = name.substr(2); + if(valid_name_string(name)) + long_names.push_back(name); + else + throw BadNameString::BadLongName(name); + } else if(name == "-" || name == "--") { + throw BadNameString::DashesOnly(name); + } else { + if(pos_name.length() > 0) + throw BadNameString::MultiPositionalNames(name); + pos_name = name; + } + } + + return std::tuple, std::vector, std::string>( + short_names, long_names, pos_name); +} + +} // namespace detail +} // namespace CLI + +// From CLI/ConfigFwd.hpp: + +namespace CLI { + +class App; + +/// Holds values to load into Options +struct ConfigItem { + /// This is the list of parents + std::vector parents{}; + + /// This is the name + std::string name{}; + + /// Listing of inputs + std::vector inputs{}; + + /// The list of parents and name joined by "." + std::string fullname() const { + std::vector tmp = parents; + tmp.emplace_back(name); + return detail::join(tmp, "."); + } +}; + +/// This class provides a converter for configuration files. +class Config { + protected: + std::vector items{}; + + public: + /// Convert an app into a configuration + virtual std::string to_config(const App *, bool, bool, std::string) const = 0; + + /// Convert a configuration into an app + virtual std::vector from_config(std::istream &) const = 0; + + /// Get a flag value + virtual std::string to_flag(const ConfigItem &item) const { + if(item.inputs.size() == 1) { + return item.inputs.at(0); + } + throw ConversionError::TooManyInputsFlag(item.fullname()); + } + + /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure + std::vector from_file(const std::string &name) { + std::ifstream input{name}; + if(!input.good()) + throw FileError::Missing(name); + + return from_config(input); + } + + /// Virtual destructor + virtual ~Config() = default; +}; + +/// This converter works with INI/TOML files; to write proper TOML files use ConfigTOML +class ConfigBase : public Config { + protected: + /// the character used for comments + char commentChar = ';'; + /// the character used to start an array '\0' is a default to not use + char arrayStart = '\0'; + /// the character used to end an array '\0' is a default to not use + char arrayEnd = '\0'; + /// the character used to separate elements in an array + char arraySeparator = ' '; + /// the character used separate the name from the value + char valueDelimiter = '='; + + public: + std::string + to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; + + std::vector from_config(std::istream &input) const override; + /// Specify the configuration for comment characters + ConfigBase *comment(char cchar) { + commentChar = cchar; + return this; + } + /// Specify the start and end characters for an array + ConfigBase *arrayBounds(char aStart, char aEnd) { + arrayStart = aStart; + arrayEnd = aEnd; + return this; + } + /// Specify the delimiter character for an array + ConfigBase *arrayDelimiter(char aSep) { + arraySeparator = aSep; + return this; + } + /// Specify the delimiter between a name and value + ConfigBase *valueSeparator(char vSep) { + valueDelimiter = vSep; + return this; + } +}; + +/// the default Config is the INI file format +using ConfigINI = ConfigBase; + +/// ConfigTOML generates a TOML compliant output +class ConfigTOML : public ConfigINI { + + public: + ConfigTOML() { + commentChar = '#'; + arrayStart = '['; + arrayEnd = ']'; + arraySeparator = ','; + valueDelimiter = '='; + } +}; +} // namespace CLI + +// From CLI/Validators.hpp: + +namespace CLI { + +class Option; + +/// @defgroup validator_group Validators + +/// @brief Some validators that are provided +/// +/// These are simple `std::string(const std::string&)` validators that are useful. They return +/// a string if the validation fails. A custom struct is provided, as well, with the same user +/// semantics, but with the ability to provide a new type name. +/// @{ + +/// +class Validator { + protected: + /// This is the description function, if empty the description_ will be used + std::function desc_function_{[]() { return std::string{}; }}; + + /// This is the base function that is to be called. + /// Returns a string error message if validation fails. + std::function func_{[](std::string &) { return std::string{}; }}; + /// The name for search purposes of the Validator + std::string name_{}; + /// A Validator will only apply to an indexed value (-1 is all elements) + int application_index_ = -1; + /// Enable for Validator to allow it to be disabled if need be + bool active_{true}; + /// specify that a validator should not modify the input + bool non_modifying_{false}; + + public: + Validator() = default; + /// Construct a Validator with just the description string + explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {} + /// Construct Validator from basic information + Validator(std::function op, std::string validator_desc, std::string validator_name = "") + : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)), + name_(std::move(validator_name)) {} + /// Set the Validator operation function + Validator &operation(std::function op) { + func_ = std::move(op); + return *this; + } + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(std::string &str) const { + std::string retstring; + if(active_) { + if(non_modifying_) { + std::string value = str; + retstring = func_(value); + } else { + retstring = func_(str); + } + } + return retstring; + }; + + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(const std::string &str) const { + std::string value = str; + return (active_) ? func_(value) : std::string{}; + }; + + /// Specify the type string + Validator &description(std::string validator_desc) { + desc_function_ = [validator_desc]() { return validator_desc; }; + return *this; + } + /// Specify the type string + Validator description(std::string validator_desc) const { + Validator newval(*this); + newval.desc_function_ = [validator_desc]() { return validator_desc; }; + return newval; + } + /// Generate type description information for the Validator + std::string get_description() const { + if(active_) { + return desc_function_(); + } + return std::string{}; + } + /// Specify the type string + Validator &name(std::string validator_name) { + name_ = std::move(validator_name); + return *this; + } + /// Specify the type string + Validator name(std::string validator_name) const { + Validator newval(*this); + newval.name_ = std::move(validator_name); + return newval; + } + /// Get the name of the Validator + const std::string &get_name() const { return name_; } + /// Specify whether the Validator is active or not + Validator &active(bool active_val = true) { + active_ = active_val; + return *this; + } + /// Specify whether the Validator is active or not + Validator active(bool active_val = true) const { + Validator newval(*this); + newval.active_ = active_val; + return newval; + } + + /// Specify whether the Validator can be modifying or not + Validator &non_modifying(bool no_modify = true) { + non_modifying_ = no_modify; + return *this; + } + /// Specify the application index of a validator + Validator &application_index(int app_index) { + application_index_ = app_index; + return *this; + }; + /// Specify the application index of a validator + Validator application_index(int app_index) const { + Validator newval(*this); + newval.application_index_ = app_index; + return newval; + }; + /// Get the current value of the application index + int get_application_index() const { return application_index_; } + /// Get a boolean if the validator is active + bool get_active() const { return active_; } + + /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input + bool get_modifying() const { return !non_modifying_; } + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator&(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " AND "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(!s1.empty() && !s2.empty()) + return std::string("(") + s1 + ") AND (" + s2 + ")"; + else + return s1 + s2; + }; + + newval.active_ = (active_ & other.active_); + newval.application_index_ = application_index_; + return newval; + } + + /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the + /// same. + Validator operator|(const Validator &other) const { + Validator newval; + + newval._merge_description(*this, other, " OR "); + + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; + + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if(s1.empty() || s2.empty()) + return std::string(); + + return std::string("(") + s1 + ") OR (" + s2 + ")"; + }; + newval.active_ = (active_ & other.active_); + newval.application_index_ = application_index_; + return newval; + } + + /// Create a validator that fails when a given validator succeeds + Validator operator!() const { + Validator newval; + const std::function &dfunc1 = desc_function_; + newval.desc_function_ = [dfunc1]() { + auto str = dfunc1(); + return (!str.empty()) ? std::string("NOT ") + str : std::string{}; + }; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + + newval.func_ = [f1, dfunc1](std::string &test) -> std::string { + std::string s1 = f1(test); + if(s1.empty()) { + return std::string("check ") + dfunc1() + " succeeded improperly"; + } + return std::string{}; + }; + newval.active_ = active_; + newval.application_index_ = application_index_; + return newval; + } + + private: + void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) { + + const std::function &dfunc1 = val1.desc_function_; + const std::function &dfunc2 = val2.desc_function_; + + desc_function_ = [=]() { + std::string f1 = dfunc1(); + std::string f2 = dfunc2(); + if((f1.empty()) || (f2.empty())) { + return f1 + f2; + } + return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')'; + }; + } +}; // namespace CLI + +/// Class wrapping some of the accessors of Validator +class CustomValidator : public Validator { + public: +}; +// The implementation of the built in validators is using the Validator class; +// the user is only expected to use the const (static) versions (since there's no setup). +// Therefore, this is in detail. +namespace detail { + +/// CLI enumeration of different file types +enum class path_type { nonexistant, file, directory }; + +#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 +/// get the type of the path from a file name +inline path_type check_path(const char *file) noexcept { + std::error_code ec; + auto stat = std::filesystem::status(file, ec); + if(ec) { + return path_type::nonexistant; + } + switch(stat.type()) { + case std::filesystem::file_type::none: + case std::filesystem::file_type::not_found: + return path_type::nonexistant; + case std::filesystem::file_type::directory: + return path_type::directory; + case std::filesystem::file_type::symlink: + case std::filesystem::file_type::block: + case std::filesystem::file_type::character: + case std::filesystem::file_type::fifo: + case std::filesystem::file_type::socket: + case std::filesystem::file_type::regular: + case std::filesystem::file_type::unknown: + default: + return path_type::file; + } +} +#else +/// get the type of the path from a file name +inline path_type check_path(const char *file) noexcept { +#if defined(_MSC_VER) + struct __stat64 buffer; + if(_stat64(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#else + struct stat buffer; + if(stat(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + } +#endif + return path_type::nonexistant; +} +#endif +/// Check for an existing file (returns error message if check fails) +class ExistingFileValidator : public Validator { + public: + ExistingFileValidator() : Validator("FILE") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistant) { + return "File does not exist: " + filename; + } + if(path_result == path_type::directory) { + return "File is actually a directory: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an existing directory (returns error message if check fails) +class ExistingDirectoryValidator : public Validator { + public: + ExistingDirectoryValidator() : Validator("DIR") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistant) { + return "Directory does not exist: " + filename; + } + if(path_result == path_type::file) { + return "Directory is actually a file: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an existing path +class ExistingPathValidator : public Validator { + public: + ExistingPathValidator() : Validator("PATH(existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result == path_type::nonexistant) { + return "Path does not exist: " + filename; + } + return std::string(); + }; + } +}; + +/// Check for an non-existing path +class NonexistentPathValidator : public Validator { + public: + NonexistentPathValidator() : Validator("PATH(non-existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if(path_result != path_type::nonexistant) { + return "Path already exists: " + filename; + } + return std::string(); + }; + } +}; + +/// Validate the given string is a legal ipv4 address +class IPV4Validator : public Validator { + public: + IPV4Validator() : Validator("IPV4") { + func_ = [](std::string &ip_addr) { + auto result = CLI::detail::split(ip_addr, '.'); + if(result.size() != 4) { + return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')'; + } + int num; + for(const auto &var : result) { + bool retval = detail::lexical_cast(var, num); + if(!retval) { + return std::string("Failed parsing number (") + var + ')'; + } + if(num < 0 || num > 255) { + return std::string("Each IP number must be between 0 and 255 ") + var; + } + } + return std::string(); + }; + } +}; + +/// Validate the argument is a number and greater than 0 +class PositiveNumber : public Validator { + public: + PositiveNumber() : Validator("POSITIVE") { + func_ = [](std::string &number_str) { + double number; + if(!detail::lexical_cast(number_str, number)) { + return std::string("Failed parsing number: (") + number_str + ')'; + } + if(number <= 0) { + return std::string("Number less or equal to 0: (") + number_str + ')'; + } + return std::string(); + }; + } +}; +/// Validate the argument is a number and greater than or equal to 0 +class NonNegativeNumber : public Validator { + public: + NonNegativeNumber() : Validator("NONNEGATIVE") { + func_ = [](std::string &number_str) { + double number; + if(!detail::lexical_cast(number_str, number)) { + return std::string("Failed parsing number: (") + number_str + ')'; + } + if(number < 0) { + return std::string("Number less than 0: (") + number_str + ')'; + } + return std::string(); + }; + } +}; + +/// Validate the argument is a number +class Number : public Validator { + public: + Number() : Validator("NUMBER") { + func_ = [](std::string &number_str) { + double number; + if(!detail::lexical_cast(number_str, number)) { + return std::string("Failed parsing as a number (") + number_str + ')'; + } + return std::string(); + }; + } +}; + +} // namespace detail + +// Static is not needed here, because global const implies static. + +/// Check for existing file (returns error message if check fails) +const detail::ExistingFileValidator ExistingFile; + +/// Check for an existing directory (returns error message if check fails) +const detail::ExistingDirectoryValidator ExistingDirectory; + +/// Check for an existing path +const detail::ExistingPathValidator ExistingPath; + +/// Check for an non-existing path +const detail::NonexistentPathValidator NonexistentPath; + +/// Check for an IP4 address +const detail::IPV4Validator ValidIPV4; + +/// Check for a positive number +const detail::PositiveNumber PositiveNumber; + +/// Check for a non-negative number +const detail::NonNegativeNumber NonNegativeNumber; + +/// Check for a number +const detail::Number Number; + +/// Produce a range (factory). Min and max are inclusive. +class Range : public Validator { + public: + /// This produces a range with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template Range(T min, T max) { + std::stringstream out; + out << detail::type_name() << " in [" << min << " - " << max << "]"; + description(out.str()); + + func_ = [min, max](std::string &input) { + T val; + bool converted = detail::lexical_cast(input, val); + if((!converted) || (val < min || val > max)) + return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " + + std::to_string(max); + + return std::string(); + }; + } + + /// Range of one value is 0 to value + template explicit Range(T max) : Range(static_cast(0), max) {} +}; + +/// Produce a bounded range (factory). Min and max are inclusive. +class Bound : public Validator { + public: + /// This bounds a value with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is not + /// needed to provide nice syntax for Range(a,b). + template Bound(T min, T max) { + std::stringstream out; + out << detail::type_name() << " bounded to [" << min << " - " << max << "]"; + description(out.str()); + + func_ = [min, max](std::string &input) { + T val; + bool converted = detail::lexical_cast(input, val); + if(!converted) { + return std::string("Value ") + input + " could not be converted"; + } + if(val < min) + input = detail::to_string(min); + else if(val > max) + input = detail::to_string(max); + + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template explicit Bound(T max) : Bound(static_cast(0), max) {} +}; + +namespace detail { +template ::type>::value, detail::enabler> = detail::dummy> +auto smart_deref(T value) -> decltype(*value) { + return *value; +} + +template < + typename T, + enable_if_t::type>::value, detail::enabler> = detail::dummy> +typename std::remove_reference::type &smart_deref(T &value) { + return value; +} +/// Generate a string representation of a set +template std::string generate_set(const T &set) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(set), + [](const iteration_type_t &v) { return detail::pair_adaptor::first(v); }, + ",")); + out.push_back('}'); + return out; +} + +/// Generate a string representation of a map +template std::string generate_map(const T &map, bool key_only = false) { + using element_t = typename detail::element_type::type; + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(map), + [key_only](const iteration_type_t &v) { + std::string res{detail::to_string(detail::pair_adaptor::first(v))}; + + if(!key_only) { + res.append("->"); + res += detail::to_string(detail::pair_adaptor::second(v)); + } + return res; + }, + ",")); + out.push_back('}'); + return out; +} + +template struct has_find { + template + static auto test(int) -> decltype(std::declval().find(std::declval()), std::true_type()); + template static auto test(...) -> decltype(std::false_type()); + + static const auto value = decltype(test(0))::value; + using type = std::integral_constant; +}; + +/// A search function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + using element_t = typename detail::element_type::type; + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) { + return (detail::pair_adaptor::first(v) == val); + }); + return {(it != std::end(setref)), it}; +} + +/// A search function that uses the built in find function +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) -> std::pair { + auto &setref = detail::smart_deref(set); + auto it = setref.find(val); + return {(it != std::end(setref)), it}; +} + +/// A search function with a filter function +template +auto search(const T &set, const V &val, const std::function &filter_function) + -> std::pair { + using element_t = typename detail::element_type::type; + // do the potentially faster first search + auto res = search(set, val); + if((res.first) || (!(filter_function))) { + return res; + } + // if we haven't found it do the longer linear search with all the element translations + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) { + V a{detail::pair_adaptor::first(v)}; + a = filter_function(a); + return (a == val); + }); + return {(it != std::end(setref)), it}; +} + +// the following suggestion was made by Nikita Ofitserov(@himikof) +// done in templates to prevent compiler warnings on negation of unsigned numbers + +/// Do a check for overflow on signed numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + if((a > 0) == (b > 0)) { + return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); + } else { + return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); + } +} +/// Do a check for overflow on unsigned numbers +template +inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { + return ((std::numeric_limits::max)() / a < b); +} + +/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. +template typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + if(a == 0 || b == 0 || a == 1 || b == 1) { + a *= b; + return true; + } + if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { + return false; + } + if(overflowCheck(a, b)) { + return false; + } + a *= b; + return true; +} + +/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise. +template +typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { + T c = a * b; + if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) { + return false; + } + a = c; + return true; +} + +} // namespace detail +/// Verify items are in a set +class IsMember : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction using an initializer list + template + IsMember(std::initializer_list values, Args &&... args) + : IsMember(std::vector(values), std::forward(args)...) {} + + /// This checks to see if an item is in a set (empty function) + template explicit IsMember(T &&set) : IsMember(std::forward(set), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit IsMember(T set, F filter_function) { + + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + + using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); }; + + // This is the function that validates + // It stores a copy of the set pointer-like, so shared_ptr will stay alive + func_ = [set, filter_fn](std::string &input) { + local_item_t b; + if(!detail::lexical_cast(input, b)) { + throw ValidationError(input); // name is added later + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(set, b, filter_fn); + if(res.first) { + // Make sure the version in the input string is identical to the one in the set + if(filter_fn) { + input = detail::value_string(detail::pair_adaptor::first(*(res.second))); + } + + // Return empty error string (success) + return std::string{}; + } + + // If you reach this point, the result was not found + std::string out(" not in "); + out += detail::generate_set(detail::smart_deref(set)); + return out; + }; + } + + /// You can pass in as many filter functions as you like, they nest (string only currently) + template + IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other) + : IsMember( + std::forward(set), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// definition of the default transformation object +template using TransformPairs = std::vector>; + +/// Translate named items to other or a value set +class Transformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + Transformer(std::initializer_list> values, Args &&... args) + : Transformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit Transformer(T &&mapping) : Transformer(std::forward(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit Transformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + // (const char * to std::string) + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + // This is the type name for help, it will take the current version of the set contents + desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); }; + + func_ = [mapping, filter_fn](std::string &input) { + local_item_t b; + if(!detail::lexical_cast(input, b)) { + return std::string(); + // there is no possible way we can match anything in the mapping if we can't convert so just return + } + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + } + return std::string{}; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other) + : Transformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// translate named items to other or a value set +class CheckedTransformer : public Validator { + public: + using filter_fn_t = std::function; + + /// This allows in-place construction + template + CheckedTransformer(std::initializer_list> values, Args &&... args) + : CheckedTransformer(TransformPairs(values), std::forward(args)...) {} + + /// direct map of std::string to std::string + template explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {} + + /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter + /// both sides of the comparison before computing the comparison. + template explicit CheckedTransformer(T mapping, F filter_function) { + + static_assert(detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have ::value_type + // if the type does not have first_type and second_type, these are both value_type + using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed + using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + // (const char * to std::string) + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair // + // the type of the object pair + + // Make a local copy of the filter function, using a std::function if not one already + std::function filter_fn = filter_function; + + auto tfunc = [mapping]() { + std::string out("value in "); + out += detail::generate_map(detail::smart_deref(mapping)) + " OR {"; + out += detail::join( + detail::smart_deref(mapping), + [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor::second(v)); }, + ","); + out.push_back('}'); + return out; + }; + + desc_function_ = tfunc; + + func_ = [mapping, tfunc, filter_fn](std::string &input) { + local_item_t b; + bool converted = detail::lexical_cast(input, b); + if(converted) { + if(filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if(res.first) { + input = detail::value_string(detail::pair_adaptor::second(*res.second)); + return std::string{}; + } + } + for(const auto &v : detail::smart_deref(mapping)) { + auto output_string = detail::value_string(detail::pair_adaptor::second(v)); + if(output_string == input) { + return std::string(); + } + } + + return "Check " + input + " " + tfunc() + " FAILED"; + }; + } + + /// You can pass in as many filter functions as you like, they nest + template + CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other) + : CheckedTransformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, + other...) {} +}; + +/// Helper function to allow ignore_case to be passed to IsMember or Transform +inline std::string ignore_case(std::string item) { return detail::to_lower(item); } + +/// Helper function to allow ignore_underscore to be passed to IsMember or Transform +inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); } + +/// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform +inline std::string ignore_space(std::string item) { + item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item)); + item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item)); + return item; +} + +/// Multiply a number by a factor using given mapping. +/// Can be used to write transforms for SIZE or DURATION inputs. +/// +/// Example: +/// With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}` +/// one can recognize inputs like "100", "12kb", "100 MB", +/// that will be automatically transformed to 100, 14448, 104857600. +/// +/// Output number type matches the type in the provided mapping. +/// Therefore, if it is required to interpret real inputs like "0.42 s", +/// the mapping should be of a type or . +class AsNumberWithUnit : public Validator { + public: + /// Adjust AsNumberWithUnit behavior. + /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. + /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError + /// if UNIT_REQUIRED is set and unit literal is not found. + enum Options { + CASE_SENSITIVE = 0, + CASE_INSENSITIVE = 1, + UNIT_OPTIONAL = 0, + UNIT_REQUIRED = 2, + DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL + }; + + template + explicit AsNumberWithUnit(std::map mapping, + Options opts = DEFAULT, + const std::string &unit_name = "UNIT") { + description(generate_description(unit_name, opts)); + validate_mapping(mapping, opts); + + // transform function + func_ = [mapping, opts](std::string &input) -> std::string { + Number num; + + detail::rtrim(input); + if(input.empty()) { + throw ValidationError("Input is empty"); + } + + // Find split position between number and prefix + auto unit_begin = input.end(); + while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) { + --unit_begin; + } + + std::string unit{unit_begin, input.end()}; + input.resize(static_cast(std::distance(input.begin(), unit_begin))); + detail::trim(input); + + if(opts & UNIT_REQUIRED && unit.empty()) { + throw ValidationError("Missing mandatory unit"); + } + if(opts & CASE_INSENSITIVE) { + unit = detail::to_lower(unit); + } + + bool converted = detail::lexical_cast(input, num); + if(!converted) { + throw ValidationError(std::string("Value ") + input + " could not be converted to " + + detail::type_name()); + } + + if(unit.empty()) { + // No need to modify input if no unit passed + return {}; + } + + // find corresponding factor + auto it = mapping.find(unit); + if(it == mapping.end()) { + throw ValidationError(unit + + " unit not recognized. " + "Allowed values: " + + detail::generate_map(mapping, true)); + } + + // perform safe multiplication + bool ok = detail::checked_multiply(num, it->second); + if(!ok) { + throw ValidationError(detail::to_string(num) + " multiplied by " + unit + + " factor would cause number overflow. Use smaller value."); + } + input = detail::to_string(num); + + return {}; + }; + } + + private: + /// Check that mapping contains valid units. + /// Update mapping for CASE_INSENSITIVE mode. + template static void validate_mapping(std::map &mapping, Options opts) { + for(auto &kv : mapping) { + if(kv.first.empty()) { + throw ValidationError("Unit must not be empty."); + } + if(!detail::isalpha(kv.first)) { + throw ValidationError("Unit must contain only letters."); + } + } + + // make all units lowercase if CASE_INSENSITIVE + if(opts & CASE_INSENSITIVE) { + std::map lower_mapping; + for(auto &kv : mapping) { + auto s = detail::to_lower(kv.first); + if(lower_mapping.count(s)) { + throw ValidationError(std::string("Several matching lowercase unit representations are found: ") + + s); + } + lower_mapping[detail::to_lower(kv.first)] = kv.second; + } + mapping = std::move(lower_mapping); + } + } + + /// Generate description like this: NUMBER [UNIT] + template static std::string generate_description(const std::string &name, Options opts) { + std::stringstream out; + out << detail::type_name() << ' '; + if(opts & UNIT_REQUIRED) { + out << name; + } else { + out << '[' << name << ']'; + } + return out.str(); + } +}; + +/// Converts a human-readable size string (with unit literal) to uin64_t size. +/// Example: +/// "100" => 100 +/// "1 b" => 100 +/// "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024) +/// "10 KB" => 10240 +/// "10 kb" => 10240 +/// "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024) +/// "10kb" => 10240 +/// "2 MB" => 2097152 +/// "2 EiB" => 2^61 // Units up to exibyte are supported +class AsSizeValue : public AsNumberWithUnit { + public: + using result_t = uint64_t; + + /// If kb_is_1000 is true, + /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 + /// (same applies to higher order units as well). + /// Otherwise, interpret all literals as factors of 1024. + /// The first option is formally correct, but + /// the second interpretation is more wide-spread + /// (see https://en.wikipedia.org/wiki/Binary_prefix). + explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) { + if(kb_is_1000) { + description("SIZE [b, kb(=1000b), kib(=1024b), ...]"); + } else { + description("SIZE [b, kb(=1024b), ...]"); + } + } + + private: + /// Get mapping + static std::map init_mapping(bool kb_is_1000) { + std::map m; + result_t k_factor = kb_is_1000 ? 1000 : 1024; + result_t ki_factor = 1024; + result_t k = 1; + result_t ki = 1; + m["b"] = 1; + for(std::string p : {"k", "m", "g", "t", "p", "e"}) { + k *= k_factor; + ki *= ki_factor; + m[p] = k; + m[p + "b"] = k; + m[p + "i"] = ki; + m[p + "ib"] = ki; + } + return m; + } + + /// Cache calculated mapping + static std::map get_mapping(bool kb_is_1000) { + if(kb_is_1000) { + static auto m = init_mapping(true); + return m; + } else { + static auto m = init_mapping(false); + return m; + } + } +}; + +namespace detail { +/// Split a string into a program name and command line arguments +/// the string is assumed to contain a file name followed by other arguments +/// the return value contains is a pair with the first argument containing the program name and the second +/// everything else. +inline std::pair split_program_name(std::string commandline) { + // try to determine the programName + std::pair vals; + trim(commandline); + auto esp = commandline.find_first_of(' ', 1); + while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) { + esp = commandline.find_first_of(' ', esp + 1); + if(esp == std::string::npos) { + // if we have reached the end and haven't found a valid file just assume the first argument is the + // program name + esp = commandline.find_first_of(' ', 1); + break; + } + } + vals.first = commandline.substr(0, esp); + rtrim(vals.first); + // strip the program name + vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{}; + ltrim(vals.second); + return vals; +} + +} // namespace detail +/// @} + +} // namespace CLI + +// From CLI/FormatterFwd.hpp: + +namespace CLI { + +class Option; +class App; + +/// This enum signifies the type of help requested +/// +/// This is passed in by App; all user classes must accept this as +/// the second argument. + +enum class AppFormatMode { + Normal, //< The normal, detailed help + All, //< A fully expanded help + Sub, //< Used when printed as part of expanded subcommand +}; + +/// This is the minimum requirements to run a formatter. +/// +/// A user can subclass this is if they do not care at all +/// about the structure in CLI::Formatter. +class FormatterBase { + protected: + /// @name Options + ///@{ + + /// The width of the first column + std::size_t column_width_{30}; + + /// @brief The required help printout labels (user changeable) + /// Values are Needs, Excludes, etc. + std::map labels_{}; + + ///@} + /// @name Basic + ///@{ + + public: + FormatterBase() = default; + FormatterBase(const FormatterBase &) = default; + FormatterBase(FormatterBase &&) = default; + + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) + + /// This is the key method that puts together help + virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; + + ///@} + /// @name Setters + ///@{ + + /// Set the "REQUIRED" label + void label(std::string key, std::string val) { labels_[key] = val; } + + /// Set the column width + void column_width(std::size_t val) { column_width_ = val; } + + ///@} + /// @name Getters + ///@{ + + /// Get the current value of a name (REQUIRED, etc.) + std::string get_label(std::string key) const { + if(labels_.find(key) == labels_.end()) + return key; + else + return labels_.at(key); + } + + /// Get the current column width + std::size_t get_column_width() const { return column_width_; } + + ///@} +}; + +/// This is a specialty override for lambda functions +class FormatterLambda final : public FormatterBase { + using funct_t = std::function; + + /// The lambda to hold and run + funct_t lambda_; + + public: + /// Create a FormatterLambda with a lambda function + explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} + + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) + + /// This will simply call the lambda function + std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { + return lambda_(app, name, mode); + } +}; + +/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few +/// overridable methods, to be highly customizable with minimal effort. +class Formatter : public FormatterBase { + public: + Formatter() = default; + Formatter(const Formatter &) = default; + Formatter(Formatter &&) = default; + + /// @name Overridables + ///@{ + + /// This prints out a group of options with title + /// + virtual std::string make_group(std::string group, bool is_positional, std::vector opts) const; + + /// This prints out just the positionals "group" + virtual std::string make_positionals(const App *app) const; + + /// This prints out all the groups of options + std::string make_groups(const App *app, AppFormatMode mode) const; + + /// This prints out all the subcommands + virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; + + /// This prints out a subcommand + virtual std::string make_subcommand(const App *sub) const; + + /// This prints out a subcommand in help-all + virtual std::string make_expanded(const App *sub) const; + + /// This prints out all the groups of options + virtual std::string make_footer(const App *app) const; + + /// This displays the description line + virtual std::string make_description(const App *app) const; + + /// This displays the usage line + virtual std::string make_usage(const App *app, std::string name) const; + + /// This puts everything together + std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override; + + ///@} + /// @name Options + ///@{ + + /// This prints out an option help line, either positional or optional form + virtual std::string make_option(const Option *opt, bool is_positional) const { + std::stringstream out; + detail::format_help( + out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_); + return out.str(); + } + + /// @brief This is the name part of an option, Default: left column + virtual std::string make_option_name(const Option *, bool) const; + + /// @brief This is the options part of the name, Default: combined into left column + virtual std::string make_option_opts(const Option *) const; + + /// @brief This is the description. Default: Right column, on new line if left column too large + virtual std::string make_option_desc(const Option *) const; + + /// @brief This is used to print the name on the USAGE line + virtual std::string make_option_usage(const Option *opt) const; + + ///@} +}; + +} // namespace CLI + +// From CLI/Option.hpp: + +namespace CLI { + +using results_t = std::vector; +/// callback function definition +using callback_t = std::function; + +class Option; +class App; + +using Option_p = std::unique_ptr