pax_global_header00006660000000000000000000000064145464646520014532gustar00rootroot0000000000000052 comment=d4eb5f3c9d8557b3820c80d55c41068839341b27 toml11-3.8.1/000077500000000000000000000000001454646465200126605ustar00rootroot00000000000000toml11-3.8.1/.circleci/000077500000000000000000000000001454646465200145135ustar00rootroot00000000000000toml11-3.8.1/.circleci/config.yml000066400000000000000000000055541454646465200165140ustar00rootroot00000000000000version: 2.1 jobs: test_suite: docker: - image: cimg/go:1.16 steps: - checkout - run: command: | g++ --version cd tests/ g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test export PATH=$(pwd):${PATH} git clone --depth 1 --branch v1.3.0 https://github.com/BurntSushi/toml-test.git cd toml-test/ go install -v ./cmd/toml-test cd - toml-test check_toml_test # go clean -modcache # go get github.com/BurntSushi/toml-test/cmd/toml-test # $GOPATH/bin/toml-test ./check_toml_test test_serialization: docker: - image: circleci/buildpack-deps:bionic steps: - checkout - run: command: | g++ --version cd tests/ g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization git clone https://github.com/BurntSushi/toml-test.git cp check_serialization toml-test/tests/valid cd toml-test/tests/valid for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; echo "---------------------------------------"; ./check_serialization ${f}; if [ $? -ne 0 ] ; then exit 1 fi echo "======================================="; done output_result: docker: - image: circleci/buildpack-deps:bionic steps: - checkout - run: command: | g++ --version cd tests/ g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check git clone https://github.com/BurntSushi/toml-test.git cp check toml-test/tests/invalid cp check toml-test/tests/valid cd toml-test/tests/invalid for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; echo "---------------------------------------"; ./check ${f} invalid; if [ $? -ne 0 ] ; then exit 1 fi echo "======================================="; done cd ../valid for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; echo "---------------------------------------"; ./check ${f} valid; if [ $? -ne 0 ] ; then exit 1 fi echo "======================================="; done workflows: version: 2.1 test: jobs: - test_suite - test_serialization - output_result toml11-3.8.1/.clusterfuzzlite/000077500000000000000000000000001454646465200162145ustar00rootroot00000000000000toml11-3.8.1/.clusterfuzzlite/Dockerfile000066400000000000000000000003041454646465200202030ustar00rootroot00000000000000FROM gcr.io/oss-fuzz-base/base-builder RUN apt-get update && apt-get install -y make autoconf automake libtool COPY . $SRC/toml11 COPY .clusterfuzzlite/build.sh $SRC/build.sh WORKDIR $SRC/toml11 toml11-3.8.1/.clusterfuzzlite/README.md000066400000000000000000000001761454646465200174770ustar00rootroot00000000000000# ClusterFuzzLite set up This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite). toml11-3.8.1/.clusterfuzzlite/build.sh000066400000000000000000000002611454646465200176460ustar00rootroot00000000000000#!/bin/bash -eu # Copy fuzzer executable to $OUT/ $CXX $CFLAGS $LIB_FUZZING_ENGINE \ $SRC/toml11/.clusterfuzzlite/parse_fuzzer.cpp \ -o $OUT/parse_fuzzer \ -I$SRC/toml11/ toml11-3.8.1/.clusterfuzzlite/parse_fuzzer.cpp000066400000000000000000000004641454646465200214430ustar00rootroot00000000000000 #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { std::string s(reinterpret_cast(data), size); std::istringstream iss(s); try { const auto ref = toml::parse(iss); } catch (...) { } return 0; } toml11-3.8.1/.clusterfuzzlite/project.yaml000066400000000000000000000000161454646465200205430ustar00rootroot00000000000000language: c++ toml11-3.8.1/.editorconfig000066400000000000000000000002021454646465200153270ustar00rootroot00000000000000root = true [*] indent_style = space [CMakeLists.txt] indent_size = 4 [*.{c,h}*] indent_size = 4 [*.{md,yml}] indent_size = 2 toml11-3.8.1/.github/000077500000000000000000000000001454646465200142205ustar00rootroot00000000000000toml11-3.8.1/.github/workflows/000077500000000000000000000000001454646465200162555ustar00rootroot00000000000000toml11-3.8.1/.github/workflows/cflite_pr.yml000066400000000000000000000014551454646465200207540ustar00rootroot00000000000000name: ClusterFuzzLite PR fuzzing on: workflow_dispatch: pull_request: branches: [ master ] permissions: read-all jobs: PR: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: [address] steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: ${{ matrix.sanitizer }} language: c++ bad-build-check: false - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 240 mode: 'code-change' report-unreproducible-crashes: false sanitizer: ${{ matrix.sanitizer }} toml11-3.8.1/.github/workflows/main.yml000066400000000000000000000234501454646465200177300ustar00rootroot00000000000000name: build on: [push, pull_request] jobs: build-linux-gcc: runs-on: Ubuntu-22.04 strategy: matrix: compiler: ['g++-12', 'g++-11', 'g++-10', 'g++-9'] standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | sudo apt-add-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install libboost-test-dev sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install ${{ matrix.compiler }} - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-linux-clang: runs-on: Ubuntu-22.04 strategy: matrix: compiler: ['15', '14', '13', '12', '11'] standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] exclude: - {compiler: '14', standard: '20'} # to avoid using gcc-13 libstdc++ - {compiler: '13', standard: '20'} # with older clang - {compiler: '12', standard: '20'} - {compiler: '11', standard: '20'} steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | sudo apt-add-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install libboost-test-dev sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install clang-${{ matrix.compiler }} - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-linux-old-gcc: runs-on: Ubuntu-20.04 strategy: matrix: compiler: ['g++-8', 'g++-7'] standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] exclude: - {compiler: 'g++-7', standard: '20'} steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | sudo apt-add-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install libboost-test-dev sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install ${{ matrix.compiler }} - name: Configure run: | mkdir build && cd build if [[ "${{ matrix.compiler }}" == "g++-8" && ( "${{ matrix.standard }}" == "17" || "${{ matrix.standard }}" == "20" ) ]] ; then cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=ON -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} else cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} fi - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-linux-old-clang: runs-on: Ubuntu-20.04 strategy: matrix: compiler: ['10', '9', '8', '7', '6.0'] standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] exclude: - {compiler: '6.0', standard: '20'} - {compiler: '7', standard: '20'} - {compiler: '8', standard: '20'} - {compiler: '9', standard: '20'} steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | sudo apt-add-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install libboost-test-dev sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install clang-${{ matrix.compiler }} - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure # build-osx-13-arm64: # runs-on: macos-13-arm64 # strategy: # matrix: # standard: ['11', '14', '17', '20'] # unreleased: ['ON', 'OFF'] # steps: # - name: Checkout # uses: actions/checkout@v3 # with: # submodules: true # - name: Install # run: | # brew install boost # - name: Configure # run: | # mkdir build && cd build # cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} # - name: Build # run: | # cd build && cmake --build . # - name: Test # run: | # cd build && ctest --output-on-failure build-osx-13: runs-on: macos-13 strategy: matrix: standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | brew install boost - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-osx-12: runs-on: macos-12 strategy: matrix: standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | brew install boost - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-osx-11: runs-on: macos-11 strategy: matrix: standard: ['11', '14', '17', '20'] unreleased: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | brew install boost - name: Configure run: | mkdir build && cd build cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build run: | cd build && cmake --build . - name: Test run: | cd build && ctest --output-on-failure build-windows-msvc: runs-on: windows-2022 strategy: matrix: standard: ['11', '14', '17', '20'] config: ['Release', 'Debug'] unreleased: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v3 with: submodules: true - name: Install run: | (New-Object System.Net.WebClient).DownloadFile("https://github.com/actions/boost-versions/releases/download/1.72.0-20200608.4/boost-1.72.0-win32-msvc14.2-x86_64.tar.gz", "$env:TEMP\\boost.tar.gz") 7z.exe x "$env:TEMP\\boost.tar.gz" -o"$env:TEMP\\boostArchive" -y | Out-Null 7z.exe x "$env:TEMP\\boostArchive" -o"$env:TEMP\\boost" -y | Out-Null Push-Location -Path "$env:TEMP\\boost" Invoke-Expression .\\setup.ps1 - uses: ilammy/msvc-dev-cmd@v1 - name: Configure shell: cmd run: | file --mime-encoding tests/test_literals.cpp mkdir build cd build cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT="C:\\hostedtoolcache\\windows\\Boost\\1.72.0\\x86_64" -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }} - name: Build working-directory: ./build run: | cmake --build . --config "${{ matrix.config }}" - name: Test working-directory: ./build run: | file --mime-encoding tests/toml/tests/example.toml file --mime-encoding tests/toml/tests/fruit.toml file --mime-encoding tests/toml/tests/hard_example.toml file --mime-encoding tests/toml/tests/hard_example_unicode.toml ctest --build-config "${{ matrix.config }}" --output-on-failure toml11-3.8.1/.gitignore000066400000000000000000000000101454646465200146370ustar00rootroot00000000000000build/* toml11-3.8.1/CMakeLists.txt000066400000000000000000000105251454646465200154230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5) enable_testing() project(toml11 VERSION 3.8.1) option(toml11_BUILD_TEST "Build toml tests" OFF) option(toml11_INSTALL "Install CMake targets during install step." ON) option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF) option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF) option(TOML11_USE_UNRELEASED_TOML_FEATURES "use features in toml-lang/toml master while testing" OFF) include(CheckCXXCompilerFlag) if("${CMAKE_VERSION}" VERSION_GREATER 3.1) set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.") if(NOT DEFINED CMAKE_CXX_STANDARD) message(FATAL_ERROR "CMAKE_CXX_STANDARD is not defined. \ The C++ standard whose features are requested to *build* all targets. \ Remember that toml11 is a header only library that does NOT require compilation to use.") endif() set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.") else() # Manually check for C++11 compiler flag. CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) CHECK_CXX_COMPILER_FLAG("-std=gnu++11" COMPILER_SUPPORTS_GNU11) CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" COMPILER_SUPPORTS_GNU0X) if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") elseif(COMPILER_SUPPORTS_CXXOX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") elseif(COMPILER_SUPPORTS_GNU11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") elseif(COMPILER_SUPPORTS_GNU0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") else() if(MSVC) if(MSVC_VERSION LESS 1900) message(SEND_ERROR "MSVC < 14.0 is not supported. Please update your compiler or use mingw") endif() else() message(SEND_ERROR "The ${CMAKE_CXX_COMPILER} compiler lacks C++11 support. Use another compiler.") endif() endif() endif() if(MSVC) # add_definitions("/Zc:__cplusplus") # define __cplusplus value correctly add_definitions("/utf-8") # enable to use u8"" literal if(MSVC_VERSION LESS 1910) message(STATUS "MSVC < 1910. DEFINE_CONVERSION_NON_INTRUSIVE is disabled") add_definitions(-DTOML11_WITHOUT_DEFINE_NON_INTRUSIVE) elseif(MSVC_VERSION LESS 1920) add_definitions("/experimental:preprocessor") # MSVC 2017 else() add_definitions("/Zc:preprocessor") # MSVC 2019 endif() endif() # Set some common directories include(GNUInstallDirs) set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11) set(toml11_install_include_dir ${CMAKE_INSTALL_INCLUDEDIR}) set(toml11_config_dir ${CMAKE_CURRENT_BINARY_DIR}/cmake/) set(toml11_config ${toml11_config_dir}/toml11Config.cmake) set(toml11_config_version ${toml11_config_dir}/toml11ConfigVersion.cmake) add_library(toml11 INTERFACE) target_include_directories(toml11 INTERFACE $ $ ) add_library(toml11::toml11 ALIAS toml11) # Write config and version config files include(CMakePackageConfigHelpers) write_basic_package_version_file( ${toml11_config_version} VERSION ${toml11_VERSION} COMPATIBILITY SameMajorVersion ) if (toml11_INSTALL) configure_package_config_file( cmake/toml11Config.cmake.in ${toml11_config} INSTALL_DESTINATION ${toml11_install_cmake_dir} PATH_VARS toml11_install_cmake_dir ) # Install config files install(FILES ${toml11_config} ${toml11_config_version} DESTINATION ${toml11_install_cmake_dir} ) # Install header files install( FILES toml.hpp DESTINATION "${toml11_install_include_dir}" ) install( DIRECTORY "toml" DESTINATION "${toml11_install_include_dir}" FILES_MATCHING PATTERN "*.hpp" ) # Export targets and install them install(TARGETS toml11 EXPORT toml11Targets ) install(EXPORT toml11Targets FILE toml11Targets.cmake DESTINATION ${toml11_install_cmake_dir} NAMESPACE toml11:: ) endif() if (toml11_BUILD_TEST) add_subdirectory(tests) endif () toml11-3.8.1/LICENSE000066400000000000000000000020651454646465200136700ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Toru Niina 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. toml11-3.8.1/README.md000066400000000000000000002004571454646465200141470ustar00rootroot00000000000000toml11 ====== [![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions) [![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master) [![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master) [![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. - It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0). - It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). - It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). - It has configurable container. You can use any random-access containers and key-value maps as backend containers. - It optionally preserves comments without any overhead. - It has configurable serializer that supports comments, inline tables, literal strings and multiline strings. - It supports user-defined type conversion from/into toml values. - It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows. ## Example ```cpp #include #include int main() { // ```toml // title = "an example toml file" // nums = [3, 1, 4, 1, 5] // ``` auto data = toml::parse("example.toml"); // find a value with the specified type from a table std::string title = toml::find(data, "title"); // convert the whole array into any container automatically std::vector nums = toml::find>(data, "nums"); // access with STL-like manner if(!data.contains("foo")) { data["foo"] = "bar"; } // pass a fallback std::string name = toml::find_or(data, "name", "not found"); // width-dependent formatting std::cout << std::setw(80) << data << std::endl; return 0; } ``` ## Table of Contents - [Integration](#integration) - [Decoding a toml file](#decoding-a-toml-file) - [In the case of syntax error](#in-the-case-of-syntax-error) - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) - [Finding a toml value](#finding-a-toml-value) - [Finding a value in a table](#finding-a-value-in-a-table) - [In case of error](#in-case-of-error) - [Dotted keys](#dotted-keys) - [Casting a toml value](#casting-a-toml-value) - [Checking value type](#checking-value-type) - [More about conversion](#more-about-conversion) - [Converting an array](#converting-an-array) - [Converting a table](#converting-a-table) - [Getting an array of tables](#getting-an-array-of-tables) - [Cost of conversion](#cost-of-conversion) - [Converting datetime and its variants](#converting-datetime-and-its-variants) - [Getting with a fallback](#getting-with-a-fallback) - [Expecting conversion](#expecting-conversion) - [Visiting a toml::value](#visiting-a-tomlvalue) - [Constructing a toml::value](#constructing-a-tomlvalue) - [Preserving Comments](#preserving-comments) - [Customizing containers](#customizing-containers) - [TOML literal](#toml-literal) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [Obtaining location information](#obtaining-location-information) - [Exceptions](#exceptions) - [Colorize Error Messages](#colorize-error-messages) - [Serializing TOML data](#serializing-toml-data) - [Underlying types](#underlying-types) - [Unreleased TOML features](#unreleased-toml-features) - [Breaking Changes from v2](#breaking-changes-from-v2) - [Running Tests](#running-tests) - [Contributors](#contributors) - [Licensing Terms](#licensing-terms) ## Integration Just include the file after adding it to the include path. ```cpp #include // that's all! now you can use it. #include int main() { const auto data = toml::parse("example.toml"); const auto title = toml::find(data, "title"); std::cout << "the title is " << title << std::endl; return 0; } ``` The convenient way is to add this repository as a git-submodule or to install it in your system by CMake. Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly. ### Example installation For local installation build & use the provided install target ```bash git clone https://github.com/ToruNiina/toml11.git mkdir -p toml11/build cd toml11/build cmake .. -DCMAKE_CXX_STANDARD=11 sudo make install ``` In case you want to create a `.deb` you can use `checkinstall`. ```bash sudo checkinstall [[ .. skipping for clarity ]] ********************************************************************** Done. The new package has been installed and saved to /home/user/toml11/build/build_20230728-1_amd64.deb You can remove it from your system anytime using: dpkg -r build ********************************************************************** ``` You should get a package that you can install with `dpkg -i .deb` and remove with `dpkg -r .deb` ## Decoding a toml file To parse a toml file, the only thing you have to do is to pass a filename to the `toml::parse` function. ```cpp const std::string fname("sample.toml"); const toml::value data = toml::parse(fname); ``` As required by the TOML specification, the top-level value is always a table. You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`. If it encounters an error while opening a file, it will throw `std::runtime_error`. You can also pass a `std::istream` to the `toml::parse` function. To show a filename in an error message, however, it is recommended to pass the filename with the stream. ```cpp std::ifstream ifs("sample.toml", std::ios_base::binary); assert(ifs.good()); const auto data = toml::parse(ifs, /*optional -> */ "sample.toml"); ``` **Note**: When you are **on Windows, open a file in binary mode**. If a file is opened in text-mode, CRLF ("\r\n") will automatically be converted to LF ("\n") and this causes inconsistency between file size and the contents that would be read. This causes weird error. ### In the case of syntax error If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error` that inherits `std::exception`. toml11 has clean and informative error messages inspired by Rust and it looks like the following. ```console terminate called after throwing an instance of 'toml::syntax_error' what(): [error] toml::parse_table: invalid line format # error description --> example.toml # file name 3 | a = 42 = true # line num and content | ^------ expected newline, but got '='. # error reason ``` If you (mistakenly) duplicate tables and got an error, it is helpful to see where they are. toml11 shows both at the same time like the following. ```console terminate called after throwing an instance of 'toml::syntax_error' what(): [error] toml::insert_value: table ("table") already exists. --> duplicate-table.toml 1 | [table] | ~~~~~~~ table already exists here ... 3 | [table] | ~~~~~~~ table defined twice ``` When toml11 encounters a malformed value, it tries to detect what type it is. Then it shows hints to fix the format. An error message while reading one of the malformed files in [the language agnostic test suite](https://github.com/BurntSushi/toml-test). is shown below. ```console what(): [error] bad time: should be HH:MM:SS.subsec --> ./datetime-malformed-no-secs.toml 1 | no-secs = 1987-07-05T17:45Z | ^------- HH:MM:SS.subsec | Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32 ``` You can find other examples in a job named `output_result` on [CircleCI](https://circleci.com/gh/ToruNiina/toml11). Since the error message generation is generally a difficult task, the current status is not ideal. If you encounter a weird error message, please let us know and contribute to improve the quality! ### Invalid UTF-8 codepoints It throws `syntax_error` if a value of an escape sequence representing unicode character is not a valid UTF-8 codepoint. ```console what(): [error] toml::read_utf8_codepoint: input codepoint is too large. --> utf8.toml 1 | exceeds_unicode = "\U0011FFFF example" | ^--------- should be in [0x00..0x10FFFF] ``` ## Finding a toml value After parsing successfully, you can obtain the values from the result of `toml::parse` using `toml::find` function. ```toml # sample.toml answer = 42 pi = 3.14 numbers = [1,2,3] time = 1979-05-27T07:32:00Z ``` ``` cpp const auto data = toml::parse("sample.toml"); const auto answer = toml::find(data, "answer"); const auto pi = toml::find(data, "pi"); const auto numbers = toml::find>(data, "numbers"); const auto timepoint = toml::find(data, "time"); ``` By default, `toml::find` returns a `toml::value`. ```cpp const toml::value& answer = toml::find(data, "answer"); ``` When you pass an exact TOML type that does not require type conversion, `toml::find` returns a reference without copying the value. ```cpp const auto data = toml::parse("sample.toml"); const auto& answer = toml::find(data, "answer"); ``` If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). **NOTE**: For some technical reason, automatic conversion between `integer` and `floating` is not supported. If you want to get a floating value even if a value has integer value, you need to convert it manually after obtaining a value, like the following. ```cpp const auto vx = toml::find(data, "x"); double x = vx.is_floating() ? vx.as_floating(std::nothrow) : static_cast(vx.as_integer()); // it throws if vx is neither // floating nor integer. ``` ### Finding a value in a table There are several way to get a value defined in a table. First, you can get a table as a normal value and find a value from the table. ```toml [fruit] name = "apple" [fruit.physical] color = "red" shape = "round" ``` ``` cpp const auto data = toml::parse("fruit.toml"); const auto& fruit = toml::find(data, "fruit"); const auto name = toml::find(fruit, "name"); const auto& physical = toml::find(fruit, "physical"); const auto color = toml::find(physical, "color"); const auto shape = toml::find(physical, "shape"); ``` Here, variable `fruit` is a `toml::value` and can be used as the first argument of `toml::find`. Second, you can pass as many arguments as the number of subtables to `toml::find`. ```cpp const auto data = toml::parse("fruit.toml"); const auto color = toml::find(data, "fruit", "physical", "color"); const auto shape = toml::find(data, "fruit", "physical", "shape"); ``` ### Finding a value in an array You can find n-th value in an array by `toml::find`. ```toml values = ["foo", "bar", "baz"] ``` ``` cpp const auto data = toml::parse("sample.toml"); const auto values = toml::find(data, "values"); const auto bar = toml::find(values, 1); ``` `toml::find` can also search array recursively. ```cpp const auto data = toml::parse("fruit.toml"); const auto bar = toml::find(data, "values", 1); ``` Before calling `toml::find`, you can check if a value corresponding to a key exists. You can use both `bool toml::value::contains(const key&) const` and `std::size_t toml::value::count(const key&) const`. Those behaves like the `std::map::contains` and `std::map::count`. ```cpp const auto data = toml::parse("fruit.toml"); if(data.contains("fruit") && data.at("fruit").count("physical") != 0) { // ... } ``` ### In case of error If the value does not exist, `toml::find` throws `std::out_of_range` with the location of the table. ```console terminate called after throwing an instance of 'std::out_of_range' what(): [error] key "answer" not found --> example.toml 6 | [tab] | ~~~~~ in this table ``` ---- If the specified type differs from the actual value contained, it throws `toml::type_error` that inherits `std::exception`. Similar to the case of syntax error, toml11 also displays clean error messages. The error message when you choose `int` to get `string` value would be like this. ```console terminate called after throwing an instance of 'toml::type_error' what(): [error] toml::value bad_cast to integer --> example.toml 3 | title = "TOML Example" | ~~~~~~~~~~~~~~ the actual type is string ``` **NOTE**: In order to show this kind of error message, all the toml values have a pointer to represent its range in a file. The entire contents of a file is shared by `toml::value`s and remains on the heap memory. It is recommended to destruct all the `toml::value` classes after configuring your application if you have a large TOML file compared to the memory resource. ### Dotted keys TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data. ```toml physical.color = "orange" physical.shape = "round" ``` This is equivalent to the following. ```toml [physical] color = "orange" shape = "round" ``` You can get both of the above tables with the same c++ code. ```cpp const auto physical = toml::find(data, "physical"); const auto color = toml::find(physical, "color"); ``` The following code does not work for the above toml file. ```cpp // XXX this does not work! const auto color = toml::find(data, "physical.color"); ``` The above code works with the following toml file. ```toml "physical.color" = "orange" # equivalent to {"physical.color": "orange"}, # NOT {"physical": {"color": "orange"}}. ``` ## Casting a toml value ### `toml::get` `toml::parse` returns `toml::value`. `toml::value` is a union type that can contain one of the following types. - `toml::boolean` (`bool`) - `toml::integer` (`std::int64_t`) - `toml::floating` (`double`) - `toml::string` (a type convertible to std::string) - `toml::local_date` - `toml::local_time` - `toml::local_datetime` - `toml::offset_datetime` - `toml::array` (by default, `std::vector`) - It depends. See [customizing containers](#customizing-containers) for detail. - `toml::table` (by default, `std::unordered_map`) - It depends. See [customizing containers](#customizing-containers) for detail. To get a value inside, you can use `toml::get()`. The usage is the same as `toml::find` (actually, `toml::find` internally uses `toml::get` after casting a value to `toml::table`). ``` cpp const toml::value data = toml::parse("sample.toml"); const toml::value answer_ = toml::get(data).at("answer"); const std::int64_t answer = toml::get(answer_); ``` When you pass an exact TOML type that does not require type conversion, `toml::get` returns a reference through which you can modify the content (if the `toml::value` is `const`, it returns `const` reference). ```cpp toml::value data = toml::parse("sample.toml"); toml::value answer_ = toml::get(data).at("answer"); toml::integer& answer = toml::get(answer_); answer = 6 * 9; // write to data.answer. now `answer_` contains 54. ``` If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). It also throws a `toml::type_error` if the type differs. ### `as_xxx` You can also use a member function to cast a value. ```cpp const std::int64_t answer = data.as_table().at("answer").as_integer(); ``` It also throws a `toml::type_error` if the type differs. If you are sure that the value `v` contains a value of the specified type, you can suppress checking by passing `std::nothrow`. ```cpp const auto& answer = data.as_table().at("answer"); if(answer.is_integer() && answer.as_integer(std::nothrow) == 42) { std::cout << "value is 42" << std::endl; } ``` If `std::nothrow` is passed, the functions are marked as noexcept. By casting a `toml::value` into an array or a table, you can iterate over the elements. ```cpp const auto data = toml::parse("example.toml"); std::cout << "keys in the top-level table are the following: \n"; for(const auto& kv : data.as_table()) { std::cout << kv.first << '\n'; } for(const auto& [k, v] : data.as_table()) // or in C++17 { std::cout << k << '\n'; } const auto& fruits = toml::find(data, "fruits"); for(const auto& v : fruits.as_array()) { std::cout << toml::find(v, "name") << '\n'; } ``` The full list of the functions is below. ```cpp namespace toml { class value { // ... const boolean& as_boolean() const&; const integer& as_integer() const&; const floating& as_floating() const&; const string& as_string() const&; const offset_datetime& as_offset_datetime() const&; const local_datetime& as_local_datetime() const&; const local_date& as_local_date() const&; const local_time& as_local_time() const&; const array& as_array() const&; const table& as_table() const&; // -------------------------------------------------------- // non-const version boolean& as_boolean() &; // ditto... // -------------------------------------------------------- // rvalue version boolean&& as_boolean() &&; // ditto... // -------------------------------------------------------- // noexcept versions ... const boolean& as_boolean(const std::nothrow_t&) const& noexcept; boolean& as_boolean(const std::nothrow_t&) & noexcept; boolean&& as_boolean(const std::nothrow_t&) && noexcept; // ditto... }; } // toml ``` ### `at()` You can access to the element of a table and an array by `toml::basic_value::at`. ```cpp const toml::value v{1,2,3,4,5}; std::cout << v.at(2).as_integer() << std::endl; // 3 const toml::value v{{"foo", 42}, {"bar", 3.14}}; std::cout << v.at("foo").as_integer() << std::endl; // 42 ``` If an invalid key (integer for a table, string for an array), it throws `toml::type_error` for the conversion. If the provided key is out-of-range, it throws `std::out_of_range`. Note that, although `std::string` has `at()` member function, `toml::value::at` throws if the contained type is a string. Because `std::string` does not contain `toml::value`. ### `operator[]` You can also access to the element of a table and an array by `toml::basic_value::operator[]`. ```cpp const toml::value v{1,2,3,4,5}; std::cout << v[2].as_integer() << std::endl; // 3 const toml::value v{{"foo", 42}, {"bar", 3.14}}; std::cout << v["foo"].as_integer() << std::endl; // 42 ``` When you access to a `toml::value` that is not initialized yet via `operator[](const std::string&)`, the `toml::value` will be a table, just like the `std::map`. ```cpp toml::value v; // not initialized as a table. v["foo"] = 42; // OK. `v` will be a table. ``` Contrary, if you access to a `toml::value` that contains an array via `operator[]`, it does not check anything. It converts `toml::value` without type check and then access to the n-th element without boundary check, just like the `std::vector::operator[]`. ```cpp toml::value v; // not initialized as an array v[2] = 42; // error! UB ``` Please make sure that the `toml::value` has an array inside when you access to its element via `operator[]`. ## Checking value type You can check the type of a value by `is_xxx` function. ```cpp const toml::value v = /* ... */; if(v.is_integer()) { std::cout << "value is an integer" << std::endl; } ``` The complete list of the functions is below. ```cpp namespace toml { class value { // ... bool is_boolean() const noexcept; bool is_integer() const noexcept; bool is_floating() const noexcept; bool is_string() const noexcept; bool is_offset_datetime() const noexcept; bool is_local_datetime() const noexcept; bool is_local_date() const noexcept; bool is_local_time() const noexcept; bool is_array() const noexcept; bool is_table() const noexcept; bool is_uninitialized() const noexcept; // ... }; } // toml ``` Also, you can get `enum class value_t` from `toml::value::type()`. ```cpp switch(data.at("something").type()) { case toml::value_t::integer: /*do some stuff*/ ; break; case toml::value_t::floating: /*do some stuff*/ ; break; case toml::value_t::string : /*do some stuff*/ ; break; default : throw std::runtime_error( "unexpected type : " + toml::stringize(data.at("something").type())); } ``` The complete list of the `enum`s can be found in the section [underlying types](#underlying-types). The `enum`s can be used as a parameter of `toml::value::is` function like the following. ```cpp toml::value v = /* ... */; if(v.is(toml::value_t::boolean)) // ... ``` ## More about conversion Since `toml::find` internally uses `toml::get`, all the following examples work with both `toml::get` and `toml::find`. ### Converting an array You can get any kind of `container` class from a `toml::array` except for `map`-like classes. ``` cpp // # sample.toml // numbers = [1,2,3] const auto numbers = toml::find(data, "numbers"); const auto vc = toml::get >(numbers); const auto ls = toml::get >(numbers); const auto dq = toml::get >(numbers); const auto ar = toml::get>(numbers); // if the size of data.at("numbers") is larger than that of std::array, // it will throw toml::type_error because std::array is not resizable. ``` Surprisingly, you can convert `toml::array` into `std::pair` and `std::tuple`. ```cpp // numbers = [1,2,3] const auto tp = toml::get>(numbers); ``` This functionality is helpful when you have a toml file like the following. ```toml array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this ``` What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s. ```cpp const auto array_of_arrays = toml::find(data, "array_of_arrays"); const auto aofa = toml::get< std::pair, std::vector> >(array_of_arrays); ``` If you don't know the type of the elements, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead. ```cpp const auto a_of_a = toml::get(array_of_arrays); const auto first = toml::get>(a_of_a.at(0)); ``` You can change the implementation of `toml::array` with `std::deque` or some other array-like container. See [Customizing containers](#customizing-containers) for detail. ### Converting a table When all the values of the table have the same type, toml11 allows you to convert a `toml::table` to a `map` that contains the convertible type. ```toml [tab] key1 = "foo" # all the values are key2 = "bar" # toml String ``` ```cpp const auto data = toml::parse("sample.toml"); const auto tab = toml::find>(data, "tab"); std::cout << tab["key1"] << std::endl; // foo std::cout << tab["key2"] << std::endl; // bar ``` But since `toml::table` is just an alias of `std::unordered_map`, normally you don't need to convert it because it has all the functionalities that `std::unordered_map` has (e.g. `operator[]`, `count`, and `find`). In most cases `toml::table` is sufficient. ```cpp toml::table tab = toml::get(data); if(data.count("title") != 0) { data["title"] = std::string("TOML example"); } ``` You can change the implementation of `toml::table` with `std::map` or some other map-like container. See [Customizing containers](#customizing-containers) for detail. ### Getting an array of tables An array of tables is just an array of tables. You can get it in completely the same way as the other arrays and tables. ```toml # sample.toml array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}] [[array_of_tables]] key = "value4" [[array_of_tables]] key = "value5" [[array_of_tables]] key = "value6" ``` ```cpp const auto data = toml::parse("sample.toml"); const auto aot1 = toml::find>(data, "array_of_inline_tables"); const auto aot2 = toml::find>(data, "array_of_tables"); ``` ### Cost of conversion Although conversion through `toml::(get|find)` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to the user-specified type. Of course in some cases this overhead is not ignorable. ```cpp // the following code constructs a std::vector. // it requires heap allocation for vector and element conversion. const auto array = toml::find>(data, "foo"); ``` By passing the exact types, `toml::get` returns reference that has no overhead. ``` cpp const auto& tab = toml::find(data, "tab"); const auto& numbers = toml::find(data, "numbers"); ``` Also, `as_xxx` are zero-overhead because they always return a reference. ``` cpp const auto& tab = toml::find(data, "tab" ).as_table(); const auto& numbers = toml::find(data, "numbers").as_array(); ``` In this case you need to call `toml::get` each time you access to the element of `toml::array` because `toml::array` is an array of `toml::value`. ```cpp const auto& num0 = toml::get(numbers.at(0)); const auto& num1 = toml::get(numbers.at(1)); const auto& num2 = toml::get(numbers.at(2)); ``` ### Converting datetime and its variants TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. Since `local_date`, `local_datetime`, and `offset_datetime` represent a time point, you can convert them to `std::chrono::system_clock::time_point`. Contrary, `local_time` does not represents a time point because they lack a date information, but it can be converted to `std::chrono::duration` that represents a duration from the beginning of the day, `00:00:00.000`. ```toml # sample.toml date = 2018-12-23 time = 12:30:00 l_dt = 2018-12-23T12:30:00 o_dt = 2018-12-23T12:30:00+09:30 ``` ```cpp const auto data = toml::parse("sample.toml"); const auto date = toml::get(data.at("date")); const auto l_dt = toml::get(data.at("l_dt")); const auto o_dt = toml::get(data.at("o_dt")); const auto time = toml::get(data.at("time")); // 12 * 60 + 30 min ``` `local_date` and `local_datetime` are assumed to be in the local timezone when they are converted into `time_point`. On the other hand, `offset_datetime` only uses the offset part of the data and it does not take local timezone into account. To contain datetime data, toml11 defines its own datetime types. For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). ## Getting with a fallback `toml::find_or` returns a default value if the value is not found or has a different type. ```cpp const auto data = toml::parse("example.toml"); const auto num = toml::find_or(data, "num", 42); ``` It works recursively if you pass several keys for subtables. In that case, the last argument is considered to be the optional value. All other arguments between `toml::value` and the optional value are considered as keys. ```cpp // [fruit.physical] // color = "red" auto data = toml::parse("fruit.toml"); auto color = toml::find_or(data, "fruit", "physical", "color", "red"); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ // arguments optional value ``` Also, `toml::get_or` returns a default value if `toml::get` failed. ```cpp toml::value v("foo"); // v contains String const int value = toml::get_or(v, 42); // conversion fails. it returns 42. ``` These functions automatically deduce what type you want to get from the default value you passed. To get a reference through this function, take care about the default value. ```cpp toml::value v("foo"); // v contains String toml::integer& i = toml::get_or(v, 42); // does not work because binding `42` // to `integer&` is invalid toml::integer opt = 42; toml::integer& i = toml::get_or(v, opt); // this works. ``` ## Expecting conversion By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`. ```cpp const auto value = toml::expect(data.at("title")); if(value.is_ok()) { std::cout << value.unwrap() << std::endl; } else { std::cout << value.unwrap_err() << std::endl; } ``` Also, you can pass a function object to modify the expected value. ```cpp const auto value = toml::expect(data.at("number")) .map(// function that receives expected type (here, int) [](const int number) -> double { return number * 1.5 + 1.0; }).unwrap_or(/*default value =*/ 3.14); ``` ## Visiting a toml::value toml11 provides `toml::visit` to apply a function to `toml::value` in the same way as `std::variant`. ```cpp const toml::value v(3.14); toml::visit([](const auto& val) -> void { std::cout << val << std::endl; }, v); ``` The function object that would be passed to `toml::visit` must be able to receive all the possible TOML types. Also, the result types should be the same each other. ## Constructing a toml::value `toml::value` can be constructed in various ways. ```cpp toml::value v(true); // boolean toml::value v(42); // integer toml::value v(3.14); // floating toml::value v("foobar"); // string toml::value v(toml::local_date(2019, toml::month_t::Apr, 1)); // date toml::value v{1, 2, 3, 4, 5}; // array toml::value v{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; // table ``` When constructing a string, you can choose to use either literal or basic string. By default, it will be a basic string. ```cpp toml::value v("foobar", toml::string_t::basic ); toml::value v("foobar", toml::string_t::literal); ``` Datetime objects can be constructed from `std::tm` and `std::chrono::system_clock::time_point`. But you need to specify what type you use to avoid ambiguity. ```cpp const auto now = std::chrono::system_clock::now(); toml::value v(toml::local_date(now)); toml::value v(toml::local_datetime(now)); toml::value v(toml::offset_datetime(now)); ``` Since local time is not equivalent to a time point, because it lacks date information, it will be constructed from `std::chrono::duration`. ```cpp toml::value v(toml::local_time(std::chrono::hours(10))); ``` You can construct an array object not only from `initializer_list`, but also from STL containers. In that case, the element type must be convertible to `toml::value`. ```cpp std::vector vec{1,2,3,4,5}; toml::value v(vec); ``` When you construct an array value, all the elements of `initializer_list` must be convertible into `toml::value`. If a `toml::value` has an array, you can `push_back` an element in it. ```cpp toml::value v{1,2,3,4,5}; v.push_back(6); ``` `emplace_back` also works. ## Preserving comments toml11 v3 or later allows you yo choose whether comments are preserved or not via template parameter ```cpp const auto data1 = toml::parse("example.toml"); const auto data2 = toml::parse("example.toml"); ``` or macro definition. ```cpp #define TOML11_PRESERVE_COMMENTS_BY_DEFAULT #include ``` This feature is controlled by template parameter in `toml::basic_value<...>`. `toml::value` is an alias of `toml::basic_value<...>`. If template parameter is explicitly specified, the return value of `toml::parse` will be `toml::basic_value`. If the macro is defined, the alias `toml::value` will be `toml::basic_value`. Comments related to a value can be obtained by `toml::value::comments()`. The return value has the same interface as `std::vector`. ```cpp const auto& com = v.comments(); for(const auto& c : com) { std::cout << c << std::endl; } ``` Comments just before and just after (within the same line) a value are kept in a value. ```toml # this is a comment for v1. v1 = "foo" v2 = "bar" # this is a comment for v2. # Note that this comment is NOT a comment for v2. # this comment is not related to any value # because there are empty lines between v3. # this comment will be ignored even if you set `preserve_comments`. # this is a comment for v3 # this is also a comment for v3. v3 = "baz" # ditto. ``` Each comment line becomes one element of a `std::vector`. Hash signs will be removed, but spaces after hash sign will not be removed. ```cpp v1.comments().at(0) == " this is a comment for v1."s; v2.comments().at(1) == " this is a comment for v1."s; v3.comments().at(0) == " this is a comment for v3."s; v3.comments().at(1) == " this is also a comment for v3."s; v3.comments().at(2) == " ditto."s; ``` Note that a comment just after an opening brace of an array will not be a comment for the array. ```toml # this is a comment for a. a = [ # this is not a comment for a. this will be ignored. 1, 2, 3, # this is a comment for `42`. 42, # this is also a comment for `42`. 5 ] # this is a comment for a. ``` You can also append and modify comments. The interfaces are the same as `std::vector`. ```cpp toml::basic_value v(42); v.comments().push_back(" add this comment."); // # add this comment. // i = 42 ``` Also, you can pass a `std::vector` when constructing a `toml::basic_value`. ```cpp std::vector comments{"comment 1", "comment 2"}; const toml::basic_value v1(42, std::move(comments)); const toml::basic_value v2(42, {"comment 1", "comment 2"}); ``` When `toml::discard_comments` is chosen, comments will not be contained in a value. `value::comments()` will always be kept empty. All the modification on comments would be ignored. All the element access in a `discard_comments` causes the same error as accessing an element of an empty `std::vector`. The comments will also be serialized. If comments exist, those comments will be added just before the values. __NOTE__: Result types from `toml::parse(...)` and `toml::parse(...)` are different. ## Customizing containers Actually, `toml::basic_value` has 3 template arguments. ```cpp template class Table = std::unordered_map, template class Array = std::vector> class basic_value; ``` This enables you to change the containers used inside. E.g. you can use `std::map` to contain a table object instead of `std::unordered_map`. And also can use `std::deque` as a array object instead of `std::vector`. You can set these parameters while calling `toml::parse` function. ```cpp const auto data = toml::parse< toml::preserve_comments, std::map, std::deque >("example.toml"); ``` Needless to say, the result types from `toml::parse(...)` and `toml::parse(...)` are different (unless you specify the same types as default). Note that, since `toml::table` and `toml::array` is an alias for a table and an array of a default `toml::value`, so it is different from the types actually contained in a `toml::basic_value` when you customize containers. To get the actual type in a generic way, use `typename toml::basic_type::table_type` and `typename toml::basic_type::array_type`. ## TOML literal toml11 supports `"..."_toml` literal. It accept both a bare value and a file content. ```cpp using namespace toml::literals::toml_literals; // `_toml` can convert a bare value without key const toml::value v = u8"0xDEADBEEF"_toml; // v is an Integer value containing 0xDEADBEEF. // raw string literal (`R"(...)"` is useful for this purpose) const toml::value t = u8R"( title = "this is TOML literal" [table] key = "value" )"_toml; // the literal will be parsed and the result will be contained in t ``` The literal function is defined in the same way as the standard library literals such as `std::literals::string_literals::operator""s`. ```cpp namespace toml { inline namespace literals { inline namespace toml_literals { toml::value operator"" _toml(const char* str, std::size_t len); } // toml_literals } // literals } // toml ``` Access to the operator can be gained with `using namespace toml::literals;`, `using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. Note that a key that is composed only of digits is allowed in TOML. And, unlike the file parser, toml-literal allows a bare value without a key. Thus it is difficult to distinguish arrays having integers and definitions of tables that are named as digits. Currently, literal `[1]` becomes a table named "1". To ensure a literal to be considered as an array with one element, you need to add a comma after the first element (like `[1,]`). ```cpp "[1,2,3]"_toml; // This is an array "[table]"_toml; // This is a table that has an empty table named "table" inside. "[[1,2,3]]"_toml; // This is an array of arrays "[[table]]"_toml; // This is a table that has an array of tables inside. "[[1]]"_toml; // This literal is ambiguous. // Currently, it becomes an empty array of table named "1". "1 = [{}]"_toml; // This is a table that has an array of table named 1. "[[1,]]"_toml; // This is an array of arrays. "[[1],]"_toml; // ditto. ``` NOTE: `_toml` literal returns a `toml::value` that does not have comments. ## Conversion between toml value and arbitrary types You can also use `toml::get` and other related functions with the types you defined after you implement a way to convert it. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext const auto data = toml::parse("example.toml"); // to do this const foo f = toml::find(data, "foo"); ``` There are 3 ways to use `toml::get` with the types that you defined. The first one is to implement `from_toml(const toml::value&)` member function. ```cpp namespace ext { struct foo { int a; double b; std::string c; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); this->c = toml::find(v, "c"); return; } }; } // ext ``` In this way, because `toml::get` first constructs `foo` without arguments, the type should be default-constructible. The second is to implement `constructor(const toml::value&)`. ```cpp namespace ext { struct foo { explicit foo(const toml::value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")), c(toml::find(v, "c")) {} int a; double b; std::string c; }; } // ext ``` Note that implicit default constructor declaration will be suppressed when a constructor is defined. If you want to use the struct (here, `foo`) in a container (e.g. `std::vector`), you may need to define default constructor explicitly. The third is to implement specialization of `toml::from` for your type. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext namespace toml { template<> struct from { static ext::foo from_toml(const value& v) { ext::foo f; f.a = find(v, "a"); f.b = find(v, "b"); f.c = find(v, "c"); return f; } }; } // toml ``` In this way, since the conversion function is defined outside of the class, you can add conversion between `toml::value` and classes defined in another library. In some cases, a class has a templatized constructor that takes a template, `T`. It confuses `toml::get/find` because it makes the class "constructible" from `toml::value`. To avoid this problem, `toml::from` and `from_toml` always precede constructor. It makes easier to implement conversion between `toml::value` and types defined in other libraries because it skips constructor. But, importantly, you cannot define `toml::from` and `T.from_toml` at the same time because it causes ambiguity in the overload resolution of `toml::get` and `toml::find`. So the precedence is `toml::from` == `T.from_toml()` > `T(toml::value)`. If you want to convert any versions of `toml::basic_value`, you need to templatize the conversion function as follows. ```cpp struct foo { template class M, template class A> void from_toml(const toml::basic_value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); this->c = toml::find(v, "c"); return; } }; // or namespace toml { template<> struct from { template class M, template class A> static ext::foo from_toml(const basic_value& v) { ext::foo f; f.a = find(v, "a"); f.b = find(v, "b"); f.c = find(v, "c"); return f; } }; } // toml ``` ---- The opposite direction is also supported in a similar way. You can directly pass your type to `toml::value`'s constructor by introducing `into_toml` or `toml::into`. ```cpp namespace ext { struct foo { int a; double b; std::string c; toml::value into_toml() const // you need to mark it const. { return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}}; } }; } // ext ext::foo f{42, 3.14, "foobar"}; toml::value v(f); ``` The definition of `toml::into` is similar to `toml::from`. ```cpp namespace ext { struct foo { int a; double b; std::string c; }; } // ext namespace toml { template<> struct into { static toml::value into_toml(const ext::foo& f) { return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}}; } }; } // toml ext::foo f{42, 3.14, "foobar"}; toml::value v(f); ``` Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and `toml::array` are okay to return from `into_toml`. You can also return a custom `toml::basic_value` from `toml::into`. ```cpp namespace toml { template<> struct into { static toml::basic_value into_toml(const ext::foo& f) { toml::basic_value v{{"a", f.a}, {"b", f.b}, {"c", f.c}}; v.comments().push_back(" comment"); return v; } }; } // toml ``` But note that, if this `basic_value` would be assigned into other `toml::value` that discards `comments`, the comments would be dropped. ### Macro to automatically define conversion functions There is a helper macro that automatically generates conversion functions `from` and `into` for a simple struct. ```cpp namespace foo { struct Foo { std::string s; double d; int i; }; } // foo TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) int main() { const auto file = toml::parse("example.toml"); auto f = toml::find(file, "foo"); } ``` And then you can use `toml::find(file, "foo");` **Note** that, because of a slight difference in implementation of preprocessor between gcc/clang and MSVC, [you need to define `/Zc:preprocessor`](https://github.com/ToruNiina/toml11/issues/139#issuecomment-803683682) to use it in MSVC (Thank you @glebm !). ## Formatting user-defined error messages When you encounter an error after you read the toml value, you may want to show the error with the value. toml11 provides you a function that formats user-defined error message with related values. With a code like the following, ```cpp const auto value = toml::find(data, "num"); if(value < 0) { std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required") << std::endl; } ``` you will get an error message like this. ```console [error] value should be positive --> example.toml 3 | num = -42 | ~~~ positive number required ``` When you pass two values to `toml::format_error`, ```cpp const auto min = toml::find(range, "min"); const auto max = toml::find(range, "max"); if(max < min) { std::cerr << toml::format_error("[error] max should be larger than min", data.at("min"), "minimum number here", data.at("max"), "maximum number here"); << std::endl; } ``` you will get an error message like this. ```console [error] max should be larger than min --> example.toml 3 | min = 54 | ~~ minimum number here ... 4 | max = 42 | ~~ maximum number here ``` You can print hints at the end of the message. ```cpp std::vector hints; hints.push_back("positive number means n >= 0."); hints.push_back("negative number is not positive."); std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required", hints) << std::endl; ``` ```console [error] value should be positive --> example.toml 2 | num = 42 | ~~ positive number required | Hint: positive number means n >= 0. Hint: negative number is not positive. ``` ## Obtaining location information You can also format error messages in your own way by using `source_location`. ```cpp struct source_location { std::uint_least32_t line() const noexcept; std::uint_least32_t column() const noexcept; std::uint_least32_t region() const noexcept; std::string const& file_name() const noexcept; std::string const& line_str() const noexcept; }; // +-- line() +--- length of the region (here, region() == 9) // v .---+---. // 12 | value = "foo bar" <- line_str() returns the line itself. // ^-------- column() points here ``` You can get this by ```cpp const toml::value v = /*...*/; const toml::source_location loc = v.location(); ``` ## Exceptions The following `exception` classes inherits `toml::exception` that inherits `std::exception`. ```cpp namespace toml { struct exception : public std::exception {/**/}; struct syntax_error : public toml::exception {/**/}; struct type_error : public toml::exception {/**/}; struct internal_error : public toml::exception {/**/}; } // toml ``` `toml::exception` has `toml::exception::location()` member function that returns `toml::source_location`, in addition to `what()`. ```cpp namespace toml { struct exception : public std::exception { // ... source_location const& location() const noexcept; }; } // toml ``` It represents where the error occurs. `syntax_error` will be thrown from `toml::parse` and `_toml` literal. `type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and other functions that takes a content inside of `toml::value`. Note that, currently, from `toml::value::at()` and `toml::find(value, key)` may throw an `std::out_of_range` that does not inherits `toml::exception`. Also, in some cases, most likely in the file open error, it will throw an `std::runtime_error`. ## Colorize Error Messages By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from `toml::parse` and `toml::find|get` will be colorized. By default, this feature is turned off. With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`, ```toml [error] array = [ "This might most likely happen in multiline arrays", Like here, "or here, and here" ] End of array comment, forgot the # ``` the error message would be like this. ![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png) With the following, ```toml [error] # array = [ # "This might most likely happen in multiline arrays", # Like here, # "or here, # and here" # ] End of array comment, forgot the # number = 3.14 pi <--again forgot the # ``` the error message would be like this. ![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png) The message would be messy when it is written to a file, not a terminal because it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined error message by passing `true` to the `toml::format_error` function. If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default. If not, the default value would be `false`. ```cpp std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required", hints, /*colorize = */ true) << std::endl; ``` Note: It colorizes `[error]` in red. That means that it detects `[error]` prefix at the front of the error message. If there is no `[error]` prefix, `format_error` adds it to the error message. Compared to the `TOML11_COLORIZE_ERROR_MESSAGE` macro that enables colorization statically, toml11 provides `toml::color::enable` & `toml::color::disable` functions to dynamically change the color mode. This feature overwrites `TOML11_COLORIZE_ERROR_MESSAGE` and the `colorize` argument of `toml::format_error` when you call `enable`. Note: If either `TOML11_COLORIZE_ERROR_MESSAGE` is defined or the `colorize` argument is used, it takes precedence, meaning that `disable` won't work. Accordingly, we highly recommend using only one of them. ```cpp toml::color::enable(); // enable colorization toml::color::disable(); // disable colorization ``` If you use user-defined error message, you can manage the setting as follows: ```cpp toml::color::enable(); std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required", hints) << std::endl; // colorized toml::color::disable(); std::cerr << toml::format_error("[error] value should be positive", data.at("num"), "positive number required", hints) << std::endl; // NOT colorized ``` Or you may use toml11 in your application like: ```cpp std::vector args(argv + 1, argv + argc); auto result = std::find(args.begin(), args.end(), "--color"); if (result != args.end()) { toml::color::enable(); } else { toml::color::disable(); } // use toml11 ... ``` ## Opting out of the default `[error]` prefix toml11 prints error messages with the `[error]` prefix by default. Defining `TOML11_NO_ERROR_PREFIX` will let toml11 omit the prefix regardless of colorized or not in case you would use a custom prefix, such as `Error:`. ```cpp #define TOML11_NO_ERROR_PREFIX ``` ## Serializing TOML data toml11 enables you to serialize data into toml format. ```cpp const toml::value data{{"foo", 42}, {"bar", "baz"}}; std::cout << data << std::endl; // bar = "baz" // foo = 42 ``` toml11 automatically makes a small table and small array inline. You can specify the width to make them inline by `std::setw` for streams. ```cpp const toml::value data{ {"qux", {{"foo", 42}, {"bar", "baz"}}}, {"quux", {"small", "array", "of", "strings"}}, {"foobar", {"this", "array", "of", "strings", "is", "too", "long", "to", "print", "into", "single", "line", "isn't", "it?"}}, }; // the threshold becomes 80. std::cout << std::setw(80) << data << std::endl; // foobar = [ // "this","array","of","strings","is","too","long","to","print","into", // "single","line","isn't","it?", // ] // quux = ["small","array","of","strings"] // qux = {bar="baz",foo=42} // the width is 0. nothing become inline. std::cout << std::setw(0) << data << std::endl; // foobar = [ // "this", // ... (snip) // "it?", // ] // quux = [ // "small", // "array", // "of", // "strings", // ] // [qux] // bar = "baz" // foo = 42 ``` It is recommended to set width before printing data. Some I/O functions changes width to 0, and it makes all the stuff (including `toml::array`) multiline. The resulting files becomes too long. To control the precision of floating point numbers, you need to pass `std::setprecision` to stream. ```cpp const toml::value data{ {"pi", 3.141592653589793}, {"e", 2.718281828459045} }; std::cout << std::setprecision(17) << data << std::endl; // e = 2.7182818284590451 // pi = 3.1415926535897931 std::cout << std::setprecision( 7) << data << std::endl; // e = 2.718282 // pi = 3.141593 ``` There is another way to format toml values, `toml::format()`. It returns `std::string` that represents a value. ```cpp const toml::value v{{"a", 42}}; const std::string fmt = toml::format(v); // a = 42 ``` Note that since `toml::format` formats a value, the resulting string may lack the key value. ```cpp const toml::value v{3.14}; const std::string fmt = toml::format(v); // 3.14 ``` To control the width and precision, `toml::format` receives optional second and third arguments to set them. By default, the width is 80 and the precision is `std::numeric_limits::max_digit10`. ```cpp const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17); ``` When you pass a comment-preserving-value, the comment will also be serialized. An array or a table containing a value that has a comment would not be inlined. ## Underlying types The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. | TOML type | underlying c++ type | enum class | | -------------- | ---------------------------------- | -------------------------------- | | Boolean | `bool` | `toml::value_t::boolean` | | Integer | `std::int64_t` | `toml::value_t::integer` | | Float | `double` | `toml::value_t::floating` | | String | `toml::string` | `toml::value_t::string` | | LocalDate | `toml::local_date` | `toml::value_t::local_date` | | LocalTime | `toml::local_time` | `toml::value_t::local_time` | | LocalDatetime | `toml::local_datetime` | `toml::value_t::local_datetime` | | OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offset_datetime` | | Array | `array-like` | `toml::value_t::array` | | Table | `map-like` | `toml::value_t::table` | `array-like` and `map-like` are the STL containers that works like a `std::vector` and `std::unordered_map`, respectively. By default, `std::vector` and `std::unordered_map` are used. See [Customizing containers](#customizing-containers) for detail. `toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, still you can get a reference that points to internal `std::string` by using `toml::get()` for convenience. The most important difference between `std::string` and `toml::string` is that `toml::string` will be formatted as a TOML string when outputted with `ostream`. This feature is introduced to make it easy to write a custom serializer. `Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day. ## Unreleased TOML features After TOML v1.0.0 has been released, some features are added to the main branch of the TOML spec repository. (see: [CHANGELOG.md in toml-lang/toml repository](https://github.com/toml-lang/toml/blob/main/CHANGELOG.md)). The following list shows available "unreleased" features that can be activated by defining a macro named `TOML11_USE_UNRELEASED_FEATURES`. - Add new `\e` shorthand for the escape character. ## Note about heterogeneous arrays Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` does not. Here the reason is explained. ```cpp // this won't be compiled toml::value v{ "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}} } ``` There is a workaround for this. By explicitly converting values into `toml::value`, you can initialize `toml::value` with a heterogeneous array. Also, you can first initialize a `toml::value` with an array and then `push_back` into it. ```cpp // OK! toml::value v{ toml::value("foo"), toml::value(3.14), toml::value(42), toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} } // OK! toml::value v(toml::array{}); v.push_back("foo"); v.push_back(3.14); // OK! toml::array a; a.push_back("foo"); a.push_back(3.14); toml::value v(std::move(a)); ``` The reason why the first example is not allowed is the following. Let's assume that you are initializing a `toml::value` with a table. ```cpp // # expecting TOML table. toml::value v{ // [v] {"answer", 42}, // answer = 42 {"pi", 3.14}, // pi = 3.14 {"foo", "bar"} // foo = "bar" }; ``` This is indistinguishable from a (heterogeneous) TOML array definition. ```toml v = [ ["answer", 42], ["pi", 3.14], ["foo", "bar"], ] ``` This means that the above C++ code makes constructor's overload resolution ambiguous. So a constructor that allows both "table as an initializer-list" and "heterogeneous array as an initializer-list" cannot be implemented. Thus, although it is painful, we need to explicitly cast values into `toml::value` when you initialize heterogeneous array in a C++ code. ```cpp toml::value v{ toml::value("foo"), toml::value(3.14), toml::value(42), toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} }; ``` ## Breaking Changes from v2 Although toml11 is relatively new library (it's three years old now), it had some confusing and inconvenient user-interfaces because of historical reasons. Between v2 and v3, those interfaces are rearranged. - `toml::parse` now returns a `toml::value`, not `toml::table`. - `toml::value` is now an alias of `toml::basic_value`. - See [Customizing containers](#customizing-containers) for detail. - The elements of `toml::value_t` are renamed as `snake_case`. - See [Underlying types](#underlying-types) for detail. - Supports for the CamelCaseNames are dropped. - See [Underlying types](#underlying-types) for detail. - `(is|as)_float` has been removed to make the function names consistent with others. - Since `float` is a keyword, toml11 named a float type as `toml::floating`. - Also a `value_t` corresponds to `toml::floating` is named `value_t::floating`. - So `(is|as)_floating` is introduced and `is_float` has been removed. - See [Casting a toml::value](#casting-a-tomlvalue) and [Checking value type](#checking-value-type) for detail. - An overload of `toml::find` for `toml::table` has been dropped. Use `toml::value` version instead. - Because type conversion between a table and a value causes ambiguity while overload resolution - Since `toml::parse` now returns a `toml::value`, this feature becomes less important. - Also because `toml::table` is a normal STL container, implementing utility function is easy. - See [Finding a toml::value](#finding-a-toml-value) for detail. - An overload of `operator<<` and `toml::format` for `toml::table`s are dropped. - Use `toml::value` instead. - See [Serializing TOML data](#serializing-toml-data) for detail. - Interface around comments. - See [Preserving Comments](#preserving-comments) for detail. - An ancient `from_toml/into_toml` has been removed. Use arbitrary type conversion support. - See [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) for detail. Such a big change will not happen in the coming years. ## Running Tests After cloning this repository, run the following command (thank you @jwillikers for automating test set fetching!). ```sh $ mkdir build $ cd build $ cmake .. -Dtoml11_BUILD_TEST=ON $ make $ make test ``` To run the language agnostic test suite, you need to compile `tests/check_toml_test.cpp` and pass it to the tester. ## Contributors I appreciate the help of the contributors who introduced the great feature to this library. - Guillaume Fraux (@Luthaf) - Windows support and CI on Appvayor - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR - Improved error messages for invalid keys to show the location where the parser fails - Petr Beneš (@wbenny) - Fixed warnings on MSVC - Ivan Shynkarenka (@chronoxor) - Fixed Visual Studio 2019 warnings - Fix compilation error in `` with MinGW - Khoi Dinh Trinh (@khoitd1997) - Fixed warnings while type conversion - @KerstinKeller - Added installation script to CMake - J.C. Moyer (@jcmoyer) - Fixed an example code in the documentation - Jt Freeman (@blockparty-sh) - Fixed feature test macro around `localtime_s` - Suppress warnings in Debug mode - OGAWA Kenichi (@kenichiice) - Suppress warnings on intel compiler - Fix include path in README - Jordan Williams (@jwillikers) - Fixed clang range-loop-analysis warnings - Fixed feature test macro to suppress -Wundef - Use cache variables in CMakeLists.txt - Automate test set fetching, update and refactor CMakeLists.txt - Scott McCaskill - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time` - Shu Wang (@halfelf) - fix "Finding a value in an array" example in README - @maass-tv and @SeverinLeonhardt - Fix MSVC warning C4866 - Mohammed Alyousef (@MoAlyousef) - Made testing optional in CMake - Alex Merry (@amerry) - Add missing include files - sneakypete81 (@sneakypete81) - Fix typo in error message - Oliver Kahrmann (@founderio) - Fix missing filename in error message if parsed file is empty - Karl Nilsson (@karl-nilsson) - Fix many spelling errors - ohdarling88 (@ohdarling) - Fix a bug in a constructor of serializer - estshorter (@estshorter) - Fix MSVC warning C26478 - Philip Top (@phlptp) - Improve checking standard library feature availability check - Louis Marascio (@marascio) - Fix free-nonheap-object warning - Axel Huebl (@ax3l) - Make installation optional if the library is embedded - Ken Matsui (@ken-matsui) - Support user-defined error message prefix - Support dynamic color mode - Giel van Schijndel (@muggenhor) - Remove needless copy in `parse` function - Lukáš Hrázký (@lukash) - Add a `parse(FILE *)` interface and improve file-related error messages - spiderman idog (@spiderman-idog) - Fix typo in README - Jajauma's GitHub (@Jajauma) - Avoid possible lexer truncation warnings - Moritz Klammler (@ctcmkl) - Many patches in (#200) including: - Improve CMake scripts, build process, and test file handling - Detect error when `discard_comments` is accessed - And more. - Chris White (@cxw42) - Fix address-sanitizer error when parsing literal strings having invalid UTF-8 characters - Fix function name in error messages - offa (@offa) - Update checkout action to v3 - Update Required CMake version - Cleanup old CI settings - Sergey Vidyuk (@VestniK) - Fix for case when vector iterator is raw pointer - Kfir Gollan (@kfirgollan) - Add installation example with checkinstall and cmake - Martin Tournoij (@arp242) - Escape control characters in keys - @DavidKorczynski - Add fuzzing test based on ClusterFuzzLite - Esonhugh Skyworship (@Esonhugh) - Fix function signature of `strerror_r` on macos ## Licensing terms This product is licensed under the terms of the [MIT License](LICENSE). - Copyright (c) 2017-2024 Toru Niina All rights reserved. toml11-3.8.1/appveyor.yml000066400000000000000000000011431454646465200152470ustar00rootroot00000000000000version: "{build}" os: Visual Studio 2015 environment: matrix: - generator: Visual Studio 14 2015 Win64 - generator: Visual Studio 14 2015 configuration: - Release - Debug clone_depth: 10 clone_folder: c:\toml11 build_script: - cd C:\toml11 - mkdir build - cd build - cmake -G"%generator%" -DCMAKE_CXX_STANDARD=11 -DBOOST_ROOT=C:/Libraries/boost_1_69_0 -Dtoml11_BUILD_TEST=ON .. - cmake --build . --config "%configuration%" - file --mime-encoding tests/toml/tests/hard_example_unicode.toml test_script: - ctest --build-config "%configuration%" --timeout 300 --output-on-failure toml11-3.8.1/cmake/000077500000000000000000000000001454646465200137405ustar00rootroot00000000000000toml11-3.8.1/cmake/toml11Config.cmake.in000066400000000000000000000001211454646465200176040ustar00rootroot00000000000000@PACKAGE_INIT@ include("@PACKAGE_toml11_install_cmake_dir@/toml11Targets.cmake") toml11-3.8.1/tests/000077500000000000000000000000001454646465200140225ustar00rootroot00000000000000toml11-3.8.1/tests/CMakeLists.txt000066400000000000000000000260271454646465200165710ustar00rootroot00000000000000include(ExternalProject) set(TOML11_LANGSPEC_GIT_REPOSITORY "https://github.com/toml-lang/toml" CACHE STRING "URL of the TOML language specification repository") set(TOML11_LANGSPEC_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/toml" CACHE FILEPATH "directory for the TOML language specification tree") if(NOT EXISTS "${TOML11_LANGSPEC_SOURCE_DIR}/toml.abnf") ExternalProject_Add(toml SOURCE_DIR "${TOML11_LANGSPEC_SOURCE_DIR}" GIT_REPOSITORY "${TOML11_LANGSPEC_GIT_REPOSITORY}" GIT_TAG "v0.5.0" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "") endif() set(TEST_NAMES test_datetime test_string test_utility test_result test_traits test_value test_lex_boolean test_lex_integer test_lex_floating test_lex_datetime test_lex_string test_lex_key_comment test_parse_boolean test_parse_integer test_parse_floating test_parse_string test_parse_datetime test_parse_array test_parse_table test_parse_inline_table test_parse_key test_parse_table_key test_literals test_comments test_get test_get_or test_find test_find_or test_find_or_recursive test_expect test_parse_file test_serialize_file test_parse_unicode test_error_detection test_format_error test_extended_conversions ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA) CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC) CHECK_CXX_COMPILER_FLAG("-Werror" COMPILER_SUPPORTS_WERROR) CHECK_CXX_COMPILER_FLAG("-Wsign-conversion" COMPILER_SUPPORTS_WSIGN_CONVERSION) CHECK_CXX_COMPILER_FLAG("-Wconversion" COMPILER_SUPPORTS_WCONVERSION) CHECK_CXX_COMPILER_FLAG("-Wduplicated-cond" COMPILER_SUPPORTS_WDUPLICATED_COND) CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BRANCHES) CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP) CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST) CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION) CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS) CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF) CHECK_CXX_COMPILER_FLAG("-Wshadow" COMPILER_SUPPORTS_WSHADOW) include(CheckCXXSourceCompiles) # check which standard library implementation is used. If libstdc++ is used, # it will fail to compile. It compiles if libc++ is used. check_cxx_source_compiles(" #include #ifdef __GLIBCXX__ static_assert(false); #endif int main() { return 0; }" TOML11_WITH_LIBCXX_LIBRARY) # LLVM 8 requires -lc++fs if compiled with libc++ to use . # LLVM 9+ does not require any special library. # GCC 8 requires -lstdc++fs. GCC 9+ does not require it. # # Yes, we can check the version of the compiler used in the current build # directly in CMake. But, in most cases, clang build uses libstdc++ as the # standard library implementation and it makes the condition complicated. # In many environment, the default installed C++ compiler is GCC and libstdc++ # is installed along with it. In most build on such an environment, even if we # chose clang as the C++ compiler, still libstdc++ is used. Checking default # gcc version makes the condition complicated. # The purpose of this file is to compile tests. We know the environment on which # the tests run. We can set this option and, I think, it is easier and better. option(TOML11_REQUIRE_FILESYSTEM_LIBRARY "need to link -lstdc++fs or -lc++fs" OFF) find_package(Boost COMPONENTS unit_test_framework REQUIRED) set(PREVIOUSLY_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES}") set(PREVIOUSLY_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") list(APPEND CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) check_cxx_source_compiles(" #define BOOST_TEST_MODULE \"dummy\" #undef BOOST_TEST_DYN_LINK #define BOOST_TEST_NO_LIB #include BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); } " TOML11_WITH_BOOST_TEST_HEADER) check_cxx_source_compiles(" #define BOOST_TEST_MODULE \"dummy\" #undef BOOST_TEST_DYN_LINK #undef BOOST_TEST_NO_LIB #include BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); } " TOML11_WITH_BOOST_TEST_STATIC) check_cxx_source_compiles(" #define BOOST_TEST_MODULE \"dummy\" #define BOOST_TEST_DYN_LINK #undef BOOST_TEST_NO_LIB #include BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); } " TOML11_WITH_BOOST_TEST_DYNAMIC) set(CMAKE_REQUIRED_INCLUDES "${PREVIOUSLY_REQUIRED_INCLUDES}") set(CMAKE_REQUIRED_LIBRARIES "${PREVIOUSLY_REQUIRED_LIBRARIES}") unset(PREVIOUSLY_REQUIRED_INCLUDES) unset(PREVIOUSLY_REQUIRED_LIBRARIES) if(TOML11_WITH_BOOST_TEST_DYNAMIC) add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST -DBOOST_TEST_DYN_LINK) elseif(TOML11_WITH_BOOST_TEST_STATIC) add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST) elseif(TOML11_WITH_BOOST_TEST_HEADER) add_definitions(-DBOOST_TEST_NO_LIB) else() message(FATAL_ERROR "Neither the Boost.Test static or shared library nor the header-only version seem to be usable.") endif() if(COMPILER_SUPPORTS_WALL) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") endif() if(COMPILER_SUPPORTS_WEXTRA) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") endif() if(COMPILER_SUPPORTS_WPEDANTIC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") endif() if(COMPILER_SUPPORTS_WERROR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") endif() if(COMPILER_SUPPORTS_WSHADOW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow") endif() if(COMPILER_SUPPORTS_WSIGN_CONVERSION) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion") endif() if(COMPILER_SUPPORTS_WCONVERSION) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion") endif() if(COMPILER_SUPPORTS_WDUPLICATED_COND) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-cond") endif() if(COMPILER_SUPPORTS_WDUPLICATED_BRANCHES) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-branches") endif() if(COMPILER_SUPPORTS_WLOGICAL_OP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op") endif() if(COMPILER_SUPPORTS_WUSELESS_CAST) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuseless-cast") endif() if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion") endif() if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis") endif() if(COMPILER_SUPPORTS_WUNDEF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef") endif() if(TOML11_USE_UNRELEASED_TOML_FEATURES) message(STATUS "adding TOML11_USE_UNRELEASED_TOML_FEATURES flag") add_definitions("-DTOML11_USE_UNRELEASED_TOML_FEATURES") endif() # Disable some MSVC warnings if(MSVC) # conversion from 'double' to 'unsigned int', possible loss of data set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") # conversion from 'int' to 'unsigned int', signed/unsigned mismatch set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4365") # layout of class may have changed from a previous version of the compiler set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4371") # enumerator in switch of enum is not explicitly handled by a case label set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4061") # unreferenced inline function has been removed set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4514") # constructor is not implicitly called set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4582") # destructor is not implicitly called set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4583") # pragma warning: there is no warning number set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4619") # default constructor was implicitly defined as deleted set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4623") # copy constructor was implicitly defined as deleted set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4625") # assignment operator was implicitly defined as deleted set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4626") # move assignment operator was implicitly defined as deleted set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4627") # is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4668") # function not inlined set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4710") # function selected for automatic inlining set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4711") # bytes padding added after data member set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4820") # pragma warning(pop): likely mismatch, popping warning state pushed in different file set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd5031") # pragma warning(pop): spectre warnings in tests set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd5045") # pragma warning(pop): spectre warnings in tests set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4265") endif() set(TEST_ENVIRON "TOMLDIR=${TOML11_LANGSPEC_SOURCE_DIR}") if(WIN32) # Set the PATH to be able to find Boost DLL STRING(REPLACE ";" "\\;" PATH_STRING "$ENV{PATH}") list(APPEND TEST_ENVIRON "PATH=${PATH_STRING}\;${Boost_LIBRARY_DIRS}") endif() foreach(TEST_NAME ${TEST_NAMES}) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11) target_include_directories(${TEST_NAME} SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}) target_compile_definitions(${TEST_NAME} PRIVATE "BOOST_TEST_MODULE=\"${TEST_NAME}\"") # to compile tests with ... if(TOML11_REQUIRE_FILESYSTEM_LIBRARY) if(TOML11_WITH_LIBCXX_LIBRARY) target_link_libraries(${TEST_NAME} "c++fs") else() target_link_libraries(${TEST_NAME} "stdc++fs") endif() endif() target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if(toml11_TEST_WITH_ASAN) set_target_properties(${TEST_NAME} PROPERTIES COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer" LINK_FLAGS "-fsanitize=address -fno-omit-frame-pointer") elseif(toml11_TEST_WITH_UBSAN) set_target_properties(${TEST_NAME} PROPERTIES COMPILE_FLAGS "-fsanitize=undefined" LINK_FLAGS "-fsanitize=undefined") endif() endif() add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${TEST_ENVIRON}") endforeach(TEST_NAME) # this test is to check it compiles. it will not run add_executable(test_multiple_translation_unit test_multiple_translation_unit_1.cpp test_multiple_translation_unit_2.cpp) target_link_libraries(test_multiple_translation_unit toml11::toml11) if(WIN32) add_executable(test_windows test_windows.cpp) target_link_libraries(test_windows toml11::toml11) endif() toml11-3.8.1/tests/check.cpp000066400000000000000000000014341454646465200156050ustar00rootroot00000000000000#include #include #include int main(int argc, char **argv) { if(argc != 3) { std::cerr << "usage: ./check [filename] [valid|invalid]" << std::endl; return 1; } const std::string file_kind(argv[2]); try { const auto data = toml::parse(argv[1]); std::cout << std::setprecision(16) << std::setw(80) << data; if(file_kind == "valid") { return 0; } else { return 1; } } catch(const toml::syntax_error& err) { std::cout << "what(): " << err.what() << std::endl; if(file_kind == "invalid") { return 0; } else { return 1; } } return 127; } toml11-3.8.1/tests/check_serialization.cpp000066400000000000000000000140221454646465200205370ustar00rootroot00000000000000#include #include #include int main(int argc, char **argv) { if(argc != 2) { std::cerr << "usage: ./check [filename]" << std::endl; return 1; } const std::string filename(argv[1]); { const auto data = toml::parse(filename); { std::ofstream ofs("tmp.toml"); ofs << std::setprecision(16) << std::setw(80) << data; } const auto serialized = toml::parse("tmp.toml"); if(data != serialized) { // this is really a ditry hack, but is the easiest way... // TODO: cleanup by adding comparison function to check if a value is NaN or not if(filename.substr(filename.size() - 22, 22) == "float-inf-and-nan.toml" && std::isnan (toml::find(serialized, "nan")) && !std::signbit (toml::find(serialized, "nan")) && std::isnan (toml::find(serialized, "nan_plus")) && !std::signbit (toml::find(serialized, "nan_plus")) && std::isnan (toml::find(serialized, "nan_neg")) && std::signbit (toml::find(serialized, "nan_neg")) && !std::isnan (toml::find(serialized, "infinity")) && !std::isfinite(toml::find(serialized, "infinity")) && !std::signbit (toml::find(serialized, "infinity")) && !std::isnan (toml::find(serialized, "infinity_plus")) && !std::isfinite(toml::find(serialized, "infinity_plus")) && !std::signbit (toml::find(serialized, "infinity_plus")) && !std::isnan (toml::find(serialized, "infinity_neg")) && !std::isfinite(toml::find(serialized, "infinity_neg")) && std::signbit (toml::find(serialized, "infinity_neg"))) { // then it is correctly serialized. // Note that, the result of (nan == nan) is false. so `data == serialized` is false. } else { std::cerr << "============================================================\n"; std::cerr << "result (w/o comment) different: " << filename << std::endl; std::cerr << "------------------------------------------------------------\n"; std::cerr << "# serialized\n"; std::cerr << serialized; std::cerr << "------------------------------------------------------------\n"; std::cerr << "# data\n"; std::cerr << data; return 1; } } } { const auto data = toml::parse(filename); { std::ofstream ofs("tmp.toml"); ofs << std::setprecision(16) << std::setw(80) << data; } const auto serialized = toml::parse("tmp.toml"); if(data != serialized) { // this is really a ditry hack, but is the easiest way... // TODO: cleanup by adding comparison function to check if a value is NaN or not if(filename.substr(filename.size() - 22, 22) == "float-inf-and-nan.toml" && std::isnan (toml::find(serialized, "nan")) && !std::signbit (toml::find(serialized, "nan")) && std::isnan (toml::find(serialized, "nan_plus")) && !std::signbit (toml::find(serialized, "nan_plus")) && std::isnan (toml::find(serialized, "nan_neg")) && std::signbit (toml::find(serialized, "nan_neg")) && !std::isnan (toml::find(serialized, "infinity")) && !std::isfinite(toml::find(serialized, "infinity")) && !std::signbit (toml::find(serialized, "infinity")) && !std::isnan (toml::find(serialized, "infinity_plus")) && !std::isfinite(toml::find(serialized, "infinity_plus")) && !std::signbit (toml::find(serialized, "infinity_plus")) && !std::isnan (toml::find(serialized, "infinity_neg")) && !std::isfinite(toml::find(serialized, "infinity_neg")) && std::signbit (toml::find(serialized, "infinity_neg")) && toml::find(data, "nan").comments() == toml::find(serialized, "nan").comments() && toml::find(data, "nan_plus").comments() == toml::find(serialized, "nan_plus").comments() && toml::find(data, "nan_neg").comments() == toml::find(serialized, "nan_neg").comments() && toml::find(data, "infinity").comments() == toml::find(serialized, "infinity").comments() && toml::find(data, "infinity_plus").comments() == toml::find(serialized, "infinity_plus").comments() && toml::find(data, "infinity_neg").comments() == toml::find(serialized, "infinity_neg").comments() ) { // then it is correctly serialized. // Note that, the result of (nan == nan) is false. so `data == serialized` is false. } else { std::cerr << "============================================================\n"; std::cerr << "result (w/ comment) different: " << filename << std::endl; std::cerr << "------------------------------------------------------------\n"; std::cerr << "# serialized\n"; std::cerr << serialized; std::cerr << "------------------------------------------------------------\n"; std::cerr << "# data\n"; std::cerr << data; return 1; } } } return 0; } toml11-3.8.1/tests/check_toml_test.cpp000066400000000000000000000077561454646465200177140ustar00rootroot00000000000000#include #include #include struct json_serializer { void operator()(toml::boolean v) { std::cout << "{\"type\":\"bool\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(toml::integer v) { std::cout << "{\"type\":\"integer\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(toml::floating v) { if(std::isnan(v) && std::signbit(v)) { // toml-test does not allow negative NaN represented in "-nan" because // there are languages that does not distinguish nan and -nan. // But toml11 keeps sign from input. To resolve this difference, // we convert -nan to nan here. v = std::numeric_limits::quiet_NaN(); } std::cout << "{\"type\":\"float\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(const toml::string& v) { // since toml11 automatically convert string to multiline string that is // valid only in TOML, we need to format the string to make it valid in // JSON. toml::serializer ser(std::numeric_limits::max()); std::cout << "{\"type\":\"string\",\"value\":" << ser(v.str) << "}"; return ; } void operator()(const toml::local_time& v) { std::cout << "{\"type\":\"time-local\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(const toml::local_date& v) { std::cout << "{\"type\":\"date-local\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(const toml::local_datetime& v) { std::cout << "{\"type\":\"datetime-local\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(const toml::offset_datetime& v) { std::cout << "{\"type\":\"datetime\",\"value\":\"" << toml::value(v) << "\"}"; return ; } void operator()(const toml::array& v) { if(!v.empty() && v.front().is_table()) { std::cout << '['; bool is_first = true; for(const auto& elem : v) { if(!is_first) {std::cout << ", ";} is_first = false; toml::visit(*this, elem); } std::cout << ']'; } else { // std::cout << "{\"type\":\"array\",\"value\":["; std::cout << "["; bool is_first = true; for(const auto& elem : v) { if(!is_first) {std::cout << ", ";} is_first = false; toml::visit(*this, elem); } std::cout << "]"; } return ; } void operator()(const toml::table& v) { std::cout << '{'; bool is_first = true; for(const auto& elem : v) { if(!is_first) {std::cout << ", ";} is_first = false; const auto k = toml::format_key(elem.first); if(k.at(0) == '"') { std::cout << k << ":"; } else // bare key { std::cout << '\"' << k << "\":"; } toml::visit(*this, elem.second); } std::cout << '}'; return ; } }; int main() { try { std::vector buf; std::cin.peek(); while(!std::cin.eof()) { buf.push_back(std::cin.get()); std::cin.peek(); } std::string bufstr(buf.begin(), buf.end()); std::istringstream ss(bufstr); const auto data = toml::parse(ss); std::cout << std::setprecision(std::numeric_limits::max_digits10); toml::visit(json_serializer(), data); return 0; } catch(const toml::syntax_error& err) { std::cout << "what(): " << err.what() << std::endl; return 1; } } toml11-3.8.1/tests/test_comments.cpp000066400000000000000000000475631454646465200174310ustar00rootroot00000000000000#include #include "unit_test.hpp" BOOST_AUTO_TEST_CASE(test_comment_before) { { const std::string file = R"( # comment for a. a = 42 # comment for b. b = "baz" )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().front() == " comment for b."); } { const std::string file = R"( # comment for a. # another comment for a. a = 42 # comment for b. # also comment for b. b = "baz" )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().back() == " another comment for a."); BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().back() == " also comment for b."); } } BOOST_AUTO_TEST_CASE(test_comment_inline) { { const std::string file = R"( a = 42 # comment for a. b = "baz" # comment for b. )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().front() == " comment for b."); } { const std::string file = R"( a = [ 42, ] # comment for a. b = [ "bar", # this is not a comment for b, but "bar" ] # this is a comment for b. )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); const auto& b0 = b.as_array().at(0); BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().front() == " this is a comment for b."); BOOST_TEST(b0.comments().size() == 1u); BOOST_TEST(b0.comments().front() == " this is not a comment for b, but \"bar\""); } } BOOST_AUTO_TEST_CASE(test_comment_both) { { const std::string file = R"( # comment for a. a = 42 # inline comment for a. # comment for b. b = "baz" # inline comment for b. # comment for c. c = [ # this comment will be ignored # comment for the first element. 10 # this also. ] # another comment for c. )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); const auto& c = toml::find(v, "c"); const auto& c0 = c.as_array().at(0); BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().back() == " inline comment for a."); BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().back() == " inline comment for b."); BOOST_TEST(c.comments().size() == 2u); BOOST_TEST(c.comments().front() == " comment for c."); BOOST_TEST(c.comments().back() == " another comment for c."); BOOST_TEST(c0.comments().size() == 2u); BOOST_TEST(c0.comments().front() == " comment for the first element."); BOOST_TEST(c0.comments().back() == " this also."); } } BOOST_AUTO_TEST_CASE(test_comments_on_implicit_values) { { const std::string file = R"( # comment for the first element of array-of-tables. [[array-of-tables]] foo = "bar" )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto aot = toml::find(v, "array-of-tables"); const auto elm = aot.at(0); BOOST_TEST(aot.comments().empty()); BOOST_TEST(elm.comments().size() == 1); BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables."); } { const std::string file = R"( # comment for the array itself array-of-tables = [ # comment for the first element of array-of-tables. {foo = "bar"} ] )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto aot = toml::find(v, "array-of-tables"); const auto elm = aot.at(0); BOOST_TEST(aot.comments().size() == 1); BOOST_TEST(aot.comments().front() == " comment for the array itself"); BOOST_TEST(elm.comments().size() == 1); BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables."); } } BOOST_AUTO_TEST_CASE(test_discard_comment) { const std::string file = R"( # comment for a. a = 42 # inline comment for a. # comment for b. b = "baz" # inline comment for b. # comment for c. c = [ # this comment will be ignored # comment for the first element. 10 # this also. ] # another comment for c. )"; std::istringstream iss(file); const auto v = toml::parse(iss); const auto& a = toml::find(v, "a"); const auto& b = toml::find(v, "b"); const auto& c = toml::find(v, "c"); const auto& c0 = c.as_array().at(0); BOOST_TEST(a.comments().empty()); BOOST_TEST(b.comments().empty()); BOOST_TEST(c.comments().empty()); BOOST_TEST(c0.comments().empty()); } BOOST_AUTO_TEST_CASE(test_construct_value_with_comments) { using value_type = toml::basic_value; { const value_type v(true, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_boolean()); BOOST_TEST(v.as_boolean() == true); } { const value_type v(42, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_integer()); BOOST_TEST(v.as_integer() == 42); } { const value_type v(3.14, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_floating()); BOOST_TEST(v.as_floating() == 3.14); } { const value_type v(toml::string("str"), {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } { const value_type v(std::string("str"), {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } { const value_type v(std::string("str"), toml::string_t::literal, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } { const value_type v("str", {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } { const value_type v("str", toml::string_t::literal, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L { using namespace std::literals::string_view_literals; const value_type v("str"sv, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } { using namespace std::literals::string_view_literals; const value_type v("str"sv, toml::string_t::literal, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_string()); BOOST_TEST(v.as_string() == "str"); } #endif const toml::local_date ld{2019, toml::month_t::Apr, 1}; const toml::local_time lt{12, 30, 45}; const toml::local_datetime ldt{ld, lt}; const toml::offset_datetime odt{ld, lt, toml::time_offset{9, 0}}; { const value_type v(ld, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_local_date()); BOOST_TEST(v.as_local_date() == ld); } { const value_type v(lt, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_local_time()); BOOST_TEST(v.as_local_time() == lt); } { const toml::local_time three_hours{3,0,0}; const value_type v(std::chrono::hours(3), {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_local_time()); BOOST_TEST(v.as_local_time() == three_hours); } { const value_type v(ldt, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_local_datetime()); BOOST_TEST(v.as_local_datetime() == ldt); } { const value_type v(odt, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_offset_datetime()); BOOST_TEST(v.as_offset_datetime() == odt); } { const auto systp = static_cast(odt); const value_type v(systp, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_offset_datetime()); // While the conversion, the information about time offset may change. const auto systp2 = static_cast( v.as_offset_datetime()); const bool result = systp == systp2; // because there is no operator<< BOOST_TEST(result); } { const typename value_type::array_type a{1,2,3,4,5}; const value_type v(a, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_array()); BOOST_TEST(v.as_array().at(0).is_integer()); BOOST_TEST(v.as_array().at(1).is_integer()); BOOST_TEST(v.as_array().at(2).is_integer()); BOOST_TEST(v.as_array().at(3).is_integer()); BOOST_TEST(v.as_array().at(4).is_integer()); BOOST_TEST(v.as_array().at(0).as_integer() == 1); BOOST_TEST(v.as_array().at(1).as_integer() == 2); BOOST_TEST(v.as_array().at(2).as_integer() == 3); BOOST_TEST(v.as_array().at(3).as_integer() == 4); BOOST_TEST(v.as_array().at(4).as_integer() == 5); } { const std::initializer_list a = {1,2,3,4,5}; const value_type v(a, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_array()); BOOST_TEST(v.as_array().at(0).is_integer()); BOOST_TEST(v.as_array().at(1).is_integer()); BOOST_TEST(v.as_array().at(2).is_integer()); BOOST_TEST(v.as_array().at(3).is_integer()); BOOST_TEST(v.as_array().at(4).is_integer()); BOOST_TEST(v.as_array().at(0).as_integer() == 1); BOOST_TEST(v.as_array().at(1).as_integer() == 2); BOOST_TEST(v.as_array().at(2).as_integer() == 3); BOOST_TEST(v.as_array().at(3).as_integer() == 4); BOOST_TEST(v.as_array().at(4).as_integer() == 5); } { const std::vector a = {1,2,3,4,5}; const value_type v(a, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_array()); BOOST_TEST(v.as_array().at(0).is_integer()); BOOST_TEST(v.as_array().at(1).is_integer()); BOOST_TEST(v.as_array().at(2).is_integer()); BOOST_TEST(v.as_array().at(3).is_integer()); BOOST_TEST(v.as_array().at(4).is_integer()); BOOST_TEST(v.as_array().at(0).as_integer() == 1); BOOST_TEST(v.as_array().at(1).as_integer() == 2); BOOST_TEST(v.as_array().at(2).as_integer() == 3); BOOST_TEST(v.as_array().at(3).as_integer() == 4); BOOST_TEST(v.as_array().at(4).as_integer() == 5); } { const typename value_type::table_type t{ {"key1", 42}, {"key2", "foobar"} }; const value_type v(t, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_table()); BOOST_TEST(v.as_table().at("key1").is_integer()); BOOST_TEST(v.as_table().at("key1").as_integer() == 42); BOOST_TEST(v.as_table().at("key2").is_string()); BOOST_TEST(v.as_table().at("key2").as_string() == "foobar"); } { const std::initializer_list> t{ {"key1", 42}, {"key2", "foobar"} }; const value_type v(t, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_table()); BOOST_TEST(v.as_table().at("key1").is_integer()); BOOST_TEST(v.as_table().at("key1").as_integer() == 42); BOOST_TEST(v.as_table().at("key2").is_string()); BOOST_TEST(v.as_table().at("key2").as_string() == "foobar"); } { const std::map t{ {"key1", 42}, {"key2", "foobar"} }; const value_type v(t, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_table()); BOOST_TEST(v.as_table().at("key1").is_integer()); BOOST_TEST(v.as_table().at("key1").as_integer() == 42); BOOST_TEST(v.as_table().at("key2").is_string()); BOOST_TEST(v.as_table().at("key2").as_string() == "foobar"); } } BOOST_AUTO_TEST_CASE(test_overwrite_comments) { using value_type = toml::basic_value; { const value_type v(42, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_integer()); BOOST_TEST(v.as_integer() == 42); const value_type u(v, {"comment3", "comment4"}); BOOST_TEST(u.comments().size() == 2u); BOOST_TEST(u.comments().at(0) == "comment3"); BOOST_TEST(u.comments().at(1) == "comment4"); BOOST_TEST(u.is_integer()); BOOST_TEST(u.as_integer() == 42); } { const value_type v(42, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_integer()); BOOST_TEST(v.as_integer() == 42); const value_type u(v); BOOST_TEST(u.comments().size() == 2u); BOOST_TEST(u.comments().at(0) == "comment1"); BOOST_TEST(u.comments().at(1) == "comment2"); BOOST_TEST(u.is_integer()); BOOST_TEST(u.as_integer() == 42); } { const value_type v(42, {"comment1", "comment2"}); BOOST_TEST(v.comments().size() == 2u); BOOST_TEST(v.comments().at(0) == "comment1"); BOOST_TEST(v.comments().at(1) == "comment2"); BOOST_TEST(v.is_integer()); BOOST_TEST(v.as_integer() == 42); const value_type u(v, {}); BOOST_TEST(u.comments().size() == 0u); BOOST_TEST(u.is_integer()); BOOST_TEST(u.as_integer() == 42); } } BOOST_AUTO_TEST_CASE(test_output_comments) { using value_type = toml::basic_value; { const value_type v(42, {"comment1", "comment2"}); std::ostringstream oss; oss << v.comments(); std::ostringstream ref; ref << "#comment1\n"; ref << "#comment2\n"; BOOST_TEST(oss.str() == ref.str()); } { const value_type v(42, {"comment1", "comment2"}); std::ostringstream oss; // If v is not a table, toml11 assumes that user is writing something // like the following. oss << "answer = " << v; BOOST_TEST(oss.str() == "answer = 42 #comment1comment2"); } { const value_type v(42, {"comment1", "comment2"}); std::ostringstream oss; // If v is not a table, toml11 assumes that user is writing something // like the following. oss << toml::nocomment << "answer = " << v; BOOST_TEST(oss.str() == "answer = 42"); } { const value_type v(42, {"comment1", "comment2"}); std::ostringstream oss; // If v is not a table, toml11 assumes that user is writing something // like the following. oss << toml::nocomment << toml::showcomment << "answer = " << v; BOOST_TEST(oss.str() == "answer = 42 #comment1comment2"); } } toml11-3.8.1/tests/test_datetime.cpp000066400000000000000000000064371454646465200173730ustar00rootroot00000000000000#include #include "unit_test.hpp" BOOST_AUTO_TEST_CASE(test_local_date) { const toml::local_date date(2018, toml::month_t::Jan, 1); const toml::local_date date1(date); BOOST_TEST(date == date1); const std::chrono::system_clock::time_point tp(date); const toml::local_date date2(tp); BOOST_TEST(date == date2); const toml::local_date date3(2017, toml::month_t::Dec, 31); BOOST_TEST(date > date3); std::ostringstream oss; oss << date; BOOST_TEST(oss.str() == std::string("2018-01-01")); } BOOST_AUTO_TEST_CASE(test_local_time) { const toml::local_time time(12, 30, 45); const toml::local_time time1(time); BOOST_TEST(time == time1); const std::chrono::nanoseconds dur(time); std::chrono::nanoseconds ns(0); ns += std::chrono::hours (12); ns += std::chrono::minutes(30); ns += std::chrono::seconds(45); BOOST_TEST(dur.count() == ns.count()); const toml::local_time time3(12, 15, 45); BOOST_TEST(time > time3); { std::ostringstream oss; oss << time; BOOST_TEST(oss.str() == std::string("12:30:45")); } { const toml::local_time time4(12, 30, 45, 123, 456); std::ostringstream oss; oss << time4; BOOST_TEST(oss.str() == std::string("12:30:45.123456")); } } BOOST_AUTO_TEST_CASE(test_time_offset) { const toml::time_offset time(9, 30); const toml::time_offset time1(time); BOOST_TEST(time == time1); const std::chrono::minutes dur(time); std::chrono::minutes m(0); m += std::chrono::hours (9); m += std::chrono::minutes(30); BOOST_TEST(dur.count() == m.count()); const toml::time_offset time2(9, 0); BOOST_TEST(time2 < time); std::ostringstream oss; oss << time; BOOST_TEST(oss.str() == std::string("+09:30")); } BOOST_AUTO_TEST_CASE(test_local_datetime) { const toml::local_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45)); const toml::local_datetime dt1(dt); BOOST_TEST(dt == dt1); const std::chrono::system_clock::time_point tp(dt); const toml::local_datetime dt2(tp); BOOST_TEST(dt == dt2); std::ostringstream oss; oss << dt; BOOST_TEST(oss.str() == std::string("2018-01-01T12:30:45")); } BOOST_AUTO_TEST_CASE(test_offset_datetime) { const toml::offset_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45), toml::time_offset(9, 30)); const toml::offset_datetime dt1(dt); BOOST_TEST(dt == dt1); const std::chrono::system_clock::time_point tp1(dt); const toml::offset_datetime dt2(tp1); const std::chrono::system_clock::time_point tp2(dt2); const bool tp_same = (tp1 == tp2); BOOST_TEST(tp_same); { std::ostringstream oss; oss << dt; BOOST_TEST(oss.str() == std::string("2018-01-01T12:30:45+09:30")); } { const toml::offset_datetime dt3( toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45), toml::time_offset(0, 0)); std::ostringstream oss; oss << dt3; BOOST_TEST(oss.str() == std::string("2018-01-01T12:30:45Z")); } } toml11-3.8.1/tests/test_error_detection.cpp000066400000000000000000000047051454646465200207620ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include BOOST_AUTO_TEST_CASE(test_detect_empty_key) { std::istringstream stream(std::string("= \"value\"")); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_missing_value) { std::istringstream stream(std::string("a =")); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_too_many_value) { std::istringstream stream(std::string("a = 1 = \"value\"")); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_duplicate_table) { std::istringstream stream(std::string( "[table]\n" "a = 42\n" "[table]\n" "b = 42\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_conflict_array_table) { std::istringstream stream(std::string( "[[table]]\n" "a = 42\n" "[table]\n" "b = 42\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_conflict_table_array) { std::istringstream stream(std::string( "[table]\n" "a = 42\n" "[[table]]\n" "b = 42\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_duplicate_value) { std::istringstream stream(std::string( "a = 1\n" "a = 2\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_conflicting_value) { std::istringstream stream(std::string( "a.b = 1\n" "a.b.c = 2\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) { #ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS std::istringstream stream(std::string( "a = [1, 1.0]\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); #else BOOST_TEST_MESSAGE("After v1.0.0-rc.1, heterogeneous arrays are allowed"); #endif } BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table) { std::istringstream stream(std::string( "a = [{b = 1}]\n" "[[a]]\n" "b = 2\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } toml11-3.8.1/tests/test_expect.cpp000066400000000000000000000014231454646465200170550ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include BOOST_AUTO_TEST_CASE(test_expect) { { toml::value v1(42); toml::value v2(3.14); const auto v1_or_0 = toml::expect(v1).unwrap_or(0); const auto v2_or_0 = toml::expect(v2).unwrap_or(0); BOOST_TEST(42 == v1_or_0); BOOST_TEST( 0 == v2_or_0); const auto v1_or_none = toml::expect(v1).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none")); const auto v2_or_none = toml::expect(v2).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none")); BOOST_TEST("42" == v1_or_none); BOOST_TEST("none" == v2_or_none); } } toml11-3.8.1/tests/test_extended_conversions.cpp000066400000000000000000000415721454646465200220260ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include namespace extlib { struct foo { int a; std::string b; }; struct bar { int a; std::string b; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } toml::table into_toml() const { return toml::table{{"a", this->a}, {"b", this->b}}; } }; struct baz { int a; std::string b; }; struct qux { int a; std::string b; }; struct foobar { // via constructor explicit foobar(const toml::value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; } // extlib namespace toml { template<> struct from { static extlib::foo from_toml(const toml::value& v) { return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { static toml::value into_toml(const extlib::foo& f) { return toml::value{{"a", f.a}, {"b", f.b}}; } }; template<> struct from { static extlib::baz from_toml(const toml::value& v) { return extlib::baz{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { static toml::table into_toml(const extlib::qux& f) { return toml::table{{"a", f.a}, {"b", f.b}}; } }; } // toml // --------------------------------------------------------------------------- namespace extlib2 { struct foo { int a; std::string b; }; struct bar { int a; std::string b; template class M, template class A> void from_toml(const toml::basic_value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } toml::table into_toml() const { return toml::table{{"a", this->a}, {"b", this->b}}; } }; struct baz { int a; std::string b; }; struct qux { int a; std::string b; }; struct foobar { template class M, template class A> explicit foobar(const toml::basic_value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; } // extlib2 namespace toml { template<> struct from { template class M, template class A> static extlib2::foo from_toml(const toml::basic_value& v) { return extlib2::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { static toml::table into_toml(const extlib2::foo& f) { return toml::table{{"a", f.a}, {"b", f.b}}; } }; template<> struct from { template class M, template class A> static extlib2::baz from_toml(const toml::basic_value& v) { return extlib2::baz{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { static toml::basic_value into_toml(const extlib2::qux& f) { return toml::basic_value{ {"a", f.a}, {"b", f.b} }; } }; } // toml // --------------------------------------------------------------------------- BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods) { { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto foo = toml::get(v); BOOST_TEST(foo.a == 42); BOOST_TEST(foo.b == "baz"); const toml::value v2(foo); BOOST_TEST(v == v2); } { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto foo = toml::get(v); BOOST_TEST(foo.a == 42); BOOST_TEST(foo.b == "baz"); const toml::value v2(foo); BOOST_TEST(v == v2); } { const toml::basic_value v{{"a", 42}, {"b", "baz"}}; const auto foo = toml::get(v); BOOST_TEST(foo.a == 42); BOOST_TEST(foo.b == "baz"); const toml::basic_value v2(foo); BOOST_TEST(v == v2); } } BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) { { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto bar = toml::get(v); BOOST_TEST(bar.a == 42); BOOST_TEST(bar.b == "baz"); const toml::value v2(bar); BOOST_TEST(v == v2); } { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto bar = toml::get(v); BOOST_TEST(bar.a == 42); BOOST_TEST(bar.b == "baz"); const toml::value v2(bar); BOOST_TEST(v == v2); } { const toml::basic_value v{{"a", 42}, {"b", "baz"}}; const auto bar = toml::get(v); BOOST_TEST(bar.a == 42); BOOST_TEST(bar.b == "baz"); const toml::basic_value v2(bar); BOOST_TEST(v == v2); } } BOOST_AUTO_TEST_CASE(test_conversion_one_way) { { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto baz = toml::get(v); BOOST_TEST(baz.a == 42); BOOST_TEST(baz.b == "baz"); } { const extlib::qux q{42, "qux"}; const toml::value v(q); BOOST_TEST(toml::find(v, "a") == 42); BOOST_TEST(toml::find(v, "b") == "qux"); } { const toml::basic_value v{ {"a", 42}, {"b", "baz"} }; const auto baz = toml::get(v); BOOST_TEST(baz.a == 42); BOOST_TEST(baz.b == "baz"); } { const extlib::qux q{42, "qux"}; const toml::basic_value v(q); BOOST_TEST(toml::find(v, "a") == 42); BOOST_TEST(toml::find(v, "b") == "qux"); } } BOOST_AUTO_TEST_CASE(test_conversion_via_constructor) { { const toml::value v{{"a", 42}, {"b", "foobar"}}; const auto foobar = toml::get(v); BOOST_TEST(foobar.a == 42); BOOST_TEST(foobar.b == "foobar"); } { const toml::basic_value v{ {"a", 42}, {"b", "foobar"} }; const auto foobar = toml::get(v); BOOST_TEST(foobar.a == 42); BOOST_TEST(foobar.b == "foobar"); } } BOOST_AUTO_TEST_CASE(test_recursive_conversion) { { const toml::value v{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}}, }; const auto foos = toml::get>(v); BOOST_TEST(foos.size() == 4ul); BOOST_TEST(foos.at(0).a == 42); BOOST_TEST(foos.at(1).a == 43); BOOST_TEST(foos.at(2).a == 44); BOOST_TEST(foos.at(3).a == 45); BOOST_TEST(foos.at(0).b == "baz"); BOOST_TEST(foos.at(1).b == "qux"); BOOST_TEST(foos.at(2).b == "quux"); BOOST_TEST(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); BOOST_TEST(bars.size() == 4ul); BOOST_TEST(bars.at(0).a == 42); BOOST_TEST(bars.at(1).a == 43); BOOST_TEST(bars.at(2).a == 44); BOOST_TEST(bars.at(3).a == 45); BOOST_TEST(bars.at(0).b == "baz"); BOOST_TEST(bars.at(1).b == "qux"); BOOST_TEST(bars.at(2).b == "quux"); BOOST_TEST(bars.at(3).b == "foobar"); } { const toml::value v{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}}, }; const auto foos = toml::get>(v); BOOST_TEST(foos.size() == 4ul); BOOST_TEST(foos.at(0).a == 42); BOOST_TEST(foos.at(1).a == 43); BOOST_TEST(foos.at(2).a == 44); BOOST_TEST(foos.at(3).a == 45); BOOST_TEST(foos.at(0).b == "baz"); BOOST_TEST(foos.at(1).b == "qux"); BOOST_TEST(foos.at(2).b == "quux"); BOOST_TEST(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); BOOST_TEST(bars.size() == 4ul); BOOST_TEST(bars.at(0).a == 42); BOOST_TEST(bars.at(1).a == 43); BOOST_TEST(bars.at(2).a == 44); BOOST_TEST(bars.at(3).a == 45); BOOST_TEST(bars.at(0).b == "baz"); BOOST_TEST(bars.at(1).b == "qux"); BOOST_TEST(bars.at(2).b == "quux"); BOOST_TEST(bars.at(3).b == "foobar"); } { const toml::basic_value v{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}} }; const auto foos = toml::get>(v); BOOST_TEST(foos.size() == 4ul); BOOST_TEST(foos.at(0).a == 42); BOOST_TEST(foos.at(1).a == 43); BOOST_TEST(foos.at(2).a == 44); BOOST_TEST(foos.at(3).a == 45); BOOST_TEST(foos.at(0).b == "baz"); BOOST_TEST(foos.at(1).b == "qux"); BOOST_TEST(foos.at(2).b == "quux"); BOOST_TEST(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); BOOST_TEST(bars.size() == 4ul); BOOST_TEST(bars.at(0).a == 42); BOOST_TEST(bars.at(1).a == 43); BOOST_TEST(bars.at(2).a == 44); BOOST_TEST(bars.at(3).a == 45); BOOST_TEST(bars.at(0).b == "baz"); BOOST_TEST(bars.at(1).b == "qux"); BOOST_TEST(bars.at(2).b == "quux"); BOOST_TEST(bars.at(3).b == "foobar"); } // via constructor { const toml::value v{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}} }; { const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at(0).a == 42); BOOST_TEST(foobars.at(1).a == 43); BOOST_TEST(foobars.at(2).a == 44); BOOST_TEST(foobars.at(3).a == 45); BOOST_TEST(foobars.at(0).b == "baz"); BOOST_TEST(foobars.at(1).b == "qux"); BOOST_TEST(foobars.at(2).b == "quux"); BOOST_TEST(foobars.at(3).b == "foobar"); } { const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at(0).a == 42); BOOST_TEST(foobars.at(1).a == 43); BOOST_TEST(foobars.at(2).a == 44); BOOST_TEST(foobars.at(3).a == 45); BOOST_TEST(foobars.at(0).b == "baz"); BOOST_TEST(foobars.at(1).b == "qux"); BOOST_TEST(foobars.at(2).b == "quux"); BOOST_TEST(foobars.at(3).b == "foobar"); } } { const toml::basic_value v{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}} }; const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at(0).a == 42); BOOST_TEST(foobars.at(1).a == 43); BOOST_TEST(foobars.at(2).a == 44); BOOST_TEST(foobars.at(3).a == 45); BOOST_TEST(foobars.at(0).b == "baz"); BOOST_TEST(foobars.at(1).b == "qux"); BOOST_TEST(foobars.at(2).b == "quux"); BOOST_TEST(foobars.at(3).b == "foobar"); } // via constructor { const toml::value v{ {"0", toml::table{{"a", 42}, {"b", "baz"}}}, {"1", toml::table{{"a", 43}, {"b", "qux"}}}, {"2", toml::table{{"a", 44}, {"b", "quux"}}}, {"3", toml::table{{"a", 45}, {"b", "foobar"}}} }; { const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at("0").a == 42); BOOST_TEST(foobars.at("1").a == 43); BOOST_TEST(foobars.at("2").a == 44); BOOST_TEST(foobars.at("3").a == 45); BOOST_TEST(foobars.at("0").b == "baz"); BOOST_TEST(foobars.at("1").b == "qux"); BOOST_TEST(foobars.at("2").b == "quux"); BOOST_TEST(foobars.at("3").b == "foobar"); } { const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at("0").a == 42); BOOST_TEST(foobars.at("1").a == 43); BOOST_TEST(foobars.at("2").a == 44); BOOST_TEST(foobars.at("3").a == 45); BOOST_TEST(foobars.at("0").b == "baz"); BOOST_TEST(foobars.at("1").b == "qux"); BOOST_TEST(foobars.at("2").b == "quux"); BOOST_TEST(foobars.at("3").b == "foobar"); } } { const toml::basic_value v{ {"0", toml::table{{"a", 42}, {"b", "baz"}}}, {"1", toml::table{{"a", 43}, {"b", "qux"}}}, {"2", toml::table{{"a", 44}, {"b", "quux"}}}, {"3", toml::table{{"a", 45}, {"b", "foobar"}}} }; const auto foobars = toml::get>(v); BOOST_TEST(foobars.size() == 4ul); BOOST_TEST(foobars.at("0").a == 42); BOOST_TEST(foobars.at("1").a == 43); BOOST_TEST(foobars.at("2").a == 44); BOOST_TEST(foobars.at("3").a == 45); BOOST_TEST(foobars.at("0").b == "baz"); BOOST_TEST(foobars.at("1").b == "qux"); BOOST_TEST(foobars.at("2").b == "quux"); BOOST_TEST(foobars.at("3").b == "foobar"); } } // =========================================================================== #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE namespace extlib3 { struct foo { int a; std::string b; }; struct bar { int a; std::string b; foo f; }; } // extlib3 TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::foo, a, b) TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::bar, a, b, f) BOOST_AUTO_TEST_CASE(test_conversion_via_macro) { { const toml::value v{{"a", 42}, {"b", "baz"}}; const auto foo = toml::get(v); BOOST_TEST(foo.a == 42); BOOST_TEST(foo.b == "baz"); const toml::value v2(foo); BOOST_TEST(v2 == v); } { const toml::basic_value v{ {"a", 42}, {"b", "baz"} }; const auto foo = toml::get(v); BOOST_TEST(foo.a == 42); BOOST_TEST(foo.b == "baz"); const toml::basic_value v2(foo); BOOST_TEST(v2 == v); } // ----------------------------------------------------------------------- { const toml::value v{ {"a", 42}, {"b", "bar.b"}, {"f", toml::table{{"a", 42}, {"b", "foo.b"}}} }; const auto bar = toml::get(v); BOOST_TEST(bar.a == 42); BOOST_TEST(bar.b == "bar.b"); BOOST_TEST(bar.f.a == 42); BOOST_TEST(bar.f.b == "foo.b"); const toml::value v2(bar); BOOST_TEST(v2 == v); } { const toml::basic_value v{ {"a", 42}, {"b", "bar.b"}, {"f", toml::table{{"a", 42}, {"b", "foo.b"}}} }; const auto bar = toml::get(v); BOOST_TEST(bar.a == 42); BOOST_TEST(bar.b == "bar.b"); BOOST_TEST(bar.f.a == 42); BOOST_TEST(bar.f.b == "foo.b"); const toml::basic_value v2(bar); BOOST_TEST(v2 == v); } } #endif // TOML11_WITHOUT_DEFINE_NON_INTRUSIVE toml11-3.8.1/tests/test_find.cpp000066400000000000000000000741271454646465200165200ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif using test_value_types = std::tuple< toml::basic_value, toml::basic_value, toml::basic_value, toml::basic_value >; BOOST_AUTO_TEST_CASE(test_find_throws) { // ----------------------------------------------------------------------- // const-reference version { // value is not a table const toml::value v(true); BOOST_CHECK_THROW(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not the expected type const toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not found const toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(v, "different_key"), std::out_of_range); } { // the positive control. const toml::value v{{"key", 42}}; BOOST_TEST(42 == toml::find(v, "key")); } // ----------------------------------------------------------------------- // reference version { // value is not a table toml::value v(true); BOOST_CHECK_THROW(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not found toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(v, "different_key"), std::out_of_range); } { // the positive control. toml::value v{{"key", 42}}; BOOST_TEST(42 == toml::find(v, "key")); } // ----------------------------------------------------------------------- // move version { // value is not a table toml::value v(true); BOOST_CHECK_THROW(toml::find(std::move(v), "key"), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(std::move(v), "key"), toml::type_error); } { // the value corresponding to the key is not found toml::value v{{"key", 42}}; BOOST_CHECK_THROW(toml::find(std::move(v), "different_key"), std::out_of_range); } { // the positive control. toml::value v{{"key", 42}}; BOOST_TEST(42 == toml::find(std::move(v), "key")); } } BOOST_AUTO_TEST_CASE(test_find_array_throws) { // ----------------------------------------------------------------------- // const-reference version { // value is not an array const toml::value v(true); BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not the expected type const toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not found const toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(v, 6), std::out_of_range); } { // the positive control. const toml::value v{1, 2, 3, 4, 5}; BOOST_TEST(3 == toml::find(v, 2)); } // ----------------------------------------------------------------------- // non-const reference version { // value is not an array toml::value v(true); BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not found toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(v, 6), std::out_of_range); } { // the positive control. toml::value v{1, 2, 3, 4, 5}; BOOST_TEST(3 == toml::find(v, 2)); } // ----------------------------------------------------------------------- // move version { // value is not an array toml::value v(true); BOOST_CHECK_THROW(toml::find(std::move(v), 0), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(std::move(v), 0), toml::type_error); } { // the value corresponding to the key is not found toml::value v{1, 2, 3, 4, 5}; BOOST_CHECK_THROW(toml::find(std::move(v), 6), std::out_of_range); } { // the positive control. toml::value v{1, 2, 3, 4, 5}; BOOST_TEST(3 == toml::find(std::move(v), 2)); } } BOOST_AUTO_TEST_CASE(test_find_recursive) { // recursively search tables { toml::value v{ {"a", { {"b", { {"c", { {"d", 42} }} }} }} }; BOOST_TEST(42 == toml::find(v, "a", "b", "c", "d")); // reference that can be used to modify the content auto& num = toml::find(v, "a", "b", "c", "d"); num = 54; BOOST_TEST(54 == toml::find(v, "a", "b", "c", "d")); const std::string a("a"), b("b"), c("c"), d("d"); auto& num2 = toml::find(v, a, b, c, d); num2 = 42; BOOST_TEST(42 == toml::find(v, a, b, c, d)); auto num3 = toml::find(v, a, "b", c, "d"); BOOST_TEST(42 == num3); auto num4 = toml::find(std::move(v), a, b, c, d); BOOST_TEST(42 == num4); } // recursively search arrays { toml::value v{ toml::array{"array", "of", "string"}, toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}} }; BOOST_TEST("array" == toml::find(v, 0, 0)); BOOST_TEST("of" == toml::find(v, 0, 1)); BOOST_TEST("string" == toml::find(v, 0, 2)); BOOST_TEST(1 == toml::find(v, 1, 0, 0)); BOOST_TEST(2 == toml::find(v, 1, 0, 1)); BOOST_TEST(3 == toml::find(v, 1, 0, 2)); BOOST_TEST(3.14 == toml::find(v, 1, 1, 0)); BOOST_TEST(2.71 == toml::find(v, 1, 1, 1)); // reference that can be used to modify the content auto& num = toml::find(v, 1, 0, 2); num = 42; BOOST_TEST( 1 == toml::find(v, 1, 0, 0)); BOOST_TEST( 2 == toml::find(v, 1, 0, 1)); BOOST_TEST(42 == toml::find(v, 1, 0, 2)); // move value auto num2 = toml::find(std::move(v), 1, 0, 2); BOOST_TEST(42 == num2); } // recursively search mixtures { toml::value v = toml::table{{"array", toml::array{ toml::array{1, 2, 3}, toml::array{ toml::table{{"foo", "bar"}, {"baz", "qux"}}, toml::table{{"pi", 3.14}, {"e", 2.71}} }} }}; BOOST_TEST(1 == toml::find(v, "array", 0, 0)); BOOST_TEST(2 == toml::find(v, "array", 0, 1)); BOOST_TEST(3 == toml::find(v, "array", 0, 2)); BOOST_TEST("bar" == toml::find(v, "array", 1, 0, "foo")); BOOST_TEST("qux" == toml::find(v, "array", 1, 0, "baz")); BOOST_TEST(3.14 == toml::find(v, "array", 1, 1, "pi")); BOOST_TEST(2.71 == toml::find(v, "array", 1, 1, "e")); const std::string ar("array"); const auto ar_c = "array"; const std::string pi("pi"); const auto pi_c = "pi"; BOOST_TEST(3.14 == toml::find(v, ar, 1, 1, "pi")); BOOST_TEST(3.14 == toml::find(v, ar, 1, 1, pi)); BOOST_TEST(3.14 == toml::find(v, ar, 1, 1, pi_c)); BOOST_TEST(3.14 == toml::find(v, ar_c, 1, 1, "pi")); BOOST_TEST(3.14 == toml::find(v, ar_c, 1, 1, pi)); BOOST_TEST(3.14 == toml::find(v, ar_c, 1, 1, pi_c)); BOOST_TEST(3.14 == toml::find(v, "array", 1, 1, pi)); BOOST_TEST(3.14 == toml::find(v, "array", 1, 1, pi_c)); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types) { { value_type v{{"key", true}}; BOOST_TEST(true == toml::find(v, "key")); toml::find(v, "key") = false; BOOST_TEST(false == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_TEST(false == moved); } { value_type v{{"key", 42}}; BOOST_TEST(toml::integer(42) == toml::find(v, "key")); toml::find(v, "key") = 54; BOOST_TEST(toml::integer(54) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_TEST(toml::integer(54) == moved); } { value_type v{{"key", 3.14}}; BOOST_TEST(toml::floating(3.14) == toml::find(v, "key")); toml::find(v, "key") = 2.71; BOOST_TEST(toml::floating(2.71) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_TEST(toml::floating(2.71) == moved); } { value_type v{{"key", "foo"}}; BOOST_TEST(toml::string("foo", toml::string_t::basic) == toml::find(v, "key")); toml::find(v, "key").str += "bar"; BOOST_TEST(toml::string("foobar", toml::string_t::basic) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_TEST(toml::string("foobar", toml::string_t::basic) == moved); } { value_type v{{"key", value_type("foo", toml::string_t::literal)}}; BOOST_TEST(toml::string("foo", toml::string_t::literal) == toml::find(v, "key")); toml::find(v, "key").str += "bar"; BOOST_TEST(toml::string("foobar", toml::string_t::literal) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_TEST(toml::string("foobar", toml::string_t::literal) == moved); } { toml::local_date d(2018, toml::month_t::Apr, 22); value_type v{{"key", d}}; BOOST_CHECK(d == toml::find(v, "key")); toml::find(v, "key").year = 2017; d.year = 2017; BOOST_CHECK(d == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_CHECK(d == moved); } { toml::local_time t(12, 30, 45); value_type v{{"key", t}}; BOOST_CHECK(t == toml::find(v, "key")); toml::find(v, "key").hour = 9; t.hour = 9; BOOST_CHECK(t == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_CHECK(t == moved); } { toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)); value_type v{{"key", dt}}; BOOST_CHECK(dt == toml::find(v, "key")); toml::find(v, "key").date.year = 2017; dt.date.year = 2017; BOOST_CHECK(dt == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_CHECK(dt == moved); } { toml::offset_datetime dt(toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); value_type v{{"key", dt}}; BOOST_CHECK(dt == toml::find(v, "key")); toml::find(v, "key").date.year = 2017; dt.date.year = 2017; BOOST_CHECK(dt == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_CHECK(dt == moved); } { typename value_type::array_type vec; vec.push_back(value_type(42)); vec.push_back(value_type(54)); value_type v{{"key", vec}}; const bool result1 = (vec == toml::find(v, "key")); BOOST_CHECK(result1); toml::find(v, "key").push_back(value_type(123)); vec.push_back(value_type(123)); const bool result2 = (vec == toml::find(v, "key")); BOOST_CHECK(result2); const auto moved = toml::find(std::move(v), "key"); const bool result3 = (vec == moved); BOOST_CHECK(result3); } { typename value_type::table_type tab; tab["key1"] = value_type(42); tab["key2"] = value_type(3.14); value_type v{{"key", tab}}; const bool result1 = (tab == toml::find(v, "key")); BOOST_CHECK(result1); toml::find(v, "key")["key3"] = value_type(123); tab["key3"] = value_type(123); const bool result2 = (tab == toml::find(v, "key")); BOOST_CHECK(result2); const auto moved = toml::find(std::move(v), "key"); const bool result3 = (tab == moved); BOOST_CHECK(result3); } { value_type v1(42); value_type v{{"key", v1}}; BOOST_CHECK(v1 == toml::find(v, "key")); value_type v2(54); toml::find(v, "key") = v2; BOOST_CHECK(v2 == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); BOOST_CHECK(v2 == moved); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_integer_type, value_type, test_value_types) { { value_type v{{"key", 42}}; BOOST_TEST(int(42) == toml::find(v, "key")); BOOST_TEST(short(42) == toml::find(v, "key")); BOOST_TEST(char(42) == toml::find(v, "key")); BOOST_TEST(unsigned(42) == toml::find(v, "key")); BOOST_TEST(long(42) == toml::find(v, "key")); BOOST_TEST(std::int64_t(42) == toml::find(v, "key")); BOOST_TEST(std::uint64_t(42) == toml::find(v, "key")); BOOST_TEST(std::int16_t(42) == toml::find(v, "key")); BOOST_TEST(std::uint16_t(42) == toml::find(v, "key")); BOOST_TEST(std::uint16_t(42) == toml::find(std::move(v), "key")); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_floating_type, value_type, test_value_types) { { value_type v{{"key", 3.14}}; const double ref(3.14); BOOST_TEST(static_cast(ref) == toml::find(v, "key")); BOOST_TEST( ref == toml::find(v, "key")); BOOST_TEST(static_cast(ref) == toml::find(v, "key")); BOOST_TEST(static_cast(ref) == toml::find(std::move(v), "key")); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_string_type, value_type, test_value_types) { { value_type v{{"key", toml::string("foo", toml::string_t::basic)}}; BOOST_TEST("foo" == toml::find(v, "key")); toml::find(v, "key") += "bar"; BOOST_TEST("foobar" == toml::find(v, "key")); } { value_type v{{"key", toml::string("foo", toml::string_t::literal)}}; BOOST_TEST("foo" == toml::find(v, "key")); toml::find(v, "key") += "bar"; BOOST_TEST("foobar" == toml::find(v, "key")); } { value_type v{{"key", toml::string("foo", toml::string_t::literal)}}; const auto moved = toml::find(std::move(v), "key"); BOOST_TEST("foo" == moved); } #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L { value_type v{{"key", toml::string("foo", toml::string_t::basic)}}; BOOST_TEST("foo" == toml::find(v, "key")); } { value_type v{{"key", toml::string("foo", toml::string_t::literal)}}; BOOST_TEST("foo" == toml::find(v, "key")); } #endif } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types) { value_type v{{"key", {42, 54, 69, 72}}}; const std::vector vec = toml::find>(v, "key"); const std::list lst = toml::find>(v, "key"); const std::deque deq = toml::find>(v, "key"); BOOST_TEST(42 == vec.at(0)); BOOST_TEST(54 == vec.at(1)); BOOST_TEST(69 == vec.at(2)); BOOST_TEST(72 == vec.at(3)); std::list::const_iterator iter = lst.begin(); BOOST_TEST(static_cast(42) == *(iter++)); BOOST_TEST(static_cast(54) == *(iter++)); BOOST_TEST(static_cast(69) == *(iter++)); BOOST_TEST(static_cast(72) == *(iter++)); BOOST_TEST(static_cast(42) == deq.at(0)); BOOST_TEST(static_cast(54) == deq.at(1)); BOOST_TEST(static_cast(69) == deq.at(2)); BOOST_TEST(static_cast(72) == deq.at(3)); std::array ary = toml::find>(v, "key"); BOOST_TEST(42 == ary.at(0)); BOOST_TEST(54 == ary.at(1)); BOOST_TEST(69 == ary.at(2)); BOOST_TEST(72 == ary.at(3)); std::tuple tpl = toml::find>(v, "key"); BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast(54) == std::get<1>(tpl)); BOOST_TEST(static_cast(69) == std::get<2>(tpl)); BOOST_TEST(static_cast(72) == std::get<3>(tpl)); value_type p{{"key", {3.14, 2.71}}}; std::pair pr = toml::find >(p, "key"); BOOST_TEST(3.14 == pr.first); BOOST_TEST(2.71 == pr.second); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array, value_type, test_value_types) { value_type v1{{"key", {42, 54, 69, 72}}}; value_type v2{{"key", {42, 54, 69, 72}}}; value_type v3{{"key", {42, 54, 69, 72}}}; value_type v4{{"key", {42, 54, 69, 72}}}; value_type v5{{"key", {42, 54, 69, 72}}}; const std::vector vec = toml::find>(std::move(v1), "key"); const std::list lst = toml::find>(std::move(v2), "key"); const std::deque deq = toml::find>(std::move(v3), "key"); BOOST_TEST(42 == vec.at(0)); BOOST_TEST(54 == vec.at(1)); BOOST_TEST(69 == vec.at(2)); BOOST_TEST(72 == vec.at(3)); std::list::const_iterator iter = lst.begin(); BOOST_TEST(static_cast(42) == *(iter++)); BOOST_TEST(static_cast(54) == *(iter++)); BOOST_TEST(static_cast(69) == *(iter++)); BOOST_TEST(static_cast(72) == *(iter++)); BOOST_TEST(static_cast(42) == deq.at(0)); BOOST_TEST(static_cast(54) == deq.at(1)); BOOST_TEST(static_cast(69) == deq.at(2)); BOOST_TEST(static_cast(72) == deq.at(3)); std::array ary = toml::find>(std::move(v4), "key"); BOOST_TEST(42 == ary.at(0)); BOOST_TEST(54 == ary.at(1)); BOOST_TEST(69 == ary.at(2)); BOOST_TEST(72 == ary.at(3)); std::tuple tpl = toml::find>(std::move(v5), "key"); BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast(54) == std::get<1>(tpl)); BOOST_TEST(static_cast(69) == std::get<2>(tpl)); BOOST_TEST(static_cast(72) == std::get<3>(tpl)); value_type p{{"key", {3.14, 2.71}}}; std::pair pr = toml::find >(std::move(p), "key"); BOOST_TEST(3.14 == pr.first); BOOST_TEST(2.71 == pr.second); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array_of_array, value_type, test_value_types) { value_type v1{42, 54, 69, 72}; value_type v2{"foo", "bar", "baz"}; value_type v{{"key", {v1, v2}}}; std::pair, std::vector> p = toml::find, std::vector>>(v, "key"); BOOST_TEST(p.first.at(0) == 42); BOOST_TEST(p.first.at(1) == 54); BOOST_TEST(p.first.at(2) == 69); BOOST_TEST(p.first.at(3) == 72); BOOST_TEST(p.second.at(0) == "foo"); BOOST_TEST(p.second.at(1) == "bar"); BOOST_TEST(p.second.at(2) == "baz"); std::tuple, std::vector> t = toml::find, std::vector>>(v, "key"); BOOST_TEST(std::get<0>(t).at(0) == 42); BOOST_TEST(std::get<0>(t).at(1) == 54); BOOST_TEST(std::get<0>(t).at(2) == 69); BOOST_TEST(std::get<0>(t).at(3) == 72); BOOST_TEST(std::get<1>(t).at(0) == "foo"); BOOST_TEST(std::get<1>(t).at(1) == "bar"); BOOST_TEST(std::get<1>(t).at(2) == "baz"); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array_of_array, value_type, test_value_types) { value_type a1{42, 54, 69, 72}; value_type a2{"foo", "bar", "baz"}; value_type v1{{"key", {a1, a2}}}; value_type v2{{"key", {a1, a2}}}; std::pair, std::vector> p = toml::find, std::vector>>(std::move(v1), "key"); BOOST_TEST(p.first.at(0) == 42); BOOST_TEST(p.first.at(1) == 54); BOOST_TEST(p.first.at(2) == 69); BOOST_TEST(p.first.at(3) == 72); BOOST_TEST(p.second.at(0) == "foo"); BOOST_TEST(p.second.at(1) == "bar"); BOOST_TEST(p.second.at(2) == "baz"); std::tuple, std::vector> t = toml::find, std::vector>>(std::move(v2), "key"); BOOST_TEST(std::get<0>(t).at(0) == 42); BOOST_TEST(std::get<0>(t).at(1) == 54); BOOST_TEST(std::get<0>(t).at(2) == 69); BOOST_TEST(std::get<0>(t).at(3) == 72); BOOST_TEST(std::get<1>(t).at(0) == "foo"); BOOST_TEST(std::get<1>(t).at(1) == "bar"); BOOST_TEST(std::get<1>(t).at(2) == "baz"); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_table, value_type, test_value_types) { { value_type v1{{"key", { {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }}}; const auto v = toml::find>(v1, "key"); BOOST_TEST(v.at("key1") == 1); BOOST_TEST(v.at("key2") == 2); BOOST_TEST(v.at("key3") == 3); BOOST_TEST(v.at("key4") == 4); } { value_type v1{{"key", { {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }}}; const auto v = toml::find>(std::move(v1), "key"); BOOST_TEST(v.at("key1") == 1); BOOST_TEST(v.at("key2") == 2); BOOST_TEST(v.at("key3") == 3); BOOST_TEST(v.at("key4") == 4); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_date, value_type, test_value_types) { { value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(v1, "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } { value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(std::move(v1), "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_time, value_type, test_value_types) { { value_type v1{{"key", toml::local_time{12, 30, 45}}}; const auto time = toml::find(v1, "key"); BOOST_CHECK(time == std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45)); } { value_type v1{{"key", toml::local_time{12, 30, 45}}}; const auto time = toml::find(std::move(v1), "key"); BOOST_CHECK(time == std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45)); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_datetime, value_type, test_value_types) { { value_type v1{{"key", toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(v1, "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } { value_type v1{{"key", toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(std::move(v1), "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_value_types) { { value_type v1{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{9, 0})}}; // 2018-04-01T12:30:00+09:00 // == 2018-04-01T03:30:00Z const auto date = toml::find(v1, "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! BOOST_CHECK(tmp); const auto tm = *tmp; BOOST_TEST(tm.tm_year + 1900 == 2018); BOOST_TEST(tm.tm_mon + 1 == 4); BOOST_TEST(tm.tm_mday == 1); BOOST_TEST(tm.tm_hour == 3); BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_sec == 0); } { value_type v1{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})}}; // 2018-04-01T12:30:00-08:00 // == 2018-04-01T20:30:00Z const auto date = toml::find(v1, "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! BOOST_CHECK(tmp); const auto tm = *tmp; BOOST_TEST(tm.tm_year + 1900 == 2018); BOOST_TEST(tm.tm_mon + 1 == 4); BOOST_TEST(tm.tm_mday == 1); BOOST_TEST(tm.tm_hour == 20); BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_sec == 0); } { value_type v1{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})}}; // 2018-04-01T12:30:00-08:00 // == 2018-04-01T20:30:00Z const auto date = toml::find(std::move(v1), "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! BOOST_CHECK(tmp); const auto tm = *tmp; BOOST_TEST(tm.tm_year + 1900 == 2018); BOOST_TEST(tm.tm_mon + 1 == 4); BOOST_TEST(tm.tm_mday == 1); BOOST_TEST(tm.tm_hour == 20); BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_sec == 0); } } toml11-3.8.1/tests/test_find_or.cpp000066400000000000000000000540251454646465200172130ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif using test_value_types = std::tuple< toml::basic_value, toml::basic_value, toml::basic_value, toml::basic_value >; namespace test { template std::basic_ostream& operator<<(std::basic_ostream& os, const std::vector& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::deque& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::list& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::unordered_map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } } // test #define TOML11_TEST_FIND_OR_EXACT(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ const toml::toml_type opt opt_expr ; \ const value_type v{{"key", init}}; \ BOOST_TEST(init != opt); \ BOOST_TEST(init == toml::find_or(v, "key", opt)); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_exact, value_type, test_value_types) { TOML11_TEST_FIND_OR_EXACT(boolean, ( true), (false)) TOML11_TEST_FIND_OR_EXACT(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_EXACT(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_EXACT(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_EXACT(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_EXACT(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_EXACT(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_EXACT(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { const typename value_type::array_type init{1,2,3,4,5}; const typename value_type::array_type opt {6,7,8,9,10}; const value_type v{{"key", init}}; BOOST_TEST(init != opt); BOOST_TEST(init == toml::find_or(v, "key", opt)); } { const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; const typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; const value_type v{{"key", init}}; BOOST_TEST(init != opt); BOOST_TEST(init == toml::find_or(v, "key", opt)); } } #undef TOML11_TEST_FIND_OR_EXACT #define TOML11_TEST_FIND_OR_MOVE(toml_type, init_expr, opt_expr) \ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt opt_expr ; \ value_type v{{"key", init}}; \ BOOST_TEST(init != opt); \ const auto moved = toml::find_or(std::move(v), "key", std::move(opt));\ BOOST_TEST(init == moved); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move, value_type, test_value_types) { TOML11_TEST_FIND_OR_MOVE(boolean, ( true), (false)) TOML11_TEST_FIND_OR_MOVE(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_MOVE(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MOVE(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MOVE(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MOVE(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MOVE(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MOVE(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt {6,7,8,9,10}; value_type v{{"key", init}}; BOOST_TEST(init != opt); const auto moved = toml::find_or(std::move(v), "key", std::move(opt)); BOOST_TEST(init == moved); } { typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; value_type v{{"key", init}}; BOOST_TEST(init != opt); const auto moved = toml::find_or(std::move(v), "key", std::move(opt)); BOOST_TEST(init == moved); } } #undef TOML11_TEST_FIND_OR_MOVE #define TOML11_TEST_FIND_OR_MODIFY(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt1 opt_expr ; \ toml::toml_type opt2 opt_expr ; \ value_type v{{"key", init}}; \ BOOST_TEST(init != opt1); \ toml::find_or(v, "key", opt2) = opt1; \ BOOST_TEST(opt1 == toml::find(v, "key"));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_modify, value_type, test_value_types) { TOML11_TEST_FIND_OR_MODIFY(boolean, ( true), (false)) TOML11_TEST_FIND_OR_MODIFY(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_MODIFY(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MODIFY(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MODIFY(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MODIFY(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MODIFY(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MODIFY(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt1{6,7,8,9,10}; typename value_type::array_type opt2{6,7,8,9,10}; BOOST_TEST(init != opt1); value_type v{{"key", init}}; toml::find_or(v, "key", opt2) = opt1; BOOST_TEST(opt1 == toml::find(v, "key")); } { typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt1{{"key1", 54}, {"key2", "bar"}}; typename value_type::table_type opt2{{"key1", 54}, {"key2", "bar"}}; value_type v{{"key", init}}; BOOST_TEST(init != opt1); toml::find_or(v, "key", opt2) = opt1; BOOST_TEST(opt1 == toml::find(v, "key")); } } #undef TOML11_TEST_FIND_OR_MODIFY #define TOML11_TEST_FIND_OR_FALLBACK(init_type, opt_type) \ { \ using namespace test; \ value_type v(init_type); \ BOOST_TEST(opt_type == toml::find_or(v, "key", opt_type));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_fallback, value_type, test_value_types) { const toml::boolean boolean (true); const toml::integer integer (42); const toml::floating floating (3.14); const toml::string string ("foo"); const toml::local_time local_time (12, 30, 45); const toml::local_date local_date (2019, toml::month_t::Apr, 1); const toml::local_datetime local_datetime ( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)); const toml::offset_datetime offset_datetime( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)); using array_type = typename value_type::array_type; using table_type = typename value_type::table_type; const array_type array{1, 2, 3, 4, 5}; const table_type table{{"key1", 42}, {"key2", "foo"}}; TOML11_TEST_FIND_OR_FALLBACK(boolean, integer ); TOML11_TEST_FIND_OR_FALLBACK(boolean, floating ); TOML11_TEST_FIND_OR_FALLBACK(boolean, string ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_time ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_date ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(boolean, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(boolean, array ); TOML11_TEST_FIND_OR_FALLBACK(boolean, table ); TOML11_TEST_FIND_OR_FALLBACK(integer, boolean ); TOML11_TEST_FIND_OR_FALLBACK(integer, floating ); TOML11_TEST_FIND_OR_FALLBACK(integer, string ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_time ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_date ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(integer, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(integer, array ); TOML11_TEST_FIND_OR_FALLBACK(integer, table ); TOML11_TEST_FIND_OR_FALLBACK(floating, boolean ); TOML11_TEST_FIND_OR_FALLBACK(floating, integer ); TOML11_TEST_FIND_OR_FALLBACK(floating, string ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_time ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_date ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(floating, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(floating, array ); TOML11_TEST_FIND_OR_FALLBACK(floating, table ); TOML11_TEST_FIND_OR_FALLBACK(string, boolean ); TOML11_TEST_FIND_OR_FALLBACK(string, integer ); TOML11_TEST_FIND_OR_FALLBACK(string, floating ); TOML11_TEST_FIND_OR_FALLBACK(string, local_time ); TOML11_TEST_FIND_OR_FALLBACK(string, local_date ); TOML11_TEST_FIND_OR_FALLBACK(string, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(string, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(string, array ); TOML11_TEST_FIND_OR_FALLBACK(string, table ); TOML11_TEST_FIND_OR_FALLBACK(local_time, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_time, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_time, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_time, string ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_time, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_time, array ); TOML11_TEST_FIND_OR_FALLBACK(local_time, table ); TOML11_TEST_FIND_OR_FALLBACK(local_date, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_date, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_date, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_date, string ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_date, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_date, array ); TOML11_TEST_FIND_OR_FALLBACK(local_date, table ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(array, boolean ); TOML11_TEST_FIND_OR_FALLBACK(array, integer ); TOML11_TEST_FIND_OR_FALLBACK(array, floating ); TOML11_TEST_FIND_OR_FALLBACK(array, string ); TOML11_TEST_FIND_OR_FALLBACK(array, local_time ); TOML11_TEST_FIND_OR_FALLBACK(array, local_date ); TOML11_TEST_FIND_OR_FALLBACK(array, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(array, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(array, table ); TOML11_TEST_FIND_OR_FALLBACK(table, boolean ); TOML11_TEST_FIND_OR_FALLBACK(table, integer ); TOML11_TEST_FIND_OR_FALLBACK(table, floating ); TOML11_TEST_FIND_OR_FALLBACK(table, string ); TOML11_TEST_FIND_OR_FALLBACK(table, local_time ); TOML11_TEST_FIND_OR_FALLBACK(table, local_date ); TOML11_TEST_FIND_OR_FALLBACK(table, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(table, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(table, array ); } #undef TOML11_TEST_FIND_OR_FALLBACK BOOST_AUTO_TEST_CASE(test_find_or_integer) { { toml::value v{{"num", 42}}; BOOST_TEST(42u == toml::find_or(v, "num", 0u)); BOOST_TEST(0u == toml::find_or(v, "foo", 0u)); } { toml::value v{{"num", 42}}; const auto moved = toml::find_or(std::move(v), "num", 0u); BOOST_TEST(42u == moved); } { toml::value v{{"num", 42}}; const auto moved = toml::find_or(std::move(v), "foo", 0u); BOOST_TEST(0u == moved); } } BOOST_AUTO_TEST_CASE(test_find_or_floating) { { toml::value v1{{"key", 42}}; toml::value v2{{"key", 3.14}}; BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f)); const double ref(3.14); BOOST_TEST(static_cast(ref) == toml::find_or(v2, "key", 2.71f)); } { toml::value v1{{"key", 42}}; toml::value v2{{"key", 3.14}}; const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f); const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f); BOOST_TEST(2.71f == moved1); const double ref(3.14); BOOST_TEST(static_cast(ref) == moved2); } } BOOST_AUTO_TEST_CASE(test_find_or_string) { { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); const std::string s2("bazqux"); BOOST_TEST("foobar" == toml::find_or(v1, "key", s1)); BOOST_TEST("bazqux" == toml::find_or(v2, "key", s1)); std::string& v1r = toml::find_or(v1, "key", s1); std::string& s1r = toml::find_or(v2, "key", s1); BOOST_TEST("foobar" == v1r); BOOST_TEST("bazqux" == s1r); BOOST_TEST("foobar" == toml::find_or(v1, "key", s2)); BOOST_TEST("bazqux" == toml::find_or(v2, "key", s2)); BOOST_TEST("foobar" == toml::find_or(std::move(v1), "key", std::move(s1))); s1 = "bazqux"; // restoring moved value BOOST_TEST("bazqux" == toml::find_or(std::move(v2), "key", std::move(s1))); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); const auto moved1 = toml::find_or(std::move(v1), "key", s1); const auto moved2 = toml::find_or(std::move(v2), "key", s1); BOOST_TEST("foobar" == moved1); BOOST_TEST("bazqux" == moved2); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); std::string s2("bazqux"); const auto moved1 = toml::find_or(std::move(v1), "key", std::move(s1)); const auto moved2 = toml::find_or(std::move(v2), "key", std::move(s2)); BOOST_TEST("foobar" == moved1); BOOST_TEST("bazqux" == moved2); } // string literal { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; BOOST_TEST("foobar" == toml::find_or(v1, "key", "bazqux")); BOOST_TEST("bazqux" == toml::find_or(v2, "key", "bazqux")); const char* lit = "bazqux"; BOOST_TEST("foobar" == toml::find_or(v1, "key", lit)); BOOST_TEST("bazqux" == toml::find_or(v2, "key", lit)); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; const auto moved1 = toml::find_or(std::move(v1), "key", "bazqux"); const auto moved2 = toml::find_or(std::move(v2), "key", "bazqux"); BOOST_TEST("foobar" == moved1); BOOST_TEST("bazqux" == moved2); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; const char* lit = "bazqux"; const auto moved1 = toml::find_or(std::move(v1), "key", lit); const auto moved2 = toml::find_or(std::move(v2), "key", lit); BOOST_TEST("foobar" == moved1); BOOST_TEST("bazqux" == moved2); } } BOOST_AUTO_TEST_CASE(test_find_or_map) { using map_type = std::map; { const toml::value v1{ {"key", {{"key", "value"}}} }; const auto key = toml::find_or(v1, "key", map_type{}); const auto key2 = toml::find_or(v1, "key2", map_type{}); BOOST_TEST(!key.empty()); BOOST_TEST(key2.empty()); BOOST_TEST(key.size() == 1u); BOOST_TEST(key.at("key") == "value"); } { toml::value v1{ {"key", {{"key", "value"}}} }; const auto key = toml::find_or(v1, "key", map_type{}); const auto key2 = toml::find_or(v1, "key2", map_type{}); BOOST_TEST(!key.empty()); BOOST_TEST(key2.empty()); BOOST_TEST(key.size() == 1u); BOOST_TEST(key.at("key") == "value"); } { toml::value v1{ {"key", {{"key", "value"}}} }; toml::value v2(v1); const auto key = toml::find_or(std::move(v1), "key", map_type{}); const auto key2 = toml::find_or(std::move(v2), "key2", map_type{}); BOOST_TEST(!key.empty()); BOOST_TEST(key2.empty()); BOOST_TEST(key.size() == 1u); BOOST_TEST(key.at("key") == "value"); } { toml::value v1{ {"key", {{"key", "value"}}} }; toml::value v2(v1); const auto key = toml::find_or(std::move(v1), "key", map_type{}); const auto key2 = toml::find_or(std::move(v2), "key2", map_type{}); BOOST_TEST(!key.empty()); BOOST_TEST(key2.empty()); BOOST_TEST(key.size() == 1u); BOOST_TEST(key.at("key") == "value"); } } toml11-3.8.1/tests/test_find_or_recursive.cpp000066400000000000000000000443101454646465200212760ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif using test_value_types = std::tuple< toml::basic_value, toml::basic_value, toml::basic_value, toml::basic_value >; namespace test { template std::basic_ostream& operator<<(std::basic_ostream& os, const std::vector& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::deque& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::list& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::unordered_map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } } // test #define TOML11_TEST_FIND_OR_EXACT(toml_type, init_expr, opt_expr) \ { \ using namespace test; \ const toml::toml_type init init_expr ; \ const toml::toml_type opt opt_expr ; \ const value_type v{{"key1", value_type{{"key2", init}} }};\ BOOST_TEST(init != opt); \ BOOST_TEST(init == toml::find_or(v, "key1", "key2", opt));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_exact, value_type, test_value_types) { TOML11_TEST_FIND_OR_EXACT(boolean, ( true), (false)) TOML11_TEST_FIND_OR_EXACT(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_EXACT(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_EXACT(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_EXACT(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_EXACT(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_EXACT(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_EXACT(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { const typename value_type::array_type init{1,2,3,4,5}; const typename value_type::array_type opt {6,7,8,9,10}; const value_type v{{"key", init}}; BOOST_TEST(init != opt); BOOST_TEST(init == toml::find_or(v, "key", opt)); } { const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; const typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; const value_type v{{"key", init}}; BOOST_TEST(init != opt); BOOST_TEST(init == toml::find_or(v, "key", opt)); } } #undef TOML11_TEST_FIND_OR_EXACT #define TOML11_TEST_FIND_OR_MOVE(toml_type, init_expr, opt_expr) \ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt opt_expr ; \ value_type v{{"key1", value_type{{"key2", init}} }}; \ BOOST_TEST(init != opt); \ const auto moved = toml::find_or(std::move(v), "key1", "key2", std::move(opt));\ BOOST_TEST(init == moved); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move, value_type, test_value_types) { TOML11_TEST_FIND_OR_MOVE(boolean, ( true), (false)) TOML11_TEST_FIND_OR_MOVE(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_MOVE(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MOVE(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MOVE(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MOVE(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MOVE(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MOVE(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt {6,7,8,9,10}; value_type v{{"key", init}}; BOOST_TEST(init != opt); const auto moved = toml::find_or(std::move(v), "key", std::move(opt)); BOOST_TEST(init == moved); } { typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; value_type v{{"key", init}}; BOOST_TEST(init != opt); const auto moved = toml::find_or(std::move(v), "key", std::move(opt)); BOOST_TEST(init == moved); } } #undef TOML11_TEST_FIND_OR_MOVE #define TOML11_TEST_FIND_OR_MODIFY(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt1 opt_expr ; \ toml::toml_type opt2 opt_expr ; \ value_type v{{"key1", value_type{{"key2", init}} }}; \ BOOST_TEST(init != opt1); \ toml::find_or(v, "key1", "key2", opt2) = opt1; \ BOOST_TEST(opt1 == toml::find(v, "key1", "key2"));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_modify, value_type, test_value_types) { TOML11_TEST_FIND_OR_MODIFY(boolean, ( true), (false)) TOML11_TEST_FIND_OR_MODIFY(integer, ( 42), ( 54)) TOML11_TEST_FIND_OR_MODIFY(floating, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MODIFY(string, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MODIFY(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MODIFY(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MODIFY(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MODIFY(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt1{6,7,8,9,10}; typename value_type::array_type opt2{6,7,8,9,10}; BOOST_TEST(init != opt1); value_type v{{"key", init}}; toml::find_or(v, "key", opt2) = opt1; BOOST_TEST(opt1 == toml::find(v, "key")); } { typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt1{{"key1", 54}, {"key2", "bar"}}; typename value_type::table_type opt2{{"key1", 54}, {"key2", "bar"}}; value_type v{{"key", init}}; BOOST_TEST(init != opt1); toml::find_or(v, "key", opt2) = opt1; BOOST_TEST(opt1 == toml::find(v, "key")); } } #undef TOML11_TEST_FIND_OR_MODIFY #define TOML11_TEST_FIND_OR_FALLBACK(init_type, opt_type) \ { \ using namespace test; \ value_type v1{{"key1", value_type{{"key3", "foo"}}}}; \ BOOST_TEST(opt_type == toml::find_or(v1, "key1", "key2", opt_type));\ value_type v2{{"key1", "foo"}}; \ BOOST_TEST(opt_type == toml::find_or(v2, "key1", "key3", opt_type));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_fallback, value_type, test_value_types) { const toml::boolean boolean (true); const toml::integer integer (42); const toml::floating floating (3.14); const toml::string string ("foo"); const toml::local_time local_time (12, 30, 45); const toml::local_date local_date (2019, toml::month_t::Apr, 1); const toml::local_datetime local_datetime ( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)); const toml::offset_datetime offset_datetime( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)); using array_type = typename value_type::array_type; using table_type = typename value_type::table_type; const array_type array{1, 2, 3, 4, 5}; const table_type table{{"key1", 42}, {"key2", "foo"}}; TOML11_TEST_FIND_OR_FALLBACK(boolean, integer ); TOML11_TEST_FIND_OR_FALLBACK(boolean, floating ); TOML11_TEST_FIND_OR_FALLBACK(boolean, string ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_time ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_date ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(boolean, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(boolean, array ); TOML11_TEST_FIND_OR_FALLBACK(boolean, table ); TOML11_TEST_FIND_OR_FALLBACK(integer, boolean ); TOML11_TEST_FIND_OR_FALLBACK(integer, floating ); TOML11_TEST_FIND_OR_FALLBACK(integer, string ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_time ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_date ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(integer, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(integer, array ); TOML11_TEST_FIND_OR_FALLBACK(integer, table ); TOML11_TEST_FIND_OR_FALLBACK(floating, boolean ); TOML11_TEST_FIND_OR_FALLBACK(floating, integer ); TOML11_TEST_FIND_OR_FALLBACK(floating, string ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_time ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_date ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(floating, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(floating, array ); TOML11_TEST_FIND_OR_FALLBACK(floating, table ); TOML11_TEST_FIND_OR_FALLBACK(string, boolean ); TOML11_TEST_FIND_OR_FALLBACK(string, integer ); TOML11_TEST_FIND_OR_FALLBACK(string, floating ); TOML11_TEST_FIND_OR_FALLBACK(string, local_time ); TOML11_TEST_FIND_OR_FALLBACK(string, local_date ); TOML11_TEST_FIND_OR_FALLBACK(string, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(string, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(string, array ); TOML11_TEST_FIND_OR_FALLBACK(string, table ); TOML11_TEST_FIND_OR_FALLBACK(local_time, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_time, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_time, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_time, string ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_time, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_time, array ); TOML11_TEST_FIND_OR_FALLBACK(local_time, table ); TOML11_TEST_FIND_OR_FALLBACK(local_date, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_date, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_date, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_date, string ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_date, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_date, array ); TOML11_TEST_FIND_OR_FALLBACK(local_date, table ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(array, boolean ); TOML11_TEST_FIND_OR_FALLBACK(array, integer ); TOML11_TEST_FIND_OR_FALLBACK(array, floating ); TOML11_TEST_FIND_OR_FALLBACK(array, string ); TOML11_TEST_FIND_OR_FALLBACK(array, local_time ); TOML11_TEST_FIND_OR_FALLBACK(array, local_date ); TOML11_TEST_FIND_OR_FALLBACK(array, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(array, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(array, table ); TOML11_TEST_FIND_OR_FALLBACK(table, boolean ); TOML11_TEST_FIND_OR_FALLBACK(table, integer ); TOML11_TEST_FIND_OR_FALLBACK(table, floating ); TOML11_TEST_FIND_OR_FALLBACK(table, string ); TOML11_TEST_FIND_OR_FALLBACK(table, local_time ); TOML11_TEST_FIND_OR_FALLBACK(table, local_date ); TOML11_TEST_FIND_OR_FALLBACK(table, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(table, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(table, array ); } #undef TOML11_TEST_FIND_OR_FALLBACK struct move_only_type { explicit move_only_type(const std::string& n): name_(n) {} void from_toml(const toml::value& v) { this->name_ = toml::find(v, "name"); return; } move_only_type(): name_("default"){} ~move_only_type() = default; move_only_type(move_only_type&&) = default; move_only_type& operator=(move_only_type&&) = default; move_only_type(const move_only_type&) = delete; move_only_type& operator=(const move_only_type&) = delete; bool operator==(const move_only_type& other) const noexcept {return this->name_ == other.name_;} bool operator!=(const move_only_type& other) const noexcept {return this->name_ != other.name_;} bool operator< (const move_only_type& other) const noexcept {return this->name_ < other.name_;} bool operator<=(const move_only_type& other) const noexcept {return this->name_ <= other.name_;} bool operator> (const move_only_type& other) const noexcept {return this->name_ > other.name_;} bool operator>=(const move_only_type& other) const noexcept {return this->name_ >= other.name_;} std::string name_; }; std::ostream& operator<<(std::ostream& os, const move_only_type& mot) { os << mot.name_; return os; } BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move_only, value_type, test_value_types) { const move_only_type ref("reference"); move_only_type opt("optional"); { const value_type v{{"key1", value_type{{"key2", value_type{{"name", "reference"}} }} }}; BOOST_TEST(ref == toml::find_or(v, "key1", "key2", std::move(opt))); } } toml11-3.8.1/tests/test_format_error.cpp000066400000000000000000000042231454646465200202670ustar00rootroot00000000000000#include #include "unit_test.hpp" #include // to check it successfully compiles. it does not check the formatted string. BOOST_AUTO_TEST_CASE(test_1_value) { toml::value val(42); { const std::string pretty_error = toml::format_error("[error] test error", val, "this is a value"); std::cout << pretty_error << std::endl; } { const std::string pretty_error = toml::format_error("[error] test error", val, "this is a value", {"this is a hint"}); std::cout << pretty_error << std::endl; } } BOOST_AUTO_TEST_CASE(test_2_values) { toml::value v1(42); toml::value v2(3.14); { const std::string pretty_error = toml::format_error("[error] test error with two values", v1, "this is the answer", v2, "this is the pi"); std::cout << pretty_error << std::endl; } { const std::string pretty_error = toml::format_error("[error] test error with two values", v1, "this is the answer", v2, "this is the pi", {"hint"}); std::cout << pretty_error << std::endl; } } BOOST_AUTO_TEST_CASE(test_3_values) { toml::value v1(42); toml::value v2(3.14); toml::value v3("foo"); { const std::string pretty_error = toml::format_error("[error] test error with two values", v1, "this is the answer", v2, "this is the pi", v3, "this is a meta-syntactic variable"); std::cout << pretty_error << std::endl; } { const std::string pretty_error = toml::format_error("[error] test error with two values", v1, "this is the answer", v2, "this is the pi", v3, "this is a meta-syntactic variable", {"hint 1", "hint 2"}); std::cout << pretty_error << std::endl; } } toml11-3.8.1/tests/test_get.cpp000066400000000000000000000423661454646465200163570ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif using test_value_types = std::tuple< toml::basic_value, toml::basic_value, toml::basic_value, toml::basic_value >; BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types) { { value_type v(true); BOOST_TEST(true == toml::get(v)); toml::get(v) = false; BOOST_TEST(false == toml::get(v)); toml::boolean x = toml::get(std::move(v)); BOOST_TEST(false == x); } { value_type v(42); BOOST_TEST(toml::integer(42) == toml::get(v)); toml::get(v) = 54; BOOST_TEST(toml::integer(54) == toml::get(v)); toml::integer x = toml::get(std::move(v)); BOOST_TEST(toml::integer(54) == x); } { value_type v(3.14); BOOST_TEST(toml::floating(3.14) == toml::get(v)); toml::get(v) = 2.71; BOOST_TEST(toml::floating(2.71) == toml::get(v)); toml::floating x = toml::get(std::move(v)); BOOST_TEST(toml::floating(2.71) == x); } { value_type v("foo"); BOOST_TEST(toml::string("foo", toml::string_t::basic) == toml::get(v)); toml::get(v).str += "bar"; BOOST_TEST(toml::string("foobar", toml::string_t::basic) == toml::get(v)); toml::string x = toml::get(std::move(v)); BOOST_TEST(toml::string("foobar") == x); } { value_type v("foo", toml::string_t::literal); BOOST_TEST(toml::string("foo", toml::string_t::literal) == toml::get(v)); toml::get(v).str += "bar"; BOOST_TEST(toml::string("foobar", toml::string_t::literal) == toml::get(v)); toml::string x = toml::get(std::move(v)); BOOST_TEST(toml::string("foobar", toml::string_t::literal) == x); } { toml::local_date d(2018, toml::month_t::Apr, 22); value_type v(d); BOOST_TEST(d == toml::get(v)); toml::get(v).year = 2017; d.year = 2017; BOOST_TEST(d == toml::get(v)); toml::local_date x = toml::get(std::move(v)); BOOST_TEST(d == x); } { toml::local_time t(12, 30, 45); value_type v(t); BOOST_TEST(t == toml::get(v)); toml::get(v).hour = 9; t.hour = 9; BOOST_TEST(t == toml::get(v)); toml::local_time x = toml::get(std::move(v)); BOOST_TEST(t == x); } { toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)); value_type v(dt); BOOST_TEST(dt == toml::get(v)); toml::get(v).date.year = 2017; dt.date.year = 2017; BOOST_TEST(dt == toml::get(v)); toml::local_datetime x = toml::get(std::move(v)); BOOST_TEST(dt == x); } { toml::offset_datetime dt(toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); value_type v(dt); BOOST_TEST(dt == toml::get(v)); toml::get(v).date.year = 2017; dt.date.year = 2017; BOOST_TEST(dt == toml::get(v)); toml::offset_datetime x = toml::get(std::move(v)); BOOST_TEST(dt == x); } { using array_type = typename value_type::array_type; array_type vec; vec.push_back(value_type(42)); vec.push_back(value_type(54)); value_type v(vec); BOOST_TEST(vec == toml::get(v)); toml::get(v).push_back(value_type(123)); vec.push_back(value_type(123)); BOOST_TEST(vec == toml::get(v)); array_type x = toml::get(std::move(v)); BOOST_TEST(vec == x); } { using table_type = typename value_type::table_type; table_type tab; tab["key1"] = value_type(42); tab["key2"] = value_type(3.14); value_type v(tab); BOOST_TEST(tab == toml::get(v)); toml::get(v)["key3"] = value_type(123); tab["key3"] = value_type(123); BOOST_TEST(tab == toml::get(v)); table_type x = toml::get(std::move(v)); BOOST_TEST(tab == x); } { value_type v1(42); BOOST_TEST(v1 == toml::get(v1)); value_type v2(54); toml::get(v1) = v2; BOOST_TEST(v2 == toml::get(v1)); value_type x = toml::get(std::move(v1)); BOOST_TEST(v2 == x); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_integer_type, value_type, test_value_types) { { value_type v(42); BOOST_TEST(int(42) == toml::get(v)); BOOST_TEST(short(42) == toml::get(v)); BOOST_TEST(char(42) == toml::get(v)); BOOST_TEST(unsigned(42) == toml::get(v)); BOOST_TEST(long(42) == toml::get(v)); BOOST_TEST(std::int64_t(42) == toml::get(v)); BOOST_TEST(std::uint64_t(42) == toml::get(v)); BOOST_TEST(std::int16_t(42) == toml::get(v)); BOOST_TEST(std::uint16_t(42) == toml::get(v)); BOOST_TEST(std::uint16_t(42) == toml::get(std::move(v))); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_floating_type, value_type, test_value_types) { { value_type v(3.14); const double ref(3.14); BOOST_TEST(static_cast(ref) == toml::get(v)); BOOST_TEST( ref == toml::get(v)); BOOST_TEST(static_cast(ref) == toml::get(v)); BOOST_TEST(static_cast(ref) == toml::get(std::move(v))); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types) { { value_type v("foo", toml::string_t::basic); BOOST_TEST("foo" == toml::get(v)); toml::get(v) += "bar"; BOOST_TEST("foobar" == toml::get(v)); const auto x = toml::get(std::move(v)); BOOST_TEST("foobar" == x); } { value_type v("foo", toml::string_t::literal); BOOST_TEST("foo" == toml::get(v)); toml::get(v) += "bar"; BOOST_TEST("foobar" == toml::get(v)); const auto x = toml::get(std::move(v)); BOOST_TEST("foobar" == x); } #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L { value_type v("foo", toml::string_t::basic); BOOST_TEST("foo" == toml::get(v)); } { value_type v("foo", toml::string_t::literal); BOOST_TEST("foo" == toml::get(v)); } #endif } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types) { { const value_type v{42, 54, 69, 72}; const std::vector vec = toml::get>(v); const std::list lst = toml::get>(v); const std::deque deq = toml::get>(v); BOOST_TEST(42 == vec.at(0)); BOOST_TEST(54 == vec.at(1)); BOOST_TEST(69 == vec.at(2)); BOOST_TEST(72 == vec.at(3)); std::list::const_iterator iter = lst.begin(); BOOST_TEST(static_cast(42) == *(iter++)); BOOST_TEST(static_cast(54) == *(iter++)); BOOST_TEST(static_cast(69) == *(iter++)); BOOST_TEST(static_cast(72) == *(iter++)); BOOST_TEST(static_cast(42) == deq.at(0)); BOOST_TEST(static_cast(54) == deq.at(1)); BOOST_TEST(static_cast(69) == deq.at(2)); BOOST_TEST(static_cast(72) == deq.at(3)); std::array ary = toml::get>(v); BOOST_TEST(42 == ary.at(0)); BOOST_TEST(54 == ary.at(1)); BOOST_TEST(69 == ary.at(2)); BOOST_TEST(72 == ary.at(3)); std::tuple tpl = toml::get>(v); BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast(54) == std::get<1>(tpl)); BOOST_TEST(static_cast(69) == std::get<2>(tpl)); BOOST_TEST(static_cast(72) == std::get<3>(tpl)); const value_type p{3.14, 2.71}; std::pair pr = toml::get >(p); BOOST_TEST(3.14 == pr.first); BOOST_TEST(2.71 == pr.second); } { value_type v{42, 54, 69, 72}; const std::vector vec = toml::get>(std::move(v)); BOOST_TEST(42 == vec.at(0)); BOOST_TEST(54 == vec.at(1)); BOOST_TEST(69 == vec.at(2)); BOOST_TEST(72 == vec.at(3)); } { value_type v{42, 54, 69, 72}; const std::deque deq = toml::get>(std::move(v)); BOOST_TEST(42 == deq.at(0)); BOOST_TEST(54 == deq.at(1)); BOOST_TEST(69 == deq.at(2)); BOOST_TEST(72 == deq.at(3)); } { value_type v{42, 54, 69, 72}; const std::list lst = toml::get>(std::move(v)); std::list::const_iterator iter = lst.begin(); BOOST_TEST(42 == *(iter++)); BOOST_TEST(54 == *(iter++)); BOOST_TEST(69 == *(iter++)); BOOST_TEST(72 == *(iter++)); } { value_type v{42, 54, 69, 72}; std::array ary = toml::get>(std::move(v)); BOOST_TEST(42 == ary.at(0)); BOOST_TEST(54 == ary.at(1)); BOOST_TEST(69 == ary.at(2)); BOOST_TEST(72 == ary.at(3)); } { value_type v{42, 54, 69, 72}; std::tuple tpl = toml::get>(std::move(v)); BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast(54) == std::get<1>(tpl)); BOOST_TEST(static_cast(69) == std::get<2>(tpl)); BOOST_TEST(static_cast(72) == std::get<3>(tpl)); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array_of_array, value_type, test_value_types) { { const value_type v1{42, 54, 69, 72}; const value_type v2{"foo", "bar", "baz"}; const value_type v{v1, v2}; std::pair, std::vector> p = toml::get, std::vector>>(v); BOOST_TEST(p.first.size() == 4u); BOOST_TEST(p.first.at(0) == 42); BOOST_TEST(p.first.at(1) == 54); BOOST_TEST(p.first.at(2) == 69); BOOST_TEST(p.first.at(3) == 72); BOOST_TEST(p.second.size() == 3u); BOOST_TEST(p.second.at(0) == "foo"); BOOST_TEST(p.second.at(1) == "bar"); BOOST_TEST(p.second.at(2) == "baz"); std::tuple, std::vector> t = toml::get, std::vector>>(v); BOOST_TEST(std::get<0>(t).at(0) == 42); BOOST_TEST(std::get<0>(t).at(1) == 54); BOOST_TEST(std::get<0>(t).at(2) == 69); BOOST_TEST(std::get<0>(t).at(3) == 72); BOOST_TEST(std::get<1>(t).at(0) == "foo"); BOOST_TEST(std::get<1>(t).at(1) == "bar"); BOOST_TEST(std::get<1>(t).at(2) == "baz"); } { const value_type v1{42, 54, 69, 72}; const value_type v2{"foo", "bar", "baz"}; value_type v{v1, v2}; std::pair, std::vector> p = toml::get, std::vector>>(std::move(v)); BOOST_TEST(p.first.size() == 4u); BOOST_TEST(p.first.at(0) == 42); BOOST_TEST(p.first.at(1) == 54); BOOST_TEST(p.first.at(2) == 69); BOOST_TEST(p.first.at(3) == 72); BOOST_TEST(p.second.size() == 3u); BOOST_TEST(p.second.at(0) == "foo"); BOOST_TEST(p.second.at(1) == "bar"); BOOST_TEST(p.second.at(2) == "baz"); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_table, value_type, test_value_types) { { const value_type v1{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }; const auto v = toml::get>(v1); BOOST_TEST(v.at("key1") == 1); BOOST_TEST(v.at("key2") == 2); BOOST_TEST(v.at("key3") == 3); BOOST_TEST(v.at("key4") == 4); } { value_type v1{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }; const auto v = toml::get>(std::move(v1)); BOOST_TEST(v.at("key1") == 1); BOOST_TEST(v.at("key2") == 2); BOOST_TEST(v.at("key3") == 3); BOOST_TEST(v.at("key4") == 4); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_local_date, value_type, test_value_types) { value_type v1(toml::local_date{2018, toml::month_t::Apr, 1}); const auto date = std::chrono::system_clock::to_time_t( toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_local_time, value_type, test_value_types) { value_type v1(toml::local_time{12, 30, 45}); const auto time = toml::get(v1); const bool result = time == std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45); BOOST_TEST(result); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_local_datetime, value_type, test_value_types) { value_type v1(toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})); const auto date = std::chrono::system_clock::to_time_t( toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); BOOST_TEST(c == date); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_value_types) { { value_type v1(toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{9, 0})); // 2018-04-01T12:30:00+09:00 // == 2018-04-01T03:30:00Z const auto date = toml::get(v1); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! BOOST_TEST(tmp); const auto tm = *tmp; BOOST_TEST(tm.tm_year + 1900 == 2018); BOOST_TEST(tm.tm_mon + 1 == 4); BOOST_TEST(tm.tm_mday == 1); BOOST_TEST(tm.tm_hour == 3); BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_sec == 0); } { value_type v1(toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})); // 2018-04-01T12:30:00-08:00 // == 2018-04-01T20:30:00Z const auto date = toml::get(v1); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! BOOST_TEST(tmp); const auto tm = *tmp; BOOST_TEST(tm.tm_year + 1900 == 2018); BOOST_TEST(tm.tm_mon + 1 == 4); BOOST_TEST(tm.tm_mday == 1); BOOST_TEST(tm.tm_hour == 20); BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_sec == 0); } } toml11-3.8.1/tests/test_get_or.cpp000066400000000000000000000457541454646465200170630ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif using test_value_types = std::tuple< toml::basic_value, toml::basic_value, toml::basic_value, toml::basic_value >; namespace test { // to compare result values in BOOST_TEST(). // // BOOST_TEST outputs the expected and actual values. Thus it includes the // output stream operator inside. To compile it, we need operator< std::basic_ostream& operator<<(std::basic_ostream& os, const std::vector& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::deque& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::list& v) { os << "[ "; for(const auto& i : v) {os << i << ' ';} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const std::unordered_map& v) { os << "[ "; for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";} os << ']'; return os; } } // test #define TOML11_TEST_GET_OR_EXACT(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ const toml::toml_type opt opt_expr ; \ const value_type v(init); \ BOOST_TEST(init != opt); \ BOOST_TEST(init == toml::get_or(v, opt)); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_exact, value_type, test_value_types) { TOML11_TEST_GET_OR_EXACT(boolean, ( true), (false)) TOML11_TEST_GET_OR_EXACT(integer, ( 42), ( 54)) TOML11_TEST_GET_OR_EXACT(floating, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_EXACT(string, ("foo"), ("bar")) TOML11_TEST_GET_OR_EXACT(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_EXACT(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_EXACT(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_EXACT(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { const typename value_type::array_type init{1,2,3,4,5}; const typename value_type::array_type opt {6,7,8,9,10}; const value_type v(init); BOOST_TEST(init != opt); BOOST_TEST(init == toml::get_or(v, opt)); } { const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; const typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; const value_type v(init); BOOST_TEST(init != opt); BOOST_TEST(init == toml::get_or(v, opt)); } } #undef TOML11_TEST_GET_OR_EXACT #define TOML11_TEST_GET_OR_MOVE_EXACT(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt opt_expr ; \ value_type v(init); \ BOOST_TEST(init != opt); \ const auto opt_ = toml::get_or(std::move(v), std::move(opt));\ BOOST_TEST(init == opt_); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_move, value_type, test_value_types) { TOML11_TEST_GET_OR_MOVE_EXACT(boolean, ( true), (false)) TOML11_TEST_GET_OR_MOVE_EXACT(integer, ( 42), ( 54)) TOML11_TEST_GET_OR_MOVE_EXACT(floating, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_MOVE_EXACT(string, ("foo"), ("bar")) TOML11_TEST_GET_OR_MOVE_EXACT(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_MOVE_EXACT(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_MOVE_EXACT(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_MOVE_EXACT(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { const typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt {6,7,8,9,10}; value_type v(init); BOOST_TEST(init != opt); const auto opt_ = toml::get_or(std::move(v), std::move(opt)); BOOST_TEST(init == opt_); } { const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}}; value_type v(init); BOOST_TEST(init != opt); const auto opt_ = toml::get_or(std::move(v), std::move(opt)); BOOST_TEST(init == opt_); } } #undef TOML11_TEST_GET_OR_MOVE_EXACT #define TOML11_TEST_GET_OR_MODIFY(toml_type, init_expr, opt_expr)\ { \ using namespace test; \ const toml::toml_type init init_expr ; \ toml::toml_type opt1 opt_expr ; \ toml::toml_type opt2 opt_expr ; \ value_type v(init); \ BOOST_TEST(init != opt1); \ toml::get_or(v, opt2) = opt1; \ BOOST_TEST(opt1 == toml::get(v)); \ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_modify, value_type, test_value_types) { TOML11_TEST_GET_OR_MODIFY(boolean, ( true), (false)) TOML11_TEST_GET_OR_MODIFY(integer, ( 42), ( 54)) TOML11_TEST_GET_OR_MODIFY(floating, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_MODIFY(string, ("foo"), ("bar")) TOML11_TEST_GET_OR_MODIFY(local_time, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_MODIFY(local_date, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_MODIFY(local_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_MODIFY(offset_datetime, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) { typename value_type::array_type init{1,2,3,4,5}; typename value_type::array_type opt1{6,7,8,9,10}; typename value_type::array_type opt2{6,7,8,9,10}; BOOST_TEST(init != opt1); value_type v(init); toml::get_or(v, opt2) = opt1; BOOST_TEST(opt1 == toml::get(v)); } { typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}}; typename value_type::table_type opt1{{"key1", 54}, {"key2", "bar"}}; typename value_type::table_type opt2{{"key1", 54}, {"key2", "bar"}}; value_type v(init); BOOST_TEST(init != opt1); toml::get_or(v, opt2) = opt1; BOOST_TEST(opt1 == toml::get(v)); } } #undef TOML11_TEST_GET_OR_MODIFY #define TOML11_TEST_GET_OR_FALLBACK(init_type, opt_type) \ { \ using namespace test; \ value_type v(init_type); \ BOOST_TEST(opt_type == toml::get_or(v, opt_type));\ } \ /**/ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_fallback, value_type, test_value_types) { const toml::boolean boolean (true); const toml::integer integer (42); const toml::floating floating (3.14); const toml::string string ("foo"); const toml::local_time local_time (12, 30, 45); const toml::local_date local_date (2019, toml::month_t::Apr, 1); const toml::local_datetime local_datetime ( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)); const toml::offset_datetime offset_datetime( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)); using array_type = typename value_type::array_type; using table_type = typename value_type::table_type; const array_type array{1, 2, 3, 4, 5}; const table_type table{{"key1", 42}, {"key2", "foo"}}; TOML11_TEST_GET_OR_FALLBACK(boolean, integer ); TOML11_TEST_GET_OR_FALLBACK(boolean, floating ); TOML11_TEST_GET_OR_FALLBACK(boolean, string ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_time ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_date ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(boolean, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(boolean, array ); TOML11_TEST_GET_OR_FALLBACK(boolean, table ); TOML11_TEST_GET_OR_FALLBACK(integer, boolean ); TOML11_TEST_GET_OR_FALLBACK(integer, floating ); TOML11_TEST_GET_OR_FALLBACK(integer, string ); TOML11_TEST_GET_OR_FALLBACK(integer, local_time ); TOML11_TEST_GET_OR_FALLBACK(integer, local_date ); TOML11_TEST_GET_OR_FALLBACK(integer, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(integer, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(integer, array ); TOML11_TEST_GET_OR_FALLBACK(integer, table ); TOML11_TEST_GET_OR_FALLBACK(floating, boolean ); TOML11_TEST_GET_OR_FALLBACK(floating, integer ); TOML11_TEST_GET_OR_FALLBACK(floating, string ); TOML11_TEST_GET_OR_FALLBACK(floating, local_time ); TOML11_TEST_GET_OR_FALLBACK(floating, local_date ); TOML11_TEST_GET_OR_FALLBACK(floating, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(floating, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(floating, array ); TOML11_TEST_GET_OR_FALLBACK(floating, table ); TOML11_TEST_GET_OR_FALLBACK(string, boolean ); TOML11_TEST_GET_OR_FALLBACK(string, integer ); TOML11_TEST_GET_OR_FALLBACK(string, floating ); TOML11_TEST_GET_OR_FALLBACK(string, local_time ); TOML11_TEST_GET_OR_FALLBACK(string, local_date ); TOML11_TEST_GET_OR_FALLBACK(string, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(string, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(string, array ); TOML11_TEST_GET_OR_FALLBACK(string, table ); TOML11_TEST_GET_OR_FALLBACK(local_time, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_time, integer ); TOML11_TEST_GET_OR_FALLBACK(local_time, floating ); TOML11_TEST_GET_OR_FALLBACK(local_time, string ); TOML11_TEST_GET_OR_FALLBACK(local_time, local_date ); TOML11_TEST_GET_OR_FALLBACK(local_time, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(local_time, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_time, array ); TOML11_TEST_GET_OR_FALLBACK(local_time, table ); TOML11_TEST_GET_OR_FALLBACK(local_date, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_date, integer ); TOML11_TEST_GET_OR_FALLBACK(local_date, floating ); TOML11_TEST_GET_OR_FALLBACK(local_date, string ); TOML11_TEST_GET_OR_FALLBACK(local_date, local_time ); TOML11_TEST_GET_OR_FALLBACK(local_date, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(local_date, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_date, array ); TOML11_TEST_GET_OR_FALLBACK(local_date, table ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, integer ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, floating ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, string ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, local_time ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, local_date ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_datetime, array ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, table ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, boolean ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, integer ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, floating ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, string ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_time ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_date ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, array ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, table ); TOML11_TEST_GET_OR_FALLBACK(array, boolean ); TOML11_TEST_GET_OR_FALLBACK(array, integer ); TOML11_TEST_GET_OR_FALLBACK(array, floating ); TOML11_TEST_GET_OR_FALLBACK(array, string ); TOML11_TEST_GET_OR_FALLBACK(array, local_time ); TOML11_TEST_GET_OR_FALLBACK(array, local_date ); TOML11_TEST_GET_OR_FALLBACK(array, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(array, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(array, table ); TOML11_TEST_GET_OR_FALLBACK(table, boolean ); TOML11_TEST_GET_OR_FALLBACK(table, integer ); TOML11_TEST_GET_OR_FALLBACK(table, floating ); TOML11_TEST_GET_OR_FALLBACK(table, string ); TOML11_TEST_GET_OR_FALLBACK(table, local_time ); TOML11_TEST_GET_OR_FALLBACK(table, local_date ); TOML11_TEST_GET_OR_FALLBACK(table, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(table, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(table, array ); } #undef TOML11_TEST_GET_OR_FALLBACK BOOST_AUTO_TEST_CASE(test_get_or_integer) { { toml::value v1(42); toml::value v2(3.14); BOOST_TEST(42u == toml::get_or(v1, 0u)); BOOST_TEST(0u == toml::get_or(v2, 0u)); } { toml::value v1(42); toml::value v2(3.14); BOOST_TEST(42u == toml::get_or(std::move(v1), 0u)); BOOST_TEST(0u == toml::get_or(std::move(v2), 0u)); } } BOOST_AUTO_TEST_CASE(test_get_or_floating) { { toml::value v1(42); toml::value v2(3.14); BOOST_TEST(2.71f == toml::get_or(v1, 2.71f)); BOOST_TEST(static_cast(v2.as_floating()) == toml::get_or(v2, 2.71f)); } { toml::value v1(42); toml::value v2(3.14); BOOST_TEST(2.71f == toml::get_or(std::move(v1), 2.71f)); BOOST_TEST(static_cast(3.14) == toml::get_or(std::move(v2), 2.71f)); } } BOOST_AUTO_TEST_CASE(test_get_or_string) { { toml::value v1("foobar"); toml::value v2(42); std::string s1("bazqux"); const std::string s2("bazqux"); BOOST_TEST("foobar" == toml::get_or(v1, s1)); BOOST_TEST("bazqux" == toml::get_or(v2, s1)); std::string& v1r = toml::get_or(v1, s1); std::string& s1r = toml::get_or(v2, s1); BOOST_TEST("foobar" == v1r); BOOST_TEST("bazqux" == s1r); BOOST_TEST("foobar" == toml::get_or(v1, s2)); BOOST_TEST("bazqux" == toml::get_or(v2, s2)); BOOST_TEST("foobar" == toml::get_or(v1, std::move(s1))); BOOST_TEST("bazqux" == toml::get_or(v2, std::move(s1))); } { toml::value v1("foobar"); toml::value v2(42); std::string s1("bazqux"); const std::string s2("bazqux"); BOOST_TEST("foobar" == toml::get_or(std::move(v1), s1)); BOOST_TEST("bazqux" == toml::get_or(std::move(v2), s1)); } { toml::value v1("foobar"); toml::value v2(42); BOOST_TEST("foobar" == toml::get_or(v1, "bazqux")); BOOST_TEST("bazqux" == toml::get_or(v2, "bazqux")); const char* lit = "bazqux"; BOOST_TEST("foobar" == toml::get_or(v1, lit)); BOOST_TEST("bazqux" == toml::get_or(v2, lit)); } { toml::value v1("foobar"); toml::value v2(42); BOOST_TEST("foobar" == toml::get_or(std::move(v1), "bazqux")); BOOST_TEST("bazqux" == toml::get_or(std::move(v2), "bazqux")); } { toml::value v1("foobar"); toml::value v2(42); const char* lit = "bazqux"; BOOST_TEST("foobar" == toml::get_or(v1, lit)); BOOST_TEST("bazqux" == toml::get_or(v2, lit)); } } toml11-3.8.1/tests/test_lex_aux.hpp000066400000000000000000000044311454646465200172410ustar00rootroot00000000000000#include #include #include #include #include #define TOML11_TEST_LEX_ACCEPT(lxr, tkn, expct) \ do { \ const std::string token (tkn); \ const std::string expected(expct); \ toml::detail::location loc("test", token); \ const auto result = lxr::invoke(loc); \ BOOST_TEST(result.is_ok()); \ if(result.is_ok()){ \ const auto region = result.unwrap(); \ BOOST_TEST(region.str() == expected); \ BOOST_TEST(region.str().size() == expected.size()); \ BOOST_TEST(static_cast(std::distance( \ loc.begin(), loc.iter())) == region.size()); \ } else { \ std::cerr << "lexer failed with input `"; \ std::cerr << token << "`. expected `" << expected << "`\n"; \ std::cerr << "reason: " << result.unwrap_err() << '\n'; \ } \ } while(false); \ /**/ #define TOML11_TEST_LEX_REJECT(lxr, tkn) \ do { \ const std::string token (tkn); \ toml::detail::location loc("test", token); \ const auto result = lxr::invoke(loc); \ BOOST_TEST(result.is_err()); \ const bool loc_same = (loc.begin() == loc.iter()); \ BOOST_TEST(loc_same); \ } while(false); /**/ toml11-3.8.1/tests/test_lex_boolean.cpp000066400000000000000000000012241454646465200200530ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_correct) { TOML11_TEST_LEX_ACCEPT(lex_boolean, "true", "true"); TOML11_TEST_LEX_ACCEPT(lex_boolean, "false", "false"); TOML11_TEST_LEX_ACCEPT(lex_boolean, "true # trailing", "true"); TOML11_TEST_LEX_ACCEPT(lex_boolean, "false # trailing", "false"); } BOOST_AUTO_TEST_CASE(test_invalid) { TOML11_TEST_LEX_REJECT(lex_boolean, "TRUE"); TOML11_TEST_LEX_REJECT(lex_boolean, "FALSE"); TOML11_TEST_LEX_REJECT(lex_boolean, "True"); TOML11_TEST_LEX_REJECT(lex_boolean, "False"); } toml11-3.8.1/tests/test_lex_datetime.cpp000066400000000000000000000034531454646465200202360ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_offset_datetime) { TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z"); TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27T07:32:00-07:00", "1979-05-27T07:32:00-07:00"); TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27T07:32:00.999999-07:00", "1979-05-27T07:32:00.999999-07:00"); TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27 07:32:00Z", "1979-05-27 07:32:00Z"); TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27 07:32:00-07:00", "1979-05-27 07:32:00-07:00"); TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, "1979-05-27 07:32:00.999999-07:00", "1979-05-27 07:32:00.999999-07:00"); } BOOST_AUTO_TEST_CASE(test_local_datetime) { TOML11_TEST_LEX_ACCEPT(lex_local_date_time, "1979-05-27T07:32:00", "1979-05-27T07:32:00"); TOML11_TEST_LEX_ACCEPT(lex_local_date_time, "1979-05-27T07:32:00.999999", "1979-05-27T07:32:00.999999"); TOML11_TEST_LEX_ACCEPT(lex_local_date_time, "1979-05-27 07:32:00", "1979-05-27 07:32:00"); TOML11_TEST_LEX_ACCEPT(lex_local_date_time, "1979-05-27 07:32:00.999999", "1979-05-27 07:32:00.999999"); } BOOST_AUTO_TEST_CASE(test_local_date) { TOML11_TEST_LEX_ACCEPT(lex_local_date, "1979-05-27", "1979-05-27"); } BOOST_AUTO_TEST_CASE(test_local_time) { TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00", "07:32:00"); TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00.999999", "07:32:00.999999"); } toml11-3.8.1/tests/test_lex_floating.cpp000066400000000000000000000107171454646465200202460ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" #include using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_fractional_valid) { TOML11_TEST_LEX_ACCEPT(lex_float, "1.0", "1.0" ); TOML11_TEST_LEX_ACCEPT(lex_float, "0.1", "0.1" ); TOML11_TEST_LEX_ACCEPT(lex_float, "0.001", "0.001" ); TOML11_TEST_LEX_ACCEPT(lex_float, "0.100", "0.100" ); TOML11_TEST_LEX_ACCEPT(lex_float, "+3.14", "+3.14" ); TOML11_TEST_LEX_ACCEPT(lex_float, "-3.14", "-3.14" ); TOML11_TEST_LEX_ACCEPT(lex_float, "3.1415_9265_3589", "3.1415_9265_3589" ); TOML11_TEST_LEX_ACCEPT(lex_float, "+3.1415_9265_3589", "+3.1415_9265_3589"); TOML11_TEST_LEX_ACCEPT(lex_float, "-3.1415_9265_3589", "-3.1415_9265_3589"); TOML11_TEST_LEX_ACCEPT(lex_float, "123_456.789", "123_456.789" ); TOML11_TEST_LEX_ACCEPT(lex_float, "+123_456.789", "+123_456.789" ); TOML11_TEST_LEX_ACCEPT(lex_float, "-123_456.789", "-123_456.789" ); } BOOST_AUTO_TEST_CASE(test_fractional_invalid) { TOML11_TEST_LEX_REJECT(lex_float, "0."); TOML11_TEST_LEX_REJECT(lex_float, ".0"); TOML11_TEST_LEX_REJECT(lex_float, "01.0"); TOML11_TEST_LEX_REJECT(lex_float, "3,14"); TOML11_TEST_LEX_REJECT(lex_float, "+-1.0"); TOML11_TEST_LEX_REJECT(lex_float, "1._0"); } BOOST_AUTO_TEST_CASE(test_exponential_valid) { TOML11_TEST_LEX_ACCEPT(lex_float, "1e10", "1e10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1e+10", "1e+10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1e-10", "1e-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "+1e10", "+1e10"); TOML11_TEST_LEX_ACCEPT(lex_float, "+1e+10", "+1e+10"); TOML11_TEST_LEX_ACCEPT(lex_float, "+1e-10", "+1e-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "-1e10", "-1e10"); TOML11_TEST_LEX_ACCEPT(lex_float, "-1e+10", "-1e+10"); TOML11_TEST_LEX_ACCEPT(lex_float, "-1e-10", "-1e-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "123e-10", "123e-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1E10", "1E10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1E+10", "1E+10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1E-10", "1E-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "123E-10", "123E-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-10", "1_2_3E-10"); TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-1_0", "1_2_3E-1_0"); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-01", "1_2_3E-01"); TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-0_1", "1_2_3E-0_1"); #endif } BOOST_AUTO_TEST_CASE(test_exponential_invalid) { // accept partially TOML11_TEST_LEX_ACCEPT(lex_float, "1e1E0", "1e1"); TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1"); } BOOST_AUTO_TEST_CASE(test_both_valid) { TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e23", "6.02e23"); TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e+23", "6.02e+23"); TOML11_TEST_LEX_ACCEPT(lex_float, "1.112_650_06e-17", "1.112_650_06e-17"); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e-07", "1.0e-07"); #endif } BOOST_AUTO_TEST_CASE(test_both_invalid) { TOML11_TEST_LEX_REJECT(lex_float, "01e1.0"); // accept partially TOML11_TEST_LEX_ACCEPT(lex_float, "1e1.0", "1e1"); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e_01", "1.0"); TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e0__1", "1.0e0"); #endif } BOOST_AUTO_TEST_CASE(test_special_floating_point) { TOML11_TEST_LEX_ACCEPT(lex_float, "inf", "inf"); TOML11_TEST_LEX_ACCEPT(lex_float, "+inf", "+inf"); TOML11_TEST_LEX_ACCEPT(lex_float, "-inf", "-inf"); TOML11_TEST_LEX_ACCEPT(lex_float, "nan", "nan"); TOML11_TEST_LEX_ACCEPT(lex_float, "+nan", "+nan"); TOML11_TEST_LEX_ACCEPT(lex_float, "-nan", "-nan"); } toml11-3.8.1/tests/test_lex_integer.cpp000066400000000000000000000074501454646465200201000ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_decimal_correct) { TOML11_TEST_LEX_ACCEPT(lex_integer, "1234", "1234" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "+1234", "+1234" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "-1234", "-1234" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0", "0" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "1_2_3_4", "1_2_3_4" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "+1_2_3_4", "+1_2_3_4" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "-1_2_3_4", "-1_2_3_4" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "123_456_789", "123_456_789"); } BOOST_AUTO_TEST_CASE(test_decimal_invalid) { TOML11_TEST_LEX_ACCEPT(lex_integer, "123+45", "123"); TOML11_TEST_LEX_ACCEPT(lex_integer, "123-45", "123"); TOML11_TEST_LEX_ACCEPT(lex_integer, "01234", "0"); TOML11_TEST_LEX_ACCEPT(lex_integer, "123__45", "123"); TOML11_TEST_LEX_REJECT(lex_integer, "_1234"); } BOOST_AUTO_TEST_CASE(test_hex_correct) { TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADBEEF", "0xDEADBEEF" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdeadbeef", "0xdeadbeef" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADbeef", "0xDEADbeef" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD_BEEF", "0xDEAD_BEEF"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_beef", "0xdead_beef"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_BEEF", "0xdead_BEEF"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xFF", "0xFF" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0x00FF", "0x00FF" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0x0000FF", "0x0000FF"); } BOOST_AUTO_TEST_CASE(test_hex_invalid) { TOML11_TEST_LEX_ACCEPT(lex_integer, "0xAPPLE", "0xA"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD+BEEF", "0xDEAD"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD__BEEF", "0xDEAD"); TOML11_TEST_LEX_REJECT(lex_hex_int, "0x_DEADBEEF"); TOML11_TEST_LEX_REJECT(lex_hex_int, "0x+DEADBEEF"); TOML11_TEST_LEX_REJECT(lex_hex_int, "-0xFF" ); TOML11_TEST_LEX_REJECT(lex_hex_int, "-0x00FF" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0x_DEADBEEF", "0" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0x+DEADBEEF", "0" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "-0xFF" , "-0" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "-0x00FF" , "-0" ); } BOOST_AUTO_TEST_CASE(test_oct_correct) { TOML11_TEST_LEX_ACCEPT(lex_integer, "0o777", "0o777" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o7_7_7", "0o7_7_7"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o007", "0o007" ); } BOOST_AUTO_TEST_CASE(test_oct_invalid) { TOML11_TEST_LEX_ACCEPT(lex_integer, "0o77+7", "0o77"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o1__0", "0o1"); TOML11_TEST_LEX_REJECT(lex_oct_int, "0o800" ); TOML11_TEST_LEX_REJECT(lex_oct_int, "-0o777"); TOML11_TEST_LEX_REJECT(lex_oct_int, "0o+777"); TOML11_TEST_LEX_REJECT(lex_oct_int, "0o_10" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o800", "0"); TOML11_TEST_LEX_ACCEPT(lex_integer, "-0o777", "-0"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o+777", "0"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0o_10", "0"); } BOOST_AUTO_TEST_CASE(test_bin_correct) { TOML11_TEST_LEX_ACCEPT(lex_integer, "0b10000", "0b10000" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0b010000", "0b010000" ); TOML11_TEST_LEX_ACCEPT(lex_integer, "0b01_00_00", "0b01_00_00"); TOML11_TEST_LEX_ACCEPT(lex_integer, "0b111111", "0b111111" ); } BOOST_AUTO_TEST_CASE(test_bin_invalid) { TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11__11", "0b11"); TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11+11" , "0b11"); TOML11_TEST_LEX_REJECT(lex_bin_int, "-0b10000"); TOML11_TEST_LEX_REJECT(lex_bin_int, "0b_1111" ); } toml11-3.8.1/tests/test_lex_key_comment.cpp000066400000000000000000000040171454646465200207510ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_bare_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "barekey", "barekey"); TOML11_TEST_LEX_ACCEPT(lex_key, "bare-key", "bare-key"); TOML11_TEST_LEX_ACCEPT(lex_key, "bare_key", "bare_key"); TOML11_TEST_LEX_ACCEPT(lex_key, "1234", "1234"); } BOOST_AUTO_TEST_CASE(test_quoted_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); // UTF-8 codepoint of characters that looks like "key" written upside down TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", "\"\xCA\x8E\xC7\x9D\xCA\x9E\""); TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); } BOOST_AUTO_TEST_CASE(test_dotted_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "physical.color", "physical.color"); TOML11_TEST_LEX_ACCEPT(lex_key, "physical.shape", "physical.shape"); TOML11_TEST_LEX_ACCEPT(lex_key, "x.y", "x.y"); TOML11_TEST_LEX_ACCEPT(lex_key, "x . y", "x . y"); TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z", "x.y.z"); TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z", "x. y .z"); TOML11_TEST_LEX_ACCEPT(lex_key, "x .y. z", "x .y. z"); TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z", "x . y . z"); TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z.w", "x.y.z.w"); TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z. w", "x. y .z. w"); TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z . w", "x . y . z . w"); TOML11_TEST_LEX_ACCEPT(lex_key, "site.\"google.com\"", "site.\"google.com\""); } BOOST_AUTO_TEST_CASE(test_comment) { TOML11_TEST_LEX_ACCEPT(lex_comment, "# hoge", "# hoge"); TOML11_TEST_LEX_ACCEPT(lex_comment, "# \n", "# "); TOML11_TEST_LEX_ACCEPT(lex_comment, "# \r\n", "# "); TOML11_TEST_LEX_ACCEPT(lex_comment, "# # \n", "# # "); } toml11-3.8.1/tests/test_lex_string.cpp000066400000000000000000000106171454646465200177500ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_lex_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_string) { TOML11_TEST_LEX_ACCEPT(lex_string, "\"The quick brown fox jumps over the lazy dog\"", "\"The quick brown fox jumps over the lazy dog\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\'The quick brown fox jumps over the lazy dog\'", "\'The quick brown fox jumps over the lazy dog\'"); TOML11_TEST_LEX_ACCEPT(lex_ml_basic_string, "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\""); TOML11_TEST_LEX_ACCEPT(lex_ml_literal_string, "'''The quick brown fox \njumps over the lazy dog'''", "'''The quick brown fox \njumps over the lazy dog'''"); } BOOST_AUTO_TEST_CASE(test_basic_string) { TOML11_TEST_LEX_ACCEPT(lex_string, "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"192.168.1.1\"", "\"192.168.1.1\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in "\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters) TOML11_TEST_LEX_ACCEPT(lex_string, "\"You'll hate me after this - #\"", "\"You'll hate me after this - #\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\" And when \\\"'s are in the string, along with # \\\"\"", "\" And when \\\"'s are in the string, along with # \\\"\""); } BOOST_AUTO_TEST_CASE(test_ml_basic_string) { TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\""); } BOOST_AUTO_TEST_CASE(test_literal_string) { TOML11_TEST_LEX_ACCEPT(lex_string, "'C:\\Users\\nodejs\\templates'", "'C:\\Users\\nodejs\\templates'"); TOML11_TEST_LEX_ACCEPT(lex_string, "'\\\\ServerX\\admin$\\system32\\'", "'\\\\ServerX\\admin$\\system32\\'"); TOML11_TEST_LEX_ACCEPT(lex_string, "'Tom \"Dubs\" Preston-Werner'", "'Tom \"Dubs\" Preston-Werner'"); TOML11_TEST_LEX_ACCEPT(lex_string, "'<\\i\\c*\\s*>'", "'<\\i\\c*\\s*>'"); } BOOST_AUTO_TEST_CASE(test_ml_literal_string) { TOML11_TEST_LEX_ACCEPT(lex_string, "'''I [dw]on't need \\d{2} apples'''", "'''I [dw]on't need \\d{2} apples'''"); TOML11_TEST_LEX_ACCEPT(lex_string, "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''"); TOML11_TEST_LEX_ACCEPT(lex_string, "''''That's still pointless', she said.'''", "''''That's still pointless', she said.'''"); TOML11_TEST_LEX_ACCEPT(lex_string, "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''"); TOML11_TEST_LEX_ACCEPT(lex_string, "''''This,' she said, 'is just a pointless statement.''''", "''''This,' she said, 'is just a pointless statement.''''"); } toml11-3.8.1/tests/test_literals.cpp000066400000000000000000000247611454646465200174160ustar00rootroot00000000000000#include #include "unit_test.hpp" #include BOOST_AUTO_TEST_CASE(test_file_as_literal) { using namespace toml::literals::toml_literals; { const toml::value r{{"a", 42}, {"b", "baz"}}; const toml::value v = R"( a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"c", 3.14}, {"table", toml::table{{"a", 42}, {"b", "baz"}}} }; const toml::value v = R"( c = 3.14 [table] a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"table", toml::table{{"a", 42}, {"b", "baz"}}} }; const toml::value v = R"( [table] a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"array_of_tables", toml::array{toml::table{}}} }; const toml::value v = R"( [[array_of_tables]] )"_toml; BOOST_TEST(r == v); } } BOOST_AUTO_TEST_CASE(test_value_as_literal) { using namespace toml::literals::toml_literals; { const toml::value v1 = "true"_toml; const toml::value v2 = "false"_toml; BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(toml::get(v1)); BOOST_TEST(!toml::get(v2)); } { const toml::value v1 = "123_456"_toml; const toml::value v2 = "0b0010"_toml; const toml::value v3 = "0xDEADBEEF"_toml; BOOST_TEST(v1.is_integer()); BOOST_TEST(v2.is_integer()); BOOST_TEST(v3.is_integer()); BOOST_TEST(toml::get(v1) == 123456); BOOST_TEST(toml::get(v2) == 2); BOOST_TEST(toml::get(v3) == 0xDEADBEEF); } { const toml::value v1 = "3.1415"_toml; const toml::value v2 = "6.02e+23"_toml; BOOST_TEST(v1.is_floating()); BOOST_TEST(v2.is_floating()); BOOST_TEST(toml::get(v1) == 3.1415, boost::test_tools::tolerance(0.00001)); BOOST_TEST(toml::get(v2) == 6.02e23, boost::test_tools::tolerance(0.0001)); } { const toml::value v1 = R"("foo")"_toml; const toml::value v2 = R"('foo')"_toml; const toml::value v3 = R"("""foo""")"_toml; const toml::value v4 = R"('''foo''')"_toml; BOOST_TEST(v1.is_string()); BOOST_TEST(v2.is_string()); BOOST_TEST(v3.is_string()); BOOST_TEST(v4.is_string()); BOOST_TEST(toml::get(v1) == "foo"); BOOST_TEST(toml::get(v2) == "foo"); BOOST_TEST(toml::get(v3) == "foo"); BOOST_TEST(toml::get(v4) == "foo"); } { { const toml::value v1 = R"([1,2,3])"_toml; BOOST_TEST(v1.is_array()); const bool result = (toml::get>(v1) == std::vector{1,2,3}); BOOST_TEST(result); } { const toml::value v2 = R"([1,])"_toml; BOOST_TEST(v2.is_array()); const bool result = (toml::get>(v2) == std::vector{1}); BOOST_TEST(result); } { const toml::value v3 = R"([[1,]])"_toml; BOOST_TEST(v3.is_array()); const bool result = (toml::get>(toml::get(v3).front()) == std::vector{1}); BOOST_TEST(result); } { const toml::value v4 = R"([[1],])"_toml; BOOST_TEST(v4.is_array()); const bool result = (toml::get>(toml::get(v4).front()) == std::vector{1}); BOOST_TEST(result); } } { const toml::value v1 = R"({a = 42})"_toml; BOOST_TEST(v1.is_table()); const bool result = toml::get>(v1) == std::map{{"a", 42}}; BOOST_TEST(result); } { const toml::value v1 = "1979-05-27"_toml; BOOST_TEST(v1.is_local_date()); BOOST_TEST(toml::get(v1) == toml::local_date(1979, toml::month_t::May, 27)); } { const toml::value v1 = "12:00:00"_toml; BOOST_TEST(v1.is_local_time()); const bool result = toml::get(v1) == std::chrono::hours(12); BOOST_TEST(result); } { const toml::value v1 = "1979-05-27T07:32:00"_toml; BOOST_TEST(v1.is_local_datetime()); BOOST_TEST(toml::get(v1) == toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); } { const toml::value v1 = "1979-05-27T07:32:00Z"_toml; BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(toml::get(v1) == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } } BOOST_AUTO_TEST_CASE(test_file_as_u8_literal) { using namespace toml::literals::toml_literals; { const toml::value r{{"a", 42}, {"b", "baz"}}; const toml::value v = u8R"( a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"c", 3.14}, {"table", toml::table{{"a", 42}, {"b", "baz"}}} }; const toml::value v = u8R"( c = 3.14 [table] a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"table", toml::table{{"a", 42}, {"b", "baz"}}} }; const toml::value v = u8R"( [table] a = 42 b = "baz" )"_toml; BOOST_TEST(r == v); } { const toml::value r{ {"array_of_tables", toml::array{toml::table{}}} }; const toml::value v = u8R"( [[array_of_tables]] )"_toml; BOOST_TEST(r == v); } } BOOST_AUTO_TEST_CASE(test_value_as_u8_literal) { using namespace toml::literals::toml_literals; { const toml::value v1 = u8"true"_toml; const toml::value v2 = u8"false"_toml; BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(toml::get(v1)); BOOST_TEST(!toml::get(v2)); } { const toml::value v1 = u8"123_456"_toml; const toml::value v2 = u8"0b0010"_toml; const toml::value v3 = u8"0xDEADBEEF"_toml; BOOST_TEST(v1.is_integer()); BOOST_TEST(v2.is_integer()); BOOST_TEST(v3.is_integer()); BOOST_TEST(toml::get(v1) == 123456); BOOST_TEST(toml::get(v2) == 2); BOOST_TEST(toml::get(v3) == 0xDEADBEEF); } { const toml::value v1 = u8"3.1415"_toml; const toml::value v2 = u8"6.02e+23"_toml; BOOST_TEST(v1.is_floating()); BOOST_TEST(v2.is_floating()); BOOST_TEST(toml::get(v1) == 3.1415, boost::test_tools::tolerance(0.00001)); BOOST_TEST(toml::get(v2) == 6.02e23, boost::test_tools::tolerance(0.0001)); } { const toml::value v1 = u8R"("foo")"_toml; const toml::value v2 = u8R"('foo')"_toml; const toml::value v3 = u8R"("""foo""")"_toml; const toml::value v4 = u8R"('''foo''')"_toml; const toml::value v5 = u8R"("ひらがな")"_toml; BOOST_TEST(v1.is_string()); BOOST_TEST(v2.is_string()); BOOST_TEST(v3.is_string()); BOOST_TEST(v4.is_string()); BOOST_TEST(v5.is_string()); BOOST_TEST(toml::get(v1) == "foo"); BOOST_TEST(toml::get(v2) == "foo"); BOOST_TEST(toml::get(v3) == "foo"); BOOST_TEST(toml::get(v4) == "foo"); BOOST_TEST(toml::get(v5) == "\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA"); } { { const toml::value v1 = u8R"([1,2,3])"_toml; BOOST_TEST(v1.is_array()); const bool result = (toml::get>(v1) == std::vector{1,2,3}); BOOST_TEST(result); } { const toml::value v2 = u8R"([1,])"_toml; BOOST_TEST(v2.is_array()); const bool result = (toml::get>(v2) == std::vector{1}); BOOST_TEST(result); } { const toml::value v3 = u8R"([[1,]])"_toml; BOOST_TEST(v3.is_array()); const bool result = (toml::get>(toml::get(v3).front()) == std::vector{1}); BOOST_TEST(result); } { const toml::value v4 = u8R"([[1],])"_toml; BOOST_TEST(v4.is_array()); const bool result = (toml::get>(toml::get(v4).front()) == std::vector{1}); BOOST_TEST(result); } } { const toml::value v1 = u8R"({a = 42})"_toml; BOOST_TEST(v1.is_table()); const bool result = toml::get>(v1) == std::map{{"a", 42}}; BOOST_TEST(result); } { const toml::value v1 = u8"1979-05-27"_toml; BOOST_TEST(v1.is_local_date()); BOOST_TEST(toml::get(v1) == toml::local_date(1979, toml::month_t::May, 27)); } { const toml::value v1 = u8"12:00:00"_toml; BOOST_TEST(v1.is_local_time()); const bool result = toml::get(v1) == std::chrono::hours(12); BOOST_TEST(result); } { const toml::value v1 = u8"1979-05-27T07:32:00"_toml; BOOST_TEST(v1.is_local_datetime()); BOOST_TEST(toml::get(v1) == toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); } { const toml::value v1 = u8"1979-05-27T07:32:00Z"_toml; BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(toml::get(v1) == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } } toml11-3.8.1/tests/test_multiple_translation_unit_1.cpp000066400000000000000000000004221454646465200233130ustar00rootroot00000000000000#include int read_a(const toml::table&); int main() { const std::string content("a = 0"); std::istringstream iss(content); const auto data = toml::parse(iss, "test_multiple_translation_unit.toml"); return read_a(toml::get(data)); } toml11-3.8.1/tests/test_multiple_translation_unit_2.cpp000066400000000000000000000001401454646465200233110ustar00rootroot00000000000000#include int read_a(const toml::table& t) { return toml::get(t.at("a")); } toml11-3.8.1/tests/test_parse_array.cpp000066400000000000000000000320531454646465200201000ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_oneline_array) { TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[]", array()); { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[3,1,4,1,5]", a); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[\"foo\", \"bar\", \"baz\"]", a); } { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[3,1,4,1,5,]", a); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[\"foo\", \"bar\", \"baz\",]", a); } } BOOST_AUTO_TEST_CASE(test_oneline_array_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[]", toml::value(array())); { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5]", toml::value(a)); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\"]", toml::value(a)); } { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5,]", toml::value(a)); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\",]", toml::value(a)); } } BOOST_AUTO_TEST_CASE(test_multiline_array) { TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type()); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\n#comment\n]", typename basic_value::array_type()); { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); } { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); } { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); } { typename basic_value::array_type a(5); a[0] = basic_value(3, {"comment"}); a[1] = basic_value(1, {"comment"}); a[2] = basic_value(4, {"comment"}); a[3] = basic_value(1, {"comment"}); a[4] = basic_value(5, {"comment"}); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("b#r"); a[2] = basic_value("b#z"); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo", {"comment"}); a[1] = basic_value("b#r", {"comment"}); a[2] = basic_value("b#z", {"comment"}); TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); } } BOOST_AUTO_TEST_CASE(test_multiline_array_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\n#comment\n]", basic_value< discard_comments>(typename basic_value< discard_comments>::array_type())); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\n#comment\n]", basic_value(typename basic_value::array_type())); { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[3,\n1,\n4,\n1,\n5]", basic_value(a)); } { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[3,\n1,\n4,\n1,\n5]", basic_value(a)); } { typename basic_value::array_type a(5); a[0] = basic_value(3); a[1] = basic_value(1); a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value(a)); } { typename basic_value::array_type a(5); a[0] = basic_value(3, {"comment"}); a[1] = basic_value(1, {"comment"}); a[2] = basic_value(4, {"comment"}); a[3] = basic_value(1, {"comment"}); a[4] = basic_value(5, {"comment"}); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value(a)); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value(a)); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value(a)); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("b#r"); a[2] = basic_value("b#z"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value(a)); } { typename basic_value::array_type a(3); a[0] = basic_value("foo", {"comment"}); a[1] = basic_value("b#r", {"comment"}); a[2] = basic_value("b#z", {"comment"}); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value(a)); } } BOOST_AUTO_TEST_CASE(test_heterogeneous_array) { #ifndef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed."); #else { array a(5); a[0] = toml::value("foo"); a[1] = toml::value(3.14); a[2] = toml::value(42); a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; a[4] = toml::value{{"key", "value"}}; TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a)); } { array a(5); a[0] = toml::value("foo"); a[1] = toml::value(3.14); a[2] = toml::value(42); a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; a[4] = toml::value{{"key", "value"}}; TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a)); } { array a(5); a[0] = toml::value("foo"); a[1] = toml::value(3.14); a[2] = toml::value(42); a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; a[4] = toml::value{{"key", "value"}}; TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a)); } { array a(5); a[0] = toml::value("foo"); a[1] = toml::value(3.14); a[2] = toml::value(42); a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; a[4] = toml::value{{"key", "value"}}; TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a)); } #endif } BOOST_AUTO_TEST_CASE(test_comments_after_comma) { { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[ \"foo\" # comment\n" ", \"bar\" # comment\n" ", \"baz\" # comment\n" "]", basic_value(a)); } { typename basic_value::array_type a(3); a[0] = basic_value("foo", {" comment"}); a[1] = basic_value("bar", {" comment"}); a[2] = basic_value("baz", {" comment"}); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value>, "[ \"foo\" # comment\n" ", \"bar\" # comment\n" ", \"baz\" # comment\n" "]", basic_value(a)); } } toml11-3.8.1/tests/test_parse_aux.hpp000066400000000000000000000067601454646465200175720ustar00rootroot00000000000000#include #include #include #include // some of the parsers returns not only a value but also a region. #define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \ do { \ const std::string token(tkn); \ toml::detail::location loc("test", token); \ const auto result = psr(loc); \ BOOST_TEST(result.is_ok()); \ if(result.is_ok()){ \ BOOST_TEST(result.unwrap().first == expct); \ } else { \ std::cerr << "parser " << #psr << " failed with input `"; \ std::cerr << token << "`.\n"; \ std::cerr << "reason: " << result.unwrap_err() << '\n'; \ } \ } while(false); \ /**/ #define TOML11_TEST_PARSE_EQUAL_VAT(psr, tkn, expct) \ do { \ const std::string token(tkn); \ toml::detail::location loc("test", token); \ const auto result = psr(loc, 0); \ BOOST_TEST(result.is_ok()); \ if(result.is_ok()){ \ BOOST_TEST(result.unwrap().first == expct); \ } else { \ std::cerr << "parser " << #psr << " failed with input `"; \ std::cerr << token << "`.\n"; \ std::cerr << "reason: " << result.unwrap_err() << '\n'; \ } \ } while(false); \ /**/ #define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ do { \ const std::string token(tkn); \ toml::detail::location loc("test", token); \ const auto result = psr(loc, 0); \ BOOST_TEST(result.is_ok()); \ if(result.is_ok()){ \ BOOST_TEST(result.unwrap() == expct); \ } else { \ std::cerr << "parse_value failed with input `"; \ std::cerr << token << "`.\n"; \ std::cerr << "reason: " << result.unwrap_err() << '\n'; \ } \ } while(false); \ /**/ toml11-3.8.1/tests/test_parse_boolean.cpp000066400000000000000000000010041454646465200203710ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_boolean) { TOML11_TEST_PARSE_EQUAL(parse_boolean, "true", true); TOML11_TEST_PARSE_EQUAL(parse_boolean, "false", false); } BOOST_AUTO_TEST_CASE(test_boolean_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "true", toml::value( true)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "false", toml::value(false)); } toml11-3.8.1/tests/test_parse_datetime.cpp000066400000000000000000000411331454646465200205550ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_time) { TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00", toml::local_time(7, 32, 0)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.99", toml::local_time(7, 32, 0, 990, 0)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999", toml::local_time(7, 32, 0, 999, 0)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999999", toml::local_time(7, 32, 0, 999, 999)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "00:00:00.000000", toml::local_time( 0, 0, 0, 0, 0)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "23:59:59.999999", toml::local_time(23, 59, 59, 999, 999)); TOML11_TEST_PARSE_EQUAL(parse_local_time, "23:59:60.999999", toml::local_time(23, 59, 60, 999, 999)); // leap second } BOOST_AUTO_TEST_CASE(test_time_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00", toml::value(toml::local_time(7, 32, 0))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.99", toml::value(toml::local_time(7, 32, 0, 990, 0))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999", toml::value(toml::local_time(7, 32, 0, 999, 0))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999999", toml::value(toml::local_time(7, 32, 0, 999, 999))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "00:00:00.000000", toml::value(toml::local_time( 0, 0, 0, 0, 0))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "23:59:59.999999", toml::value(toml::local_time(23, 59, 59, 999, 999))); std::istringstream stream1(std::string("invalid-datetime = 24:00:00")); std::istringstream stream2(std::string("invalid-datetime = 00:60:00")); std::istringstream stream3(std::string("invalid-datetime = 00:00:61")); BOOST_CHECK_THROW(toml::parse(stream1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream2), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream3), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_date) { TOML11_TEST_PARSE_EQUAL(parse_local_date, "1979-05-27", toml::local_date(1979, toml::month_t::May, 27)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-01-01", toml::local_date(2000, toml::month_t::Jan, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-01-31", toml::local_date(2000, toml::month_t::Jan, 31)); std::istringstream stream1_1(std::string("invalid-datetime = 2000-01-00")); std::istringstream stream1_2(std::string("invalid-datetime = 2000-01-32")); BOOST_CHECK_THROW(toml::parse(stream1_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream1_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-02-01", toml::local_date(2000, toml::month_t::Feb, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-02-29", toml::local_date(2000, toml::month_t::Feb, 29)); std::istringstream stream2_1(std::string("invalid-datetime = 2000-02-00")); std::istringstream stream2_2(std::string("invalid-datetime = 2000-02-30")); BOOST_CHECK_THROW(toml::parse(stream2_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream2_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2001-02-28", toml::local_date(2001, toml::month_t::Feb, 28)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2004-02-29", toml::local_date(2004, toml::month_t::Feb, 29)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2100-02-28", toml::local_date(2100, toml::month_t::Feb, 28)); std::istringstream stream2_3(std::string("invalid-datetime = 2001-02-29")); std::istringstream stream2_4(std::string("invalid-datetime = 2004-02-30")); std::istringstream stream2_5(std::string("invalid-datetime = 2100-02-29")); BOOST_CHECK_THROW(toml::parse(stream2_3), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream2_4), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream2_5), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-03-01", toml::local_date(2000, toml::month_t::Mar, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-03-31", toml::local_date(2000, toml::month_t::Mar, 31)); std::istringstream stream3_1(std::string("invalid-datetime = 2000-03-00")); std::istringstream stream3_2(std::string("invalid-datetime = 2000-03-32")); BOOST_CHECK_THROW(toml::parse(stream3_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream3_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-04-01", toml::local_date(2000, toml::month_t::Apr, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-04-30", toml::local_date(2000, toml::month_t::Apr, 30)); std::istringstream stream4_1(std::string("invalid-datetime = 2000-04-00")); std::istringstream stream4_2(std::string("invalid-datetime = 2000-04-31")); BOOST_CHECK_THROW(toml::parse(stream4_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream4_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-05-01", toml::local_date(2000, toml::month_t::May, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-05-31", toml::local_date(2000, toml::month_t::May, 31)); std::istringstream stream5_1(std::string("invalid-datetime = 2000-05-00")); std::istringstream stream5_2(std::string("invalid-datetime = 2000-05-32")); BOOST_CHECK_THROW(toml::parse(stream5_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream5_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-06-01", toml::local_date(2000, toml::month_t::Jun, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-06-30", toml::local_date(2000, toml::month_t::Jun, 30)); std::istringstream stream6_1(std::string("invalid-datetime = 2000-06-00")); std::istringstream stream6_2(std::string("invalid-datetime = 2000-06-31")); BOOST_CHECK_THROW(toml::parse(stream6_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream6_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-07-01", toml::local_date(2000, toml::month_t::Jul, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-07-31", toml::local_date(2000, toml::month_t::Jul, 31)); std::istringstream stream7_1(std::string("invalid-datetime = 2000-07-00")); std::istringstream stream7_2(std::string("invalid-datetime = 2000-07-32")); BOOST_CHECK_THROW(toml::parse(stream7_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream7_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-08-01", toml::local_date(2000, toml::month_t::Aug, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-08-31", toml::local_date(2000, toml::month_t::Aug, 31)); std::istringstream stream8_1(std::string("invalid-datetime = 2000-08-00")); std::istringstream stream8_2(std::string("invalid-datetime = 2000-08-32")); BOOST_CHECK_THROW(toml::parse(stream8_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream8_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-09-01", toml::local_date(2000, toml::month_t::Sep, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-09-30", toml::local_date(2000, toml::month_t::Sep, 30)); std::istringstream stream9_1(std::string("invalid-datetime = 2000-09-00")); std::istringstream stream9_2(std::string("invalid-datetime = 2000-09-31")); BOOST_CHECK_THROW(toml::parse(stream9_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream9_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-10-01", toml::local_date(2000, toml::month_t::Oct, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-10-31", toml::local_date(2000, toml::month_t::Oct, 31)); std::istringstream stream10_1(std::string("invalid-datetime = 2000-10-00")); std::istringstream stream10_2(std::string("invalid-datetime = 2000-10-32")); BOOST_CHECK_THROW(toml::parse(stream10_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream10_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-11-01", toml::local_date(2000, toml::month_t::Nov, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-11-30", toml::local_date(2000, toml::month_t::Nov, 30)); std::istringstream stream11_1(std::string("invalid-datetime = 2000-11-00")); std::istringstream stream11_2(std::string("invalid-datetime = 2000-11-31")); BOOST_CHECK_THROW(toml::parse(stream11_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream11_2), toml::syntax_error); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-12-01", toml::local_date(2000, toml::month_t::Dec, 1)); TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-12-31", toml::local_date(2000, toml::month_t::Dec, 31)); std::istringstream stream12_1(std::string("invalid-datetime = 2000-12-00")); std::istringstream stream12_2(std::string("invalid-datetime = 2000-12-32")); BOOST_CHECK_THROW(toml::parse(stream12_1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream12_2), toml::syntax_error); std::istringstream stream13_1(std::string("invalid-datetime = 2000-13-01")); BOOST_CHECK_THROW(toml::parse(stream13_1), toml::syntax_error); std::istringstream stream0_1(std::string("invalid-datetime = 2000-00-01")); BOOST_CHECK_THROW(toml::parse(stream0_1), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_date_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27", value(toml::local_date(1979, toml::month_t::May, 27))); } BOOST_AUTO_TEST_CASE(test_datetime) { TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.99", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.999999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.99", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.999999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.99", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.999999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); } BOOST_AUTO_TEST_CASE(test_datetime_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.99", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.999999", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.99", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.999999", toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); } BOOST_AUTO_TEST_CASE(test_offset_datetime) { TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00Z", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99Z", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0))); TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999Z", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0))); TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00+09:00", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(9, 0))); TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99+09:00", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0))); TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999+09:00", toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0))); std::istringstream stream1(std::string("invalid-datetime = 2000-01-01T00:00:00+24:00")); std::istringstream stream2(std::string("invalid-datetime = 2000-01-01T00:00:00+00:60")); BOOST_CHECK_THROW(toml::parse(stream1), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream2), toml::syntax_error); } BOOST_AUTO_TEST_CASE(test_offset_datetime_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00Z", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99Z", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999Z", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00+09:00", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(9, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99+09:00", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0)))); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999+09:00", toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0)))); } toml11-3.8.1/tests/test_parse_file.cpp000066400000000000000000001147361454646465200177120ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include BOOST_AUTO_TEST_CASE(test_example) { const auto data = toml::parse(testinput("example.toml")); BOOST_TEST(toml::find(data, "title") == "TOML Example"); const auto& owner = toml::find(data, "owner"); { BOOST_TEST(toml::find(owner, "name") == "Tom Preston-Werner"); BOOST_TEST(toml::find(owner, "organization") == "GitHub"); BOOST_TEST(toml::find(owner, "bio") == "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_TEST(toml::find(owner, "dob") == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } const auto& database = toml::find(data, "database"); { BOOST_TEST(toml::find(database, "server") == "192.168.1.1"); const std::vector expected_ports{8001, 8001, 8002}; BOOST_CHECK(toml::find>(database, "ports") == expected_ports); BOOST_TEST(toml::find(database, "connection_max") == 5000); BOOST_TEST(toml::find(database, "enabled") == true); } const auto& servers = toml::find(data, "servers"); { toml::table alpha = toml::find(servers, "alpha"); BOOST_TEST(toml::get(alpha.at("ip")) == "10.0.0.1"); BOOST_TEST(toml::get(alpha.at("dc")) == "eqdc10"); toml::table beta = toml::find(servers, "beta"); BOOST_TEST(toml::get(beta.at("ip")) == "10.0.0.2"); BOOST_TEST(toml::get(beta.at("dc")) == "eqdc10"); BOOST_TEST(toml::get(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD"); } const auto& clients = toml::find(data, "clients"); { toml::array clients_data = toml::find(clients, "data"); std::vector expected_name{"gamma", "delta"}; BOOST_CHECK(toml::get>(clients_data.at(0)) == expected_name); std::vector expected_number{1, 2}; BOOST_CHECK(toml::get>(clients_data.at(1)) == expected_number); std::vector expected_hosts{"alpha", "omega"}; BOOST_CHECK(toml::find>(clients, "hosts") == expected_hosts); } std::vector products = toml::find>(data, "products"); { BOOST_TEST(toml::get(products.at(0).at("name")) == "Hammer"); BOOST_TEST(toml::get(products.at(0).at("sku")) == 738594937); BOOST_TEST(toml::get(products.at(1).at("name")) == "Nail"); BOOST_TEST(toml::get(products.at(1).at("sku")) == 284758393); BOOST_TEST(toml::get(products.at(1).at("color")) == "gray"); } } BOOST_AUTO_TEST_CASE(test_example_stream) { std::ifstream ifs(testinput("example.toml"), std::ios::binary); const auto data = toml::parse(ifs); BOOST_TEST(toml::find(data, "title") == "TOML Example"); const auto& owner = toml::find(data, "owner"); { BOOST_TEST(toml::find(owner, "name") == "Tom Preston-Werner"); BOOST_TEST(toml::find(owner, "organization") == "GitHub"); BOOST_TEST(toml::find(owner, "bio") == "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_TEST(toml::find(owner, "dob") == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } const auto& database = toml::find(data, "database"); { BOOST_TEST(toml::find(database, "server") == "192.168.1.1"); const std::vector expected_ports{8001, 8001, 8002}; BOOST_CHECK(toml::find>(database, "ports") == expected_ports); BOOST_TEST(toml::find(database, "connection_max") == 5000); BOOST_TEST(toml::find(database, "enabled") == true); } const auto& servers = toml::find(data, "servers"); { toml::table alpha = toml::find(servers, "alpha"); BOOST_TEST(toml::get(alpha.at("ip")) == "10.0.0.1"); BOOST_TEST(toml::get(alpha.at("dc")) == "eqdc10"); toml::table beta = toml::find(servers, "beta"); BOOST_TEST(toml::get(beta.at("ip")) == "10.0.0.2"); BOOST_TEST(toml::get(beta.at("dc")) == "eqdc10"); BOOST_TEST(toml::get(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD"); } const auto& clients = toml::find(data, "clients"); { toml::array clients_data = toml::find(clients, "data"); std::vector expected_name{"gamma", "delta"}; BOOST_CHECK(toml::get>(clients_data.at(0)) == expected_name); std::vector expected_number{1, 2}; BOOST_CHECK(toml::get>(clients_data.at(1)) == expected_number); std::vector expected_hosts{"alpha", "omega"}; BOOST_CHECK(toml::find>(clients, "hosts") == expected_hosts); } std::vector products = toml::find>(data, "products"); { BOOST_TEST(toml::get(products.at(0).at("name")) == "Hammer"); BOOST_TEST(toml::get(products.at(0).at("sku")) == 738594937); BOOST_TEST(toml::get(products.at(1).at("name")) == "Nail"); BOOST_TEST(toml::get(products.at(1).at("sku")) == 284758393); BOOST_TEST(toml::get(products.at(1).at("color")) == "gray"); } } BOOST_AUTO_TEST_CASE(test_example_file_pointer) { FILE * file = fopen(testinput("example.toml").c_str(), "rb"); const auto data = toml::parse(file, "toml/tests/example.toml"); fclose(file); BOOST_TEST(toml::find(data, "title") == "TOML Example"); const auto& owner = toml::find(data, "owner"); { BOOST_TEST(toml::find(owner, "name") == "Tom Preston-Werner"); BOOST_TEST(toml::find(owner, "organization") == "GitHub"); BOOST_TEST(toml::find(owner, "bio") == "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_TEST(toml::find(owner, "dob") == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } const auto& database = toml::find(data, "database"); { BOOST_TEST(toml::find(database, "server") == "192.168.1.1"); const std::vector expected_ports{8001, 8001, 8002}; BOOST_CHECK(toml::find>(database, "ports") == expected_ports); BOOST_TEST(toml::find(database, "connection_max") == 5000); BOOST_TEST(toml::find(database, "enabled") == true); } const auto& servers = toml::find(data, "servers"); { toml::table alpha = toml::find(servers, "alpha"); BOOST_TEST(toml::get(alpha.at("ip")) == "10.0.0.1"); BOOST_TEST(toml::get(alpha.at("dc")) == "eqdc10"); toml::table beta = toml::find(servers, "beta"); BOOST_TEST(toml::get(beta.at("ip")) == "10.0.0.2"); BOOST_TEST(toml::get(beta.at("dc")) == "eqdc10"); BOOST_TEST(toml::get(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD"); } const auto& clients = toml::find(data, "clients"); { toml::array clients_data = toml::find(clients, "data"); std::vector expected_name{"gamma", "delta"}; BOOST_CHECK(toml::get>(clients_data.at(0)) == expected_name); std::vector expected_number{1, 2}; BOOST_CHECK(toml::get>(clients_data.at(1)) == expected_number); std::vector expected_hosts{"alpha", "omega"}; BOOST_CHECK(toml::find>(clients, "hosts") == expected_hosts); } std::vector products = toml::find>(data, "products"); { BOOST_TEST(toml::get(products.at(0).at("name")) == "Hammer"); BOOST_TEST(toml::get(products.at(0).at("sku")) == 738594937); BOOST_TEST(toml::get(products.at(1).at("name")) == "Nail"); BOOST_TEST(toml::get(products.at(1).at("sku")) == 284758393); BOOST_TEST(toml::get(products.at(1).at("color")) == "gray"); } } BOOST_AUTO_TEST_CASE(test_fruit) { const auto data = toml::parse(testinput("fruit.toml")); const auto blah = toml::find(toml::find(data, "fruit"), "blah"); BOOST_TEST(toml::find(blah.at(0), "name") == "apple"); BOOST_TEST(toml::find(blah.at(1), "name") == "banana"); { const auto physical = toml::find(blah.at(0), "physical"); BOOST_TEST(toml::find(physical, "color") == "red"); BOOST_TEST(toml::find(physical, "shape") == "round"); } { const auto physical = toml::find(blah.at(1), "physical"); BOOST_TEST(toml::find(physical, "color") == "yellow"); BOOST_TEST(toml::find(physical, "shape") == "bent"); } } BOOST_AUTO_TEST_CASE(test_hard_example) { const auto data = toml::parse(testinput("hard_example.toml")); const auto the = toml::find(data, "the"); BOOST_TEST(toml::find(the, "test_string") == "You'll hate me after this - #"); const auto hard = toml::find(the, "hard"); const std::vector expected_the_hard_test_array{"] ", " # "}; BOOST_CHECK(toml::find>(hard, "test_array") == expected_the_hard_test_array); const std::vector expected_the_hard_test_array2{ "Test #11 ]proved that", "Experiment #9 was a success"}; BOOST_CHECK(toml::find>(hard, "test_array2") == expected_the_hard_test_array2); BOOST_TEST(toml::find(hard, "another_test_string") == " Same thing, but with a string #"); BOOST_TEST(toml::find(hard, "harder_test_string") == " And when \"'s are in the string, along with # \""); const auto bit = toml::find(hard, "bit#"); BOOST_TEST(toml::find(bit, "what?") == "You don't think some user won't do that?"); const std::vector expected_multi_line_array{"]"}; BOOST_CHECK(toml::find>(bit, "multi_line_array") == expected_multi_line_array); } BOOST_AUTO_TEST_CASE(test_hard_example_comment) { const auto data = toml::parse(testinput("hard_example.toml")); const auto the = toml::find(data, "the"); BOOST_TEST(toml::find(the, "test_string") == "You'll hate me after this - #"); const auto hard = toml::find(the, "hard"); const std::vector expected_the_hard_test_array{"] ", " # "}; BOOST_CHECK(toml::find>(hard, "test_array") == expected_the_hard_test_array); const std::vector expected_the_hard_test_array2{ "Test #11 ]proved that", "Experiment #9 was a success"}; BOOST_CHECK(toml::find>(hard, "test_array2") == expected_the_hard_test_array2); BOOST_TEST(toml::find(hard, "another_test_string") == " Same thing, but with a string #"); BOOST_TEST(toml::find(hard, "harder_test_string") == " And when \"'s are in the string, along with # \""); const auto bit = toml::find(hard, "bit#"); BOOST_TEST(toml::find(bit, "what?") == "You don't think some user won't do that?"); const std::vector expected_multi_line_array{"]"}; BOOST_CHECK(toml::find>(bit, "multi_line_array") == expected_multi_line_array); } BOOST_AUTO_TEST_CASE(test_example_preserve_comment) { const auto data = toml::parse(testinput("example.toml")); BOOST_TEST(toml::find(data, "title") == "TOML Example"); const auto& owner = toml::find(data, "owner"); { BOOST_TEST(toml::find(owner, "name") == "Tom Preston-Werner"); BOOST_TEST(toml::find(owner, "organization") == "GitHub"); BOOST_TEST(toml::find(owner, "bio") == "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_TEST(toml::find(owner, "dob") == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); BOOST_TEST(toml::find(owner, "dob").comments().at(0) == " First class dates? Why not?"); } const auto& database = toml::find(data, "database"); { BOOST_TEST(toml::find(database, "server") == "192.168.1.1"); const std::vector expected_ports{8001, 8001, 8002}; BOOST_CHECK(toml::find>(database, "ports") == expected_ports); BOOST_TEST(toml::find(database, "connection_max") == 5000); BOOST_TEST(toml::find(database, "enabled") == true); } const auto& servers = toml::find(data, "servers"); { const auto& alpha = toml::find(servers, "alpha"); BOOST_TEST(alpha.comments().at(0) == " You can indent as you please. Tabs or spaces. TOML don't care."); BOOST_TEST(toml::find(alpha, "ip") == "10.0.0.1"); BOOST_TEST(toml::find(alpha, "dc") == "eqdc10"); const auto& beta = toml::find(servers, "beta"); BOOST_TEST(toml::find(beta, "ip") == "10.0.0.2"); BOOST_TEST(toml::find(beta, "dc") == "eqdc10"); BOOST_TEST(toml::find(beta, "country") == "\xE4\xB8\xAD\xE5\x9B\xBD"); BOOST_TEST(toml::find(beta, "country").comments().at(0) == " This should be parsed as UTF-8"); } const auto& clients = toml::find(data, "clients"); { BOOST_TEST(toml::find(clients, "data").comments().at(0) == " just an update to make sure parsers support it"); toml::array clients_data = toml::find(clients, "data"); std::vector expected_name{"gamma", "delta"}; BOOST_CHECK(toml::get>(clients_data.at(0)) == expected_name); std::vector expected_number{1, 2}; BOOST_CHECK(toml::get>(clients_data.at(1)) == expected_number); std::vector expected_hosts{"alpha", "omega"}; BOOST_CHECK(toml::find>(clients, "hosts") == expected_hosts); BOOST_TEST(toml::find(clients, "hosts").comments().at(0) == " Line breaks are OK when inside arrays"); } std::vector products = toml::find>(data, "products"); { BOOST_TEST(toml::get(products.at(0).at("name")) == "Hammer"); BOOST_TEST(toml::get(products.at(0).at("sku")) == 738594937); BOOST_TEST(toml::get(products.at(1).at("name")) == "Nail"); BOOST_TEST(toml::get(products.at(1).at("sku")) == 284758393); BOOST_TEST(toml::get(products.at(1).at("color")) == "gray"); } } BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque) { const auto data = toml::parse( testinput("example.toml")); static_assert(std::is_same::type> >::value, ""); static_assert(std::is_same::type> >::value, ""); BOOST_TEST(toml::find(data, "title") == "TOML Example"); const auto& owner = toml::find(data, "owner"); { BOOST_TEST(toml::find(owner, "name") == "Tom Preston-Werner"); BOOST_TEST(toml::find(owner, "organization") == "GitHub"); BOOST_TEST(toml::find(owner, "bio") == "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_TEST(toml::find(owner, "dob") == toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); BOOST_TEST(toml::find(owner, "dob").comments().at(0) == " First class dates? Why not?"); } const auto& database = toml::find(data, "database"); { BOOST_TEST(toml::find(database, "server") == "192.168.1.1"); const std::vector expected_ports{8001, 8001, 8002}; BOOST_CHECK(toml::find>(database, "ports") == expected_ports); BOOST_TEST(toml::find(database, "connection_max") == 5000); BOOST_TEST(toml::find(database, "enabled") == true); } const auto& servers = toml::find(data, "servers"); { const auto& alpha = toml::find(servers, "alpha"); BOOST_TEST(alpha.comments().at(0) == " You can indent as you please. Tabs or spaces. TOML don't care."); BOOST_TEST(toml::find(alpha, "ip") == "10.0.0.1"); BOOST_TEST(toml::find(alpha, "dc") == "eqdc10"); const auto& beta = toml::find(servers, "beta"); BOOST_TEST(toml::find(beta, "ip") == "10.0.0.2"); BOOST_TEST(toml::find(beta, "dc") == "eqdc10"); BOOST_TEST(toml::find(beta, "country") == "\xE4\xB8\xAD\xE5\x9B\xBD"); BOOST_TEST(toml::find(beta, "country").comments().at(0) == " This should be parsed as UTF-8"); } const auto& clients = toml::find(data, "clients"); { BOOST_TEST(toml::find(clients, "data").comments().at(0) == " just an update to make sure parsers support it"); toml::array clients_data = toml::find(clients, "data"); std::vector expected_name{"gamma", "delta"}; BOOST_CHECK(toml::get>(clients_data.at(0)) == expected_name); std::vector expected_number{1, 2}; BOOST_CHECK(toml::get>(clients_data.at(1)) == expected_number); std::vector expected_hosts{"alpha", "omega"}; BOOST_CHECK(toml::find>(clients, "hosts") == expected_hosts); BOOST_TEST(toml::find(clients, "hosts").comments().at(0) == " Line breaks are OK when inside arrays"); } std::vector products = toml::find>(data, "products"); { BOOST_TEST(toml::get(products.at(0).at("name")) == "Hammer"); BOOST_TEST(toml::get(products.at(0).at("sku")) == 738594937); BOOST_TEST(toml::get(products.at(1).at("name")) == "Nail"); BOOST_TEST(toml::get(products.at(1).at("sku")) == 284758393); BOOST_TEST(toml::get(products.at(1).at("color")) == "gray"); } } // --------------------------------------------------------------------------- // after here, the test codes generate the content of a file. BOOST_AUTO_TEST_CASE(test_file_with_BOM) { { const std::string table( "\xEF\xBB\xBF" // BOM "key = \"value\"\n" "[table]\n" "key = \"value\"\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_with_BOM.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "\xEF\xBB\xBF" // BOM "key = \"value\"\n" "[table]\n" "key = \"value\"\n" ); { std::ofstream ofs("tmp.toml"); ofs << table; } const auto data = toml::parse("tmp.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "\xEF\xBB\xBF" // BOM "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_with_BOM_CRLF.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "\xEF\xBB\xBF" // BOM "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" ); { // with text-mode, "\n" is converted to "\r\n" and the resulting // value will be "\r\r\n". To avoid the additional "\r", use binary // mode. std::ofstream ofs("tmp.toml", std::ios_base::binary); ofs.write(table.data(), static_cast(table.size())); } const auto data = toml::parse("tmp.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } } BOOST_AUTO_TEST_CASE(test_file_without_newline_at_the_end_of_file) { { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file_CRLF.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\" # comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\" # comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\" \t" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file_ws.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\" \t" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_file_without_newline_at_the_end_of_file_ws.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } } BOOST_AUTO_TEST_CASE(test_files_end_with_comment) { // comment w/o newline { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "# comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "# comment\n" "# one more comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // comment w/ newline { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "# comment\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "# comment\n" "# one more comment\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // CRLF version { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "# comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "# comment\r\n" "# one more comment" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "# comment\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "# comment\r\n" "# one more comment\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_comment.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } } BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines) { { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "\n" "\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // with whitespaces { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" " \n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" " \n" " \n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "\n" " \n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" " \n" "\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // with whitespaces but no newline { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" " " ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // without newline { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"\n" "a = 0" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // CRLF { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "\r\n" "\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } // with whitespaces { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" " \r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" "\r\n" " \r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" " \r\n" "\r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" " \r\n" " \r\n" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } { const std::string table( "key = \"value\"\r\n" "[table]\r\n" "key = \"value\"\r\n" " " ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_with_newline.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } } BOOST_AUTO_TEST_CASE(test_file_ends_without_lf) { { const std::string table( "key = \"value\"\n" "[table]\n" "key = \"value\"" ); std::istringstream iss(table); const auto data = toml::parse(iss, "test_files_end_without_lf.toml"); BOOST_TEST(toml::find(data, "key") == "value"); BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); } } BOOST_AUTO_TEST_CASE(test_parse_function_compiles) { using result_type = decltype(toml::parse("string literal")); (void) [](const char* that) -> result_type { return toml::parse(that); }; (void) [](char* that) -> result_type { return toml::parse(that); }; (void) [](const std::string& that) -> result_type { return toml::parse(that); }; (void) [](std::string& that) -> result_type { return toml::parse(that); }; (void) [](std::string&& that) -> result_type { return toml::parse(that); }; #ifdef TOML11_HAS_STD_FILESYSTEM (void) [](const std::filesystem::path& that) -> result_type { return toml::parse(that); }; (void) [](std::filesystem::path& that) -> result_type { return toml::parse(that); }; (void) [](std::filesystem::path&& that) -> result_type { return toml::parse(that); }; #endif (void) [](std::FILE* that) -> result_type { return toml::parse(that, "mandatory.toml"); }; } BOOST_AUTO_TEST_CASE(test_parse_nonexistent_file) { BOOST_CHECK_THROW(toml::parse("nonexistent.toml"), std::ios_base::failure); } toml11-3.8.1/tests/test_parse_floating.cpp000066400000000000000000000216531454646465200205710ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" #include using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_fractional) { TOML11_TEST_PARSE_EQUAL(parse_floating, "1.0", 1.0); TOML11_TEST_PARSE_EQUAL(parse_floating, "0.1", 0.1); TOML11_TEST_PARSE_EQUAL(parse_floating, "0.001", 0.001); TOML11_TEST_PARSE_EQUAL(parse_floating, "0.100", 0.1); TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.14", 3.14); TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.14", -3.14); TOML11_TEST_PARSE_EQUAL(parse_floating, "3.1415_9265_3589", 3.141592653589); TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.1415_9265_3589", 3.141592653589); TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.1415_9265_3589", -3.141592653589); TOML11_TEST_PARSE_EQUAL(parse_floating, "123_456.789", 123456.789); TOML11_TEST_PARSE_EQUAL(parse_floating, "+123_456.789", 123456.789); TOML11_TEST_PARSE_EQUAL(parse_floating, "-123_456.789", -123456.789); TOML11_TEST_PARSE_EQUAL(parse_floating, "+0.0", 0.0); TOML11_TEST_PARSE_EQUAL(parse_floating, "-0.0", -0.0); } BOOST_AUTO_TEST_CASE(test_fractional_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.0", value( 1.0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.1", value( 0.1)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.001", value( 0.001)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.100", value( 0.1)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.14", value( 3.14)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.14", value(-3.14)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "3.1415_9265_3589", value( 3.141592653589)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.1415_9265_3589", value( 3.141592653589)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.1415_9265_3589", value(-3.141592653589)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456.789", value( 123456.789)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+123_456.789", value( 123456.789)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-123_456.789", value(-123456.789)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0.0", value( 0.0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0.0", value(-0.0)); } BOOST_AUTO_TEST_CASE(test_exponential) { TOML11_TEST_PARSE_EQUAL(parse_floating, "1e10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1e+10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1e-10", 1e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e+10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e-10", 1e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e10", -1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e+10", -1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e-10", -1e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "123e-10", 123e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1E10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1E+10", 1e10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1E-10", 1e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "123E-10", 123e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-10", 123e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-1_0", 123e-10); TOML11_TEST_PARSE_EQUAL(parse_floating, "+0e0", 0.0); TOML11_TEST_PARSE_EQUAL(parse_floating, "-0e0", -0.0); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-01", 123e-1); TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-0_1", 123e-1); #endif } BOOST_AUTO_TEST_CASE(test_exponential_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e+10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e-10", value(1e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e+10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e-10", value(1e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e10", value(-1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e+10", value(-1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e-10", value(-1e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123e-10", value(123e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E+10", value(1e10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E-10", value(1e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123E-10", value(123e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-10", value(123e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-1_0", value(123e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0e0", value( 0.0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0e0", value(-0.0)); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-01", value(123e-1)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-0_1", value(123e-1)); #endif } BOOST_AUTO_TEST_CASE(test_fe) { TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e23", 6.02e23); TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e+23", 6.02e23); TOML11_TEST_PARSE_EQUAL(parse_floating, "1.112_650_06e-17", 1.11265006e-17); } BOOST_AUTO_TEST_CASE(test_fe_vaule) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e23", value(6.02e23)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e+23", value(6.02e23)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.112_650_06e-17", value(1.11265006e-17)); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES BOOST_TEST_MESSAGE("testing an unreleased toml feature: leading zeroes in float exponent part"); // toml-lang/toml master permits leading 0s in exp part (unreleased) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "3.141_5e-01", value(3.1415e-1)); #endif } BOOST_AUTO_TEST_CASE(test_inf) { { const std::string token("inf"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(r.unwrap().first > 0.0); } { const std::string token("+inf"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(r.unwrap().first > 0.0); } { const std::string token("-inf"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(r.unwrap().first < 0.0); } } BOOST_AUTO_TEST_CASE(test_nan) { { const std::string token("nan"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isnan(r.unwrap().first)); } { const std::string token("+nan"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isnan(r.unwrap().first)); } { const std::string token("-nan"); toml::detail::location loc("test", token); const auto r = parse_floating(loc); BOOST_CHECK(r.is_ok()); BOOST_CHECK(std::isnan(r.unwrap().first)); } } BOOST_AUTO_TEST_CASE(test_overflow) { std::istringstream float_overflow (std::string("float-overflow = 1.0e+1024")); BOOST_CHECK_THROW(toml::parse(float_overflow ), toml::syntax_error); // istringstream >> float does not set failbit in case of underflow. } toml11-3.8.1/tests/test_parse_inline_table.cpp000066400000000000000000000032641454646465200214110ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_inline_table) { TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{}", table()); { table t; t["foo"] = toml::value(42); t["bar"] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{foo = 42, bar = \"baz\"}", t); } { table t; table t_sub; t_sub["name"] = toml::value("pug"); t["type"] = toml::value(t_sub); TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{type.name = \"pug\"}", t); } } BOOST_AUTO_TEST_CASE(test_inline_table_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{}", value(table())); { table t; t["foo"] = toml::value(42); t["bar"] = toml::value("baz"); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{foo = 42, bar = \"baz\"}", value(t)); } { table t; table t_sub; t_sub["name"] = toml::value("pug"); t["type"] = toml::value(t_sub); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{type.name = \"pug\"}", value(t)); } } BOOST_AUTO_TEST_CASE(test_inline_table_immutability) { { std::istringstream stream(std::string( "a = {b = 1}\n" "a.c = 2\n")); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } { std::istringstream stream(std::string( "a = {b = {c = 1}}\n" "a.b.d = 2\n")); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); } } toml11-3.8.1/tests/test_parse_integer.cpp000066400000000000000000000134641454646465200204240ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_decimal) { TOML11_TEST_PARSE_EQUAL(parse_integer, "1234", 1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "+1234", 1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "-1234", -1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "0", 0); TOML11_TEST_PARSE_EQUAL(parse_integer, "1_2_3_4", 1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "+1_2_3_4", +1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "-1_2_3_4", -1234); TOML11_TEST_PARSE_EQUAL(parse_integer, "123_456_789", 123456789); } BOOST_AUTO_TEST_CASE(test_decimal_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1234", toml::value( 1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1234", toml::value( 1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1234", toml::value( -1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0", toml::value( 0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3_4", toml::value( 1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1_2_3_4", toml::value( +1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1_2_3_4", toml::value( -1234)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456_789", toml::value(123456789)); } BOOST_AUTO_TEST_CASE(test_hex) { TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADBEEF", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdeadbeef", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADbeef", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEAD_BEEF", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_beef", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_BEEF", 0xDEADBEEF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0xFF", 0xFF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0x00FF", 0xFF); TOML11_TEST_PARSE_EQUAL(parse_integer, "0x0000FF", 0xFF); } BOOST_AUTO_TEST_CASE(test_hex_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADBEEF", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdeadbeef", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADbeef", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEAD_BEEF", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_beef", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_BEEF", value(0xDEADBEEF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xFF", value(0xFF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x00FF", value(0xFF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x0000FF", value(0xFF)); } BOOST_AUTO_TEST_CASE(test_oct) { TOML11_TEST_PARSE_EQUAL(parse_integer, "0o777", 64*7+8*7+7); TOML11_TEST_PARSE_EQUAL(parse_integer, "0o7_7_7", 64*7+8*7+7); TOML11_TEST_PARSE_EQUAL(parse_integer, "0o007", 7); } BOOST_AUTO_TEST_CASE(test_oct_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o777", value(64*7+8*7+7)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o7_7_7", value(64*7+8*7+7)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o007", value(7)); } BOOST_AUTO_TEST_CASE(test_bin) { TOML11_TEST_PARSE_EQUAL(parse_integer, "0b10000", 16); TOML11_TEST_PARSE_EQUAL(parse_integer, "0b010000", 16); TOML11_TEST_PARSE_EQUAL(parse_integer, "0b01_00_00", 16); TOML11_TEST_PARSE_EQUAL(parse_integer, "0b111111", 63); } BOOST_AUTO_TEST_CASE(test_bin_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b10000", value(16)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b010000", value(16)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b01_00_00", value(16)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b111111", value(63)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000", // 1 0 0 0 // 0 C 8 4 value(0x0888888888888888)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111", // 1 0 0 0 // 0 C 8 4 value(0x7FFFFFFFFFFFFFFF)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b00000000_01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111", // 1 0 0 0 // 0 C 8 4 value(0x7FFFFFFFFFFFFFFF)); } BOOST_AUTO_TEST_CASE(test_integer_overflow) { std::istringstream dec_overflow(std::string("dec-overflow = 9223372036854775808")); std::istringstream hex_overflow(std::string("hex-overflow = 0x1_00000000_00000000")); std::istringstream oct_overflow(std::string("oct-overflow = 0o1_000_000_000_000_000_000_000")); // 64 56 48 40 32 24 16 8 std::istringstream bin_overflow(std::string("bin-overflow = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000")); BOOST_CHECK_THROW(toml::parse(dec_overflow), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(hex_overflow), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(oct_overflow), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(bin_overflow), toml::syntax_error); } toml11-3.8.1/tests/test_parse_key.cpp000066400000000000000000000037761454646465200175640ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_bare_key) { TOML11_TEST_PARSE_EQUAL(parse_key, "barekey", std::vector(1, "barekey")); TOML11_TEST_PARSE_EQUAL(parse_key, "bare-key", std::vector(1, "bare-key")); TOML11_TEST_PARSE_EQUAL(parse_key, "bare_key", std::vector(1, "bare_key")); TOML11_TEST_PARSE_EQUAL(parse_key, "1234", std::vector(1, "1234")); } BOOST_AUTO_TEST_CASE(test_quoted_key) { TOML11_TEST_PARSE_EQUAL(parse_key, "\"127.0.0.1\"", std::vector(1, "127.0.0.1" )); TOML11_TEST_PARSE_EQUAL(parse_key, "\"character encoding\"", std::vector(1, "character encoding")); #if defined(_MSC_VER) || defined(__INTEL_COMPILER) TOML11_TEST_PARSE_EQUAL(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector(1, "\xCA\x8E\xC7\x9D\xCA\x9E")); #else TOML11_TEST_PARSE_EQUAL(parse_key, "\"ʎǝʞ\"", std::vector(1, "ʎǝʞ" )); #endif TOML11_TEST_PARSE_EQUAL(parse_key, "'key2'", std::vector(1, "key2" )); TOML11_TEST_PARSE_EQUAL(parse_key, "'quoted \"value\"'", std::vector(1, "quoted \"value\"" )); } BOOST_AUTO_TEST_CASE(test_dotted_key) { { std::vector keys(2); keys[0] = "physical"; keys[1] = "color"; TOML11_TEST_PARSE_EQUAL(parse_key, "physical.color", keys); } { std::vector keys(2); keys[0] = "physical"; keys[1] = "shape"; TOML11_TEST_PARSE_EQUAL(parse_key, "physical.shape", keys); } { std::vector keys(4); keys[0] = "x"; keys[1] = "y"; keys[2] = "z"; keys[3] = "w"; TOML11_TEST_PARSE_EQUAL(parse_key, "x.y.z.w", keys); } { std::vector keys(2); keys[0] = "site"; keys[1] = "google.com"; TOML11_TEST_PARSE_EQUAL(parse_key, "site.\"google.com\"", keys); } } toml11-3.8.1/tests/test_parse_string.cpp000066400000000000000000000266371454646465200203030ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_string) { TOML11_TEST_PARSE_EQUAL(parse_string, "\"The quick brown fox jumps over the lazy dog\"", string("The quick brown fox jumps over the lazy dog", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\'The quick brown fox jumps over the lazy dog\'", string("The quick brown fox jumps over the lazy dog", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", string("The quick brown fox jumps over the lazy dog", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "'''The quick brown fox \njumps over the lazy dog'''", string("The quick brown fox \njumps over the lazy dog", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_string_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"The quick brown fox jumps over the lazy dog\"", toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\'The quick brown fox jumps over the lazy dog\'", toml::value("The quick brown fox jumps over the lazy dog", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'''The quick brown fox \njumps over the lazy dog'''", toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_basic_string) { TOML11_TEST_PARSE_EQUAL(parse_string, "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", string("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"192.168.1.1\"", string("192.168.1.1", string_t::basic)); #if defined(_MSC_VER) || defined(__INTEL_COMPILER) TOML11_TEST_PARSE_EQUAL(parse_string, "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", string("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); #else TOML11_TEST_PARSE_EQUAL(parse_string, "\"中国\"", string("中国", string_t::basic)); #endif TOML11_TEST_PARSE_EQUAL(parse_string, "\"You'll hate me after this - #\"", string("You'll hate me after this - #", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\" And when \\\"'s are in the along with # \\\"\"", string(" And when \"'s are in the along with # \"", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"Here are fifteen apostrophes: '''''''''''''''\"", string("Here are fifteen apostrophes: '''''''''''''''", string_t::basic)); } BOOST_AUTO_TEST_CASE(test_basic_string_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"192.168.1.1\"", value("192.168.1.1", string_t::basic)); #if defined(_MSC_VER) || defined(__INTEL_COMPILER) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); #else TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"中国\"", value("中国", string_t::basic)); #endif TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"You'll hate me after this - #\"", value("You'll hate me after this - #", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\" And when \\\"'s are in the along with # \\\"\"", value(" And when \"'s are in the along with # \"", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"Here are fifteen apostrophes: '''''''''''''''\"", value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic)); } BOOST_AUTO_TEST_CASE(test_ml_basic_string) { TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", string("The quick brown fox jumps over the lazy dog.", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", string("The quick brown fox jumps over the lazy dog.", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", string("Here are two quotation marks: \"\". Simple enough.", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", string("Here are three quotation marks: \"\"\".", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", string("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic)); } BOOST_AUTO_TEST_CASE(test_ml_basic_string_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", value("The quick brown fox jumps over the lazy dog.", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", value("The quick brown fox jumps over the lazy dog.", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", value("Here are two quotation marks: \"\". Simple enough.", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", value("Here are three quotation marks: \"\"\".", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", value("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic)); } BOOST_AUTO_TEST_CASE(test_literal_string) { TOML11_TEST_PARSE_EQUAL(parse_string, "'C:\\Users\\nodejs\\templates'", string("C:\\Users\\nodejs\\templates", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "'\\\\ServerX\\admin$\\system32\\'", string("\\\\ServerX\\admin$\\system32\\", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "'Tom \"Dubs\" Preston-Werner'", string("Tom \"Dubs\" Preston-Werner", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "'<\\i\\c*\\s*>'", string("<\\i\\c*\\s*>", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_literal_string_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'C:\\Users\\nodejs\\templates'", value("C:\\Users\\nodejs\\templates", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'\\\\ServerX\\admin$\\system32\\'", value("\\\\ServerX\\admin$\\system32\\", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'Tom \"Dubs\" Preston-Werner'", value("Tom \"Dubs\" Preston-Werner", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'<\\i\\c*\\s*>'", value("<\\i\\c*\\s*>", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_ml_literal_string) { TOML11_TEST_PARSE_EQUAL(parse_string, "'''I [dw]on't need \\d{2} apples'''", string("I [dw]on't need \\d{2} apples", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "''''That's still pointless', she said.'''", string("'That's still pointless', she said.", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal)); TOML11_TEST_PARSE_EQUAL(parse_string, "''''This,' she said, 'is just a pointless statement.''''", string("'This,' she said, 'is just a pointless statement.'", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_ml_literal_string_value) { TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'''I [dw]on't need \\d{2} apples'''", value("I [dw]on't need \\d{2} apples", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "''''That's still pointless', she said.'''", value("'That's still pointless', she said.", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "''''This,' she said, 'is just a pointless statement.''''", value("'This,' she said, 'is just a pointless statement.'", string_t::literal)); } BOOST_AUTO_TEST_CASE(test_simple_excape_sequences) { TOML11_TEST_PARSE_EQUAL(parse_string, R"("\"\\\b\f\n\r\t")", string("\"\\\b\f\n\r\t", string_t::basic)); #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES TOML11_TEST_PARSE_EQUAL(parse_string, R"("\e")", string("\x1b", string_t::basic)); #endif } BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence) { #if defined(_MSC_VER) || defined(__INTEL_COMPILER) TOML11_TEST_PARSE_EQUAL(parse_string, "\"\\u03B1\\u03B2\\u03B3\"", string("\xCE\xB1\xCE\xB2\xCE\xB3", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\\U0001D7AA\"", string("\xF0\x9D\x9E\xAA", string_t::basic)); #else TOML11_TEST_PARSE_EQUAL(parse_string, "\"\\u03B1\\u03B2\\u03B3\"", string("αβγ", string_t::basic)); TOML11_TEST_PARSE_EQUAL(parse_string, "\"\\U0001D7AA\"", string("𝞪", string_t::basic)); #endif } toml11-3.8.1/tests/test_parse_table.cpp000066400000000000000000000023141454646465200200460ustar00rootroot00000000000000#include #include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_normal_table) { std::string table( "key1 = \"value\"\n" "key2 = 42\n" "key3 = 3.14\n" ); location loc("test", table); const auto result = toml::detail::parse_ml_table(loc); BOOST_TEST(result.is_ok()); const auto data = result.unwrap(); BOOST_TEST(toml::get(data.at("key1")) == "value"); BOOST_TEST(toml::get(data.at("key2")) == 42); BOOST_TEST(toml::get(data.at("key3")) == 3.14); } BOOST_AUTO_TEST_CASE(test_nested_table) { std::string table( "a.b = \"value\"\n" "a.c.d = 42\n" ); location loc("test", table); const auto result = toml::detail::parse_ml_table(loc); BOOST_TEST(result.is_ok()); const auto data = result.unwrap(); const auto a = toml::get(data.at("a")); const auto c = toml::get(a.at("c")); BOOST_TEST(toml::get(a.at("b")) == "value"); BOOST_TEST(toml::get(c.at("d")) == 42); } toml11-3.8.1/tests/test_parse_table_key.cpp000066400000000000000000000112161454646465200207170ustar00rootroot00000000000000#include #include "unit_test.hpp" #include "test_parse_aux.hpp" using namespace toml; using namespace detail; BOOST_AUTO_TEST_CASE(test_table_bare_key) { TOML11_TEST_PARSE_EQUAL(parse_table_key, "[barekey]", std::vector(1, "barekey")); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare-key]", std::vector(1, "bare-key")); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare_key]", std::vector(1, "bare_key")); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[1234]", std::vector(1, "1234")); } BOOST_AUTO_TEST_CASE(test_table_quoted_key) { TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"127.0.0.1\"]", std::vector(1, "127.0.0.1" )); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"character encoding\"]", std::vector(1, "character encoding")); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"ʎǝʞ\"]", std::vector(1, "ʎǝʞ" )); TOML11_TEST_PARSE_EQUAL(parse_table_key, "['key2']", std::vector(1, "key2" )); TOML11_TEST_PARSE_EQUAL(parse_table_key, "['quoted \"value\"']", std::vector(1, "quoted \"value\"" )); } BOOST_AUTO_TEST_CASE(test_table_dotted_key) { { std::vector keys(2); keys[0] = "physical"; keys[1] = "color"; TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.color]", keys); } { std::vector keys(2); keys[0] = "physical"; keys[1] = "shape"; TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.shape]", keys); } { std::vector keys(4); keys[0] = "x"; keys[1] = "y"; keys[2] = "z"; keys[3] = "w"; TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x.y.z.w]", keys); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x . y . z . w]", keys); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x. y .z. w]", keys); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x .y. z .w]", keys); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x. y .z . w ]", keys); TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x . y . z . w ]", keys); } { std::vector keys(2); keys[0] = "site"; keys[1] = "google.com"; TOML11_TEST_PARSE_EQUAL(parse_table_key, "[site.\"google.com\"]", keys); } } BOOST_AUTO_TEST_CASE(test_array_of_table_bare_key) { TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[barekey]]", std::vector(1, "barekey")); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare-key]]", std::vector(1, "bare-key")); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare_key]]", std::vector(1, "bare_key")); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[1234]]", std::vector(1, "1234")); } BOOST_AUTO_TEST_CASE(test_array_of_table_quoted_key) { TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"127.0.0.1\"]]", std::vector(1, "127.0.0.1" )); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"character encoding\"]]", std::vector(1, "character encoding")); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"ʎǝʞ\"]]", std::vector(1, "ʎǝʞ" )); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['key2']]", std::vector(1, "key2" )); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['quoted \"value\"']]", std::vector(1, "quoted \"value\"" )); } BOOST_AUTO_TEST_CASE(test_array_of_table_dotted_key) { { std::vector keys(2); keys[0] = "physical"; keys[1] = "color"; TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.color]]", keys); } { std::vector keys(2); keys[0] = "physical"; keys[1] = "shape"; TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.shape]]", keys); } { std::vector keys(4); keys[0] = "x"; keys[1] = "y"; keys[2] = "z"; keys[3] = "w"; TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x.y.z.w]]", keys); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x . y . z . w]]", keys); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x. y .z. w]]", keys); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x .y. z .w]]", keys); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x. y .z . w ]]", keys); TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x . y . z . w ]]", keys); } { std::vector keys(2); keys[0] = "site"; keys[1] = "google.com"; TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[site.\"google.com\"]]", keys); } } toml11-3.8.1/tests/test_parse_unicode.cpp000066400000000000000000000055261454646465200204150ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include BOOST_AUTO_TEST_CASE(test_hard_example_unicode) { const auto data = toml::parse(testinput("hard_example_unicode.toml")); const auto the = toml::find(data, "the"); BOOST_TEST(toml::get(the.at("test_string")) == std::string("\xC3\x9D\xC3\xB4\xC3\xBA\x27\xE2\x84\x93\xE2\x84\x93\x20\xCE\xBB\xC3\xA1\xC6\xAD\xC3\xA8\x20\xE2\x82\xA5\xC3\xA8\x20\xC3\xA1\xC6\x92\xC6\xAD\xC3\xA8\xC5\x99\x20\xC6\xAD\xCE\xBB\xC3\xAF\xC6\xA8\x20\x2D\x20\x23")); const auto hard = toml::get(the.at("hard")); const std::vector expected_the_hard_test_array{"] ", " # "}; BOOST_CHECK(toml::get>(hard.at("test_array")) == expected_the_hard_test_array); const std::vector expected_the_hard_test_array2{ std::string("\x54\xC3\xA8\xC6\xA8\xC6\xAD\x20\x23\x31\x31\x20\x5D\xC6\xA5\xC5\x99\xC3\xB4\xC6\xB2\xC3\xA8\xCE\xB4\x20\xC6\xAD\xCE\xBB\xC3\xA1\xC6\xAD"), std::string("\xC3\x89\xD0\xB6\xC6\xA5\xC3\xA8\xC5\x99\xC3\xAF\xE2\x82\xA5\xC3\xA8\xC3\xB1\xC6\xAD\x20\x23\x39\x20\xCF\x89\xC3\xA1\xC6\xA8\x20\xC3\xA1\x20\xC6\xA8\xC3\xBA\xC3\xA7\xC3\xA7\xC3\xA8\xC6\xA8\xC6\xA8") }; BOOST_CHECK(toml::get>(hard.at("test_array2")) == expected_the_hard_test_array2); BOOST_TEST(toml::get(hard.at("another_test_string")) == std::string("\xC2\xA7\xC3\xA1\xE2\x82\xA5\xC3\xA8\x20\xC6\xAD\xCE\xBB\xC3\xAF\xC3\xB1\xCF\xB1\x2C\x20\xCE\xB2\xC3\xBA\xC6\xAD\x20\xCF\x89\xC3\xAF\xC6\xAD\xCE\xBB\x20\xC3\xA1\x20\xC6\xA8\xC6\xAD\xC5\x99\xC3\xAF\xC3\xB1\xCF\xB1\x20\x23")); BOOST_TEST(toml::get(hard.at("harder_test_string")) == std::string("\x20\xC3\x82\xC3\xB1\xCE\xB4\x20\xCF\x89\xCE\xBB\xC3\xA8\xC3\xB1\x20\x22\x27\xC6\xA8\x20\xC3\xA1\xC5\x99\xC3\xA8\x20\xC3\xAF\xC3\xB1\x20\xC6\xAD\xCE\xBB\xC3\xA8\x20\xC6\xA8\xC6\xAD\xC5\x99\xC3\xAF\xC3\xB1\xCF\xB1\x2C\x20\xC3\xA1\xE2\x84\x93\xC3\xB4\xC3\xB1\xCF\xB1\x20\xCF\x89\xC3\xAF\xC6\xAD\xCE\xBB\x20\x23\x20\x22")); // const auto bit = toml::get(hard.at(std::string("\xCE\xB2\xC3\xAF\xC6\xAD\x23"))); BOOST_TEST(toml::get(bit.at(std::string("\xCF\x89\xCE\xBB\xC3\xA1\xC6\xAD\x3F"))) == std::string("\xC3\x9D\xC3\xB4\xC3\xBA\x20\xCE\xB4\xC3\xB4\xC3\xB1\x27\xC6\xAD\x20\xC6\xAD\xCE\xBB\xC3\xAF\xC3\xB1\xC6\x99\x20\xC6\xA8\xC3\xB4\xE2\x82\xA5\xC3\xA8\x20\xC3\xBA\xC6\xA8\xC3\xA8\xC5\x99\x20\xCF\x89\xC3\xB4\xC3\xB1\x27\xC6\xAD\x20\xCE\xB4\xC3\xB4\x20\xC6\xAD\xCE\xBB\xC3\xA1\xC6\xAD\x3F")); const std::vector expected_multi_line_array{"]"}; BOOST_CHECK(toml::get>(bit.at("multi_line_array")) == expected_multi_line_array); } toml11-3.8.1/tests/test_result.cpp000066400000000000000000000335011454646465200171050ustar00rootroot00000000000000#include #include "unit_test.hpp" #include BOOST_AUTO_TEST_CASE(test_construct) { { auto s = toml::ok(42); toml::result result(s); BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { const auto s = toml::ok(42); toml::result result(s); BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { toml::result result(toml::ok(42)); BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { auto f = toml::err("foobar"); toml::result result(f); BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "foobar"); } { const auto f = toml::err("foobar"); toml::result result(f); BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "foobar"); } { toml::result result(toml::err("foobar")); BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "foobar"); } } BOOST_AUTO_TEST_CASE(test_assignment) { { toml::result result(toml::err("foobar")); result = toml::ok(42); BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); auto s = toml::ok(42); result = s; BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); const auto s = toml::ok(42); result = s; BOOST_TEST(!!result); BOOST_TEST(result.is_ok()); BOOST_TEST(!result.is_err()); BOOST_TEST(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); result = toml::err("hoge"); BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "hoge"); } { toml::result result(toml::err("foobar")); auto f = toml::err("hoge"); result = f; BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "hoge"); } { toml::result result(toml::err("foobar")); const auto f = toml::err("hoge"); result = f; BOOST_TEST(!result); BOOST_TEST(!result.is_ok()); BOOST_TEST(result.is_err()); BOOST_TEST(result.unwrap_err() == "hoge"); } } BOOST_AUTO_TEST_CASE(test_map) { { const toml::result result(toml::ok(42)); const auto mapped = result.map( [](const int i) -> int { return i * 2; }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42 * 2); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).map( [](std::unique_ptr i) -> int { return *i; }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42); } { const toml::result result(toml::err("hoge")); const auto mapped = result.map( [](const int i) -> int { return i * 2; }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hoge"); } { toml::result, std::string> result(toml::err("hoge")); const auto mapped = std::move(result).map( [](std::unique_ptr i) -> int { return *i; }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hoge"); } } BOOST_AUTO_TEST_CASE(test_map_err) { { const toml::result result(toml::ok(42)); const auto mapped = result.map_err( [](const std::string s) -> std::string { return s + s; }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).map_err( [](const std::string s) -> std::string { return s + s; }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(*(mapped.unwrap()) == 42); } { const toml::result result(toml::err("hoge")); const auto mapped = result.map_err( [](const std::string s) -> std::string { return s + s; }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hogehoge"); } { toml::result> result(toml::err(std::unique_ptr(new std::string("hoge")))); const auto mapped = std::move(result).map_err( [](std::unique_ptr p) -> std::string { return *p; }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hoge"); } } BOOST_AUTO_TEST_CASE(test_map_or_else) { { const toml::result result(toml::ok(42)); const auto mapped = result.map_or_else( [](const int i) -> int { return i * 2; }, 54); BOOST_TEST(mapped == 42 * 2); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).map_or_else( [](std::unique_ptr i) -> int { return *i; }, 54); BOOST_TEST(mapped == 42); } { const toml::result result(toml::err("hoge")); const auto mapped = result.map_or_else( [](const int i) -> int { return i * 2; }, 54); BOOST_TEST(mapped == 54); } { toml::result, std::string> result(toml::err("hoge")); const auto mapped = std::move(result).map_or_else( [](std::unique_ptr i) -> int { return *i; }, 54); BOOST_TEST(mapped == 54); } } BOOST_AUTO_TEST_CASE(test_map_err_or_else) { { const toml::result result(toml::ok(42)); const auto mapped = result.map_err_or_else( [](const std::string i) -> std::string { return i + i; }, "foobar"); BOOST_TEST(mapped == "foobar"); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).map_err_or_else( [](const std::string i) -> std::string { return i + i; }, "foobar"); BOOST_TEST(mapped == "foobar"); } { const toml::result result(toml::err("hoge")); const auto mapped = result.map_err_or_else( [](const std::string i) -> std::string { return i + i; }, "foobar"); BOOST_TEST(mapped == "hogehoge"); } { toml::result, std::string> result(toml::err("hoge")); const auto mapped = result.map_err_or_else( [](const std::string i) -> std::string { return i + i; }, "foobar"); BOOST_TEST(mapped == "hogehoge"); } } BOOST_AUTO_TEST_CASE(test_and_then) { { const toml::result result(toml::ok(42)); const auto mapped = result.and_then( [](const int i) -> toml::result { return toml::ok(i * 2); }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42 * 2); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).and_then( [](std::unique_ptr i) -> toml::result { return toml::ok(*i); }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42); } { const toml::result result(toml::err("hoge")); const auto mapped = result.and_then( [](const int i) -> toml::result { return toml::ok(i * 2); }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hoge"); } { toml::result, std::string> result(toml::err("hoge")); const auto mapped = std::move(result).and_then( [](std::unique_ptr i) -> toml::result { return toml::ok(*i); }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hoge"); } } BOOST_AUTO_TEST_CASE(test_or_else) { { const toml::result result(toml::ok(42)); const auto mapped = result.or_else( [](const std::string& s) -> toml::result { return toml::err(s + s); }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(mapped.unwrap() == 42); } { toml::result, std::string> result(toml::ok(std::unique_ptr(new int(42)))); const auto mapped = std::move(result).or_else( [](const std::string& s) -> toml::result, std::string> { return toml::err(s + s); }); BOOST_TEST(!!mapped); BOOST_TEST(mapped.is_ok()); BOOST_TEST(!mapped.is_err()); BOOST_TEST(*mapped.unwrap() == 42); } { const toml::result result(toml::err("hoge")); const auto mapped = result.or_else( [](const std::string& s) -> toml::result { return toml::err(s + s); }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hogehoge"); } { toml::result, std::string> result(toml::err("hoge")); const auto mapped = std::move(result).or_else( [](const std::string& s) -> toml::result, std::string> { return toml::err(s + s); }); BOOST_TEST(!mapped); BOOST_TEST(!mapped.is_ok()); BOOST_TEST(mapped.is_err()); BOOST_TEST(mapped.unwrap_err() == "hogehoge"); } } BOOST_AUTO_TEST_CASE(test_and_or_other) { { const toml::result r1(toml::ok(42)); const toml::result r2(toml::err("foo")); BOOST_TEST(r1 == r1.or_other(r2)); BOOST_TEST(r2 == r1.and_other(r2)); BOOST_TEST(42 == r1.or_other(r2).unwrap()); BOOST_TEST("foo" == r1.and_other(r2).unwrap_err()); } { auto r1_gen = []() -> toml::result { return toml::ok(42); }; auto r2_gen = []() -> toml::result { return toml::err("foo"); }; const auto r3 = r1_gen(); const auto r4 = r2_gen(); BOOST_TEST(r3 == r1_gen().or_other (r2_gen())); BOOST_TEST(r4 == r1_gen().and_other(r2_gen())); BOOST_TEST(42 == r1_gen().or_other (r2_gen()).unwrap()); BOOST_TEST("foo" == r1_gen().and_other(r2_gen()).unwrap_err()); } } toml11-3.8.1/tests/test_serialize_file.cpp000066400000000000000000000304701454646465200205570ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include template class Table, template class Array> bool has_comment_inside(const toml::basic_value& v) { if(!v.comments().empty()) { return false; } // v itself does not have a comment. if(v.is_array()) { for(const auto& x : v.as_array()) { if(has_comment_inside(x)) { return false; } } } if(v.is_table()) { for(const auto& x : v.as_table()) { if(has_comment_inside(x.second)) { return false; } } } return true; } BOOST_AUTO_TEST_CASE(test_example) { const auto data = toml::parse(testinput("example.toml")); { std::ofstream ofs("tmp1.toml"); ofs << std::setw(80) << data; } auto serialized = toml::parse("tmp1.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_example_map_dq) { const auto data = toml::parse( testinput("example.toml")); { std::ofstream ofs("tmp1_map_dq.toml"); ofs << std::setw(80) << data; } auto serialized = toml::parse( "tmp1_map_dq.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_example_with_comment) { const auto data = toml::parse(testinput("example.toml")); { std::ofstream ofs("tmp1_com.toml"); ofs << std::setw(80) << data; } auto serialized = toml::parse("tmp1_com.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } BOOST_TEST(data == serialized); { std::ofstream ofs("tmp1_com1.toml"); ofs << std::setw(80) << serialized; } } BOOST_AUTO_TEST_CASE(test_example_with_comment_nocomment) { { const auto data = toml::parse(testinput("example.toml")); { std::ofstream ofs("tmp1_com_nocomment.toml"); ofs << std::setw(80) << toml::nocomment << data; } const auto serialized = toml::parse("tmp1_com_nocomment.toml"); // check no comment exist BOOST_TEST(!has_comment_inside(serialized)); } { const auto data_nocomment = toml::parse(testinput("example.toml")); auto serialized = toml::parse("tmp1_com_nocomment.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } // check collectly serialized BOOST_TEST(data_nocomment == serialized); } } BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq) { const auto data = toml::parse( testinput("example.toml")); { std::ofstream ofs("tmp1_com_map_dq.toml"); ofs << std::setw(80) << data; } auto serialized = toml::parse( "tmp1_com_map_dq.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } BOOST_TEST(data == serialized); { std::ofstream ofs("tmp1_com1_map_dq.toml"); ofs << std::setw(80) << serialized; } } BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq_nocomment) { { const auto data = toml::parse(testinput("example.toml")); { std::ofstream ofs("tmp1_com_map_dq_nocomment.toml"); ofs << std::setw(80) << toml::nocomment << data; } const auto serialized = toml::parse("tmp1_com_map_dq_nocomment.toml"); BOOST_TEST(!has_comment_inside(serialized)); } { const auto data_nocomment = toml::parse(testinput("example.toml")); auto serialized = toml::parse("tmp1_com_map_dq_nocomment.toml"); { auto& owner = toml::find(serialized, "owner"); auto& bio = toml::find(owner, "bio"); const auto CR = std::find(bio.begin(), bio.end(), '\r'); if(CR != bio.end()) { bio.erase(CR); } } BOOST_TEST(data_nocomment == serialized); } } BOOST_AUTO_TEST_CASE(test_fruit) { const auto data = toml::parse(testinput("fruit.toml")); { std::ofstream ofs("tmp2.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse("tmp2.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_fruit_map_dq) { const auto data = toml::parse( testinput("fruit.toml")); { std::ofstream ofs("tmp2.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse( "tmp2.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_fruit_with_comments) { const auto data = toml::parse(testinput("fruit.toml")); { std::ofstream ofs("tmp2_com.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse("tmp2_com.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_fruit_with_comments_map_dq) { const auto data = toml::parse( testinput("fruit.toml")); { std::ofstream ofs("tmp2_com.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse("tmp2_com.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_hard_example) { const auto data = toml::parse(testinput("hard_example.toml")); { std::ofstream ofs("tmp3.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse("tmp3.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_hard_example_map_dq) { const auto data = toml::parse( testinput("hard_example.toml")); { std::ofstream ofs("tmp3.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse( "tmp3.toml"); BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_hard_example_with_comment) { const auto data = toml::parse( testinput("hard_example.toml")); { std::ofstream ofs("tmp3_com.toml"); ofs << std::setw(80) << data; } const auto serialized = toml::parse( "tmp3_com.toml"); { std::ofstream ofs("tmp3_com1.toml"); ofs << std::setw(80) << serialized; } BOOST_TEST(data == serialized); } BOOST_AUTO_TEST_CASE(test_format_key) { { const toml::key key("normal_bare-key"); BOOST_TEST("normal_bare-key" == toml::format_key(key)); } { const toml::key key("key.include.dots"); BOOST_TEST("\"key.include.dots\"" == toml::format_key(key)); } { const toml::key key("key-include-unicode-\xE3\x81\x82"); BOOST_TEST("\"key-include-unicode-\xE3\x81\x82\"" == toml::format_key(key)); } { const toml::key key("special-chars-\\-\"-\b-\f-\r-\n-\t"); BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key)); } } // In toml11, an implicitly-defined value does not have any comments. // So, in the following file, // ```toml // # comment // [[array-of-tables]] // foo = "bar" // ``` // The array named "array-of-tables" does not have the comment, but the first // element of the array has. That means that, the above file is equivalent to // the following. // ```toml // array-of-tables = [ // # comment // {foo = "bar"}, // ] // ``` // If the array itself has a comment (value_has_comment_ == true), we should try // to make it inline. // ```toml // # comment about array // array-of-tables = [ // # comment about table element // {foo = "bar"} // ] // ``` // If it is formatted as a multiline table, the two comments becomes // indistinguishable. // ```toml // # comment about array // # comment about table element // [[array-of-tables]] // foo = "bar" // ``` // So we need to try to make it inline, and it force-inlines regardless // of the line width limit. // It may fail if the element of a table has comment. In that case, // the array-of-tables will be formatted as a multiline table. BOOST_AUTO_TEST_CASE(test_distinguish_comment) { const std::string str = R"(# comment about array itself array_of_table = [ # comment about the first element (table) {key = "value"}, ])"; std::istringstream iss(str); const auto data = toml::parse(iss); const auto serialized = toml::format(data, /*width = */ 0); std::istringstream reparse(serialized); const auto parsed = toml::parse(reparse); BOOST_TEST(parsed.at("array_of_table").comments().size() == 1u); BOOST_TEST(parsed.at("array_of_table").comments().front() == " comment about array itself"); BOOST_TEST(parsed.at("array_of_table").at(0).comments().size() == 1u); BOOST_TEST(parsed.at("array_of_table").at(0).comments().front() == " comment about the first element (table)"); } BOOST_AUTO_TEST_CASE(test_serialize_under_locale) { // avoid null init (setlocale returns null when it failed) std::string setloc(std::setlocale(LC_ALL, nullptr)); // fr_FR is a one of locales that uses `,` as a decimal separator. if(const char* try_hyphen = std::setlocale(LC_ALL, "fr_FR.UTF-8")) { setloc = std::string(try_hyphen); } else if(const char* try_nohyphen = std::setlocale(LC_ALL, "fr_FR.utf8")) { setloc = std::string(try_nohyphen); } // In some envs, fr_FR locale has not been installed. Tests must work even in such a case. // else // { // BOOST_TEST(false); // } BOOST_TEST_MESSAGE("current locale at the beginning of the test = " << setloc); const std::string str = R"( pi = 3.14159 large_int = 1234567890 )"; std::istringstream iss(str); const auto ref = toml::parse(iss); const auto serialized_str = toml::format(ref, /*width = */ 80); BOOST_TEST_MESSAGE("serialized = " << serialized_str); std::istringstream serialized_iss(serialized_str); const auto serialized_ref = toml::parse(serialized_iss); BOOST_TEST(serialized_ref.at("pi").as_floating() == ref.at("pi").as_floating()); BOOST_TEST(serialized_ref.at("large_int").as_integer() == ref.at("large_int").as_integer()); const std::string endloc(std::setlocale(LC_ALL, nullptr)); BOOST_TEST_MESSAGE("current locale at the end of the test = " << endloc); // check if serializer change global locale BOOST_TEST(setloc == endloc); } toml11-3.8.1/tests/test_string.cpp000066400000000000000000000076071454646465200171050ustar00rootroot00000000000000#include #include "unit_test.hpp" BOOST_AUTO_TEST_CASE(test_basic_string) { { const toml::string str("basic string"); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "\"basic string\""); } { const std::string s1 ("basic string"); const toml::string str(s1); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "\"basic string\""); } { const toml::string str("basic string", toml::string_t::basic); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "\"basic string\""); } { const std::string s1 ("basic string"); const toml::string str(s1, toml::string_t::basic); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "\"basic string\""); } } BOOST_AUTO_TEST_CASE(test_basic_ml_string) { { const toml::string str("basic\nstring"); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\""; BOOST_TEST(oss1.str() == oss2.str()); } { const std::string s1 ("basic\nstring"); const toml::string str(s1); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\""; BOOST_TEST(oss1.str() == oss2.str()); } { const toml::string str("basic\nstring", toml::string_t::basic); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\""; BOOST_TEST(oss1.str() == oss2.str()); } { const std::string s1 ("basic\nstring"); const toml::string str(s1, toml::string_t::basic); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "\"\"\"\nbasic\nstring\\\n\"\"\""; BOOST_TEST(oss1.str() == oss2.str()); } } BOOST_AUTO_TEST_CASE(test_literal_string) { { const toml::string str("literal string", toml::string_t::literal); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "'literal string'"); } { const std::string s1 ("literal string"); const toml::string str(s1, toml::string_t::literal); std::ostringstream oss; oss << str; BOOST_TEST(oss.str() == "'literal string'"); } } BOOST_AUTO_TEST_CASE(test_literal_ml_string) { { const toml::string str("literal\nstring", toml::string_t::literal); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "'''\nliteral\nstring'''"; BOOST_TEST(oss1.str() == oss2.str()); } { const std::string s1 ("literal\nstring"); const toml::string str(s1, toml::string_t::literal); std::ostringstream oss1; oss1 << str; std::ostringstream oss2; oss2 << "'''\nliteral\nstring'''"; BOOST_TEST(oss1.str() == oss2.str()); } } BOOST_AUTO_TEST_CASE(test_string_add_assign) { // string literal { toml::string str("foo"); str += "bar"; BOOST_TEST(str.str == "foobar"); } // std::string { toml::string str("foo"); std::string str2("bar"); str += str2; BOOST_TEST(str.str == "foobar"); } // toml::string { toml::string str("foo"); toml::string str2("bar"); str += str2; BOOST_TEST(str.str == "foobar"); } #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L // std::string_view { toml::string str("foo"); str += std::string_view("bar"); BOOST_TEST(str == "foobar"); } #endif // std::string += toml::string { std::string str("foo"); toml::string str2("bar"); str += str2; BOOST_TEST(str == "foobar"); } } toml11-3.8.1/tests/test_traits.cpp000066400000000000000000000066751454646465200171110ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #include #include #include #include #include #include #include struct dummy_type{}; template struct dummy_container { typedef T value_type; typedef value_type* pointer; typedef value_type& reference; typedef value_type const* const_pointer; typedef value_type const& const_reference; typedef pointer iterator; typedef const_pointer const_iterator; }; typedef std::array std_array_type; typedef std::map std_map_type; typedef std::unordered_map std_unordered_map_type; BOOST_AUTO_TEST_CASE(test_has_xxx) { BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_iterator::value); BOOST_TEST(toml::detail::has_iterator::value); BOOST_TEST(toml::detail::has_iterator::value); BOOST_TEST(toml::detail::has_iterator>::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_value_type::value); BOOST_TEST(toml::detail::has_value_type::value); BOOST_TEST(toml::detail::has_value_type>::value); BOOST_TEST(toml::detail::has_key_type::value); BOOST_TEST(toml::detail::has_key_type::value); BOOST_TEST(toml::detail::has_mapped_type::value); BOOST_TEST(toml::detail::has_mapped_type::value); } BOOST_AUTO_TEST_CASE(test_is_xxx) { BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(toml::detail::is_container>::value); BOOST_TEST(!toml::detail::is_container::value); BOOST_TEST(!toml::detail::is_container::value); BOOST_TEST(toml::detail::is_map::value); BOOST_TEST(toml::detail::is_map::value); } toml11-3.8.1/tests/test_utility.cpp000066400000000000000000000024751454646465200173000ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include BOOST_AUTO_TEST_CASE(test_try_reserve) { { // since BOOST_TEST is a macro, it cannot handle commas correctly. // When toml::detail::has_reserve_method>::value // is passed to a macro, C preprocessor considers // toml::detail::has_reserve_method>::value as the second argument. We need an alias to avoid // this problem. using reservable_type = std::vector ; using nonreservable_type = std::array; BOOST_TEST( toml::detail::has_reserve_method::value); BOOST_TEST(!toml::detail::has_reserve_method::value); } { std::vector v; toml::try_reserve(v, 100); BOOST_TEST(v.capacity() == 100u); } } BOOST_AUTO_TEST_CASE(test_concat_to_string) { const std::string cat = toml::concat_to_string("foo", "bar", 42); BOOST_TEST(cat == "foobar42"); } BOOST_AUTO_TEST_CASE(test_from_string) { { const std::string str("123"); BOOST_TEST(toml::from_string(str, 0) == 123); } { const std::string str("01"); BOOST_TEST(toml::from_string(str, 0) == 1); } } toml11-3.8.1/tests/test_value.cpp000066400000000000000000001123401454646465200167020ustar00rootroot00000000000000#include #include "unit_test.hpp" #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif BOOST_AUTO_TEST_CASE(test_value_boolean) { toml::value v1(true); toml::value v2(false); BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v2.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v2.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v2.cast() == false); BOOST_TEST(v1.as_boolean() == true); BOOST_TEST(v2.as_boolean() == false); BOOST_TEST(v1.as_boolean(std::nothrow) == true); BOOST_TEST(v2.as_boolean(std::nothrow) == false); v1 = false; v2 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v2.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v2.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(v1.cast() == false); BOOST_TEST(v2.cast() == true); BOOST_TEST(v1.as_boolean() == false); BOOST_TEST(v2.as_boolean() == true); toml::value v3(v1); toml::value v4(v2); BOOST_TEST(v3 == v1); BOOST_TEST(v4 == v2); BOOST_TEST(v3.type() == toml::value_t::boolean); BOOST_TEST(v4.type() == toml::value_t::boolean); BOOST_TEST(v3.is(toml::value_t::boolean)); BOOST_TEST(v4.is(toml::value_t::boolean)); BOOST_TEST(v3.is()); BOOST_TEST(v4.is()); BOOST_TEST(v3.is_boolean()); BOOST_TEST(v4.is_boolean()); BOOST_TEST(v3.cast() == false); BOOST_TEST(v4.cast() == true); BOOST_TEST(v3.as_boolean() == false); BOOST_TEST(v4.as_boolean() == true); toml::value v5(std::move(v1)); toml::value v6(std::move(v2)); BOOST_TEST(v5.type() == toml::value_t::boolean); BOOST_TEST(v6.type() == toml::value_t::boolean); BOOST_TEST(v5.is(toml::value_t::boolean)); BOOST_TEST(v6.is(toml::value_t::boolean)); BOOST_TEST(v5.is()); BOOST_TEST(v6.is()); BOOST_TEST(v3.is_boolean()); BOOST_TEST(v4.is_boolean()); BOOST_TEST(v5.cast() == false); BOOST_TEST(v6.cast() == true); BOOST_TEST(v5.as_boolean() == false); BOOST_TEST(v6.as_boolean() == true); v1 = 42; v2 = 3.14; BOOST_TEST(v1.type() == toml::value_t::integer); BOOST_TEST(v2.type() == toml::value_t::floating); BOOST_TEST(v1.is(toml::value_t::integer)); BOOST_TEST(v2.is(toml::value_t::floating)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_integer()); BOOST_TEST(v2.is_floating()); BOOST_TEST(v1.cast() == 42); BOOST_TEST(v2.cast() == 3.14); BOOST_TEST(v1.as_integer() == 42); BOOST_TEST(v2.as_floating() == 3.14); } BOOST_AUTO_TEST_CASE(test_value_integer) { toml::value v1(-42); toml::value v2(42u); BOOST_TEST(v1.type() == toml::value_t::integer); BOOST_TEST(v2.type() == toml::value_t::integer); BOOST_TEST(v1.is(toml::value_t::integer)); BOOST_TEST(v2.is(toml::value_t::integer)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_integer()); BOOST_TEST(v2.is_integer()); BOOST_TEST(v1.cast() == -42); BOOST_TEST(v2.cast() == 42u); BOOST_TEST(v1.as_integer() == -42); BOOST_TEST(v2.as_integer() == 42u); BOOST_TEST(v1.as_integer(std::nothrow) == -42); BOOST_TEST(v2.as_integer(std::nothrow) == 42u); v1 = 54; v2 = -54; BOOST_TEST(v1.type() == toml::value_t::integer); BOOST_TEST(v2.type() == toml::value_t::integer); BOOST_TEST(v1.is(toml::value_t::integer)); BOOST_TEST(v2.is(toml::value_t::integer)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_integer()); BOOST_TEST(v2.is_integer()); BOOST_TEST(v1.cast() == 54); BOOST_TEST(v2.cast() == -54); BOOST_TEST(v1.as_integer() == 54); BOOST_TEST(v2.as_integer() == -54); toml::value v3(v1); toml::value v4(v2); BOOST_TEST(v3 == v1); BOOST_TEST(v4 == v2); BOOST_TEST(v3.type() == toml::value_t::integer); BOOST_TEST(v4.type() == toml::value_t::integer); BOOST_TEST(v3.is(toml::value_t::integer)); BOOST_TEST(v4.is(toml::value_t::integer)); BOOST_TEST(v3.is()); BOOST_TEST(v4.is()); BOOST_TEST(v3.is_integer()); BOOST_TEST(v4.is_integer()); BOOST_TEST(v3.cast() == 54); BOOST_TEST(v4.cast() == -54); BOOST_TEST(v3.as_integer() == 54); BOOST_TEST(v4.as_integer() == -54); toml::value v5(std::move(v1)); toml::value v6(std::move(v2)); BOOST_TEST(v5.type() == toml::value_t::integer); BOOST_TEST(v6.type() == toml::value_t::integer); BOOST_TEST(v5.is(toml::value_t::integer)); BOOST_TEST(v6.is(toml::value_t::integer)); BOOST_TEST(v5.is()); BOOST_TEST(v6.is()); BOOST_TEST(v5.is_integer()); BOOST_TEST(v6.is_integer()); BOOST_TEST(v5.cast() == 54); BOOST_TEST(v6.cast() == -54); BOOST_TEST(v5.as_integer() == 54); BOOST_TEST(v6.as_integer() == -54); v1 = true; v2 = false; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v2.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v2.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v2.cast() == false); BOOST_TEST(v1.as_boolean() == true); BOOST_TEST(v2.as_boolean() == false); } BOOST_AUTO_TEST_CASE(test_value_float) { toml::value v1(3.14); toml::value v2(3.14f); BOOST_TEST(v1.type() == toml::value_t::floating); BOOST_TEST(v2.type() == toml::value_t::floating); BOOST_TEST(v1.is(toml::value_t::floating)); BOOST_TEST(v2.is(toml::value_t::floating)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_floating()); BOOST_TEST(v2.is_floating()); BOOST_TEST(v1.cast() == 3.14); BOOST_TEST(v2.cast() == 3.14, boost::test_tools::tolerance(1e-2)); BOOST_TEST(v1.as_floating() == 3.14); BOOST_TEST(v2.as_floating() == 3.14, boost::test_tools::tolerance(1e-2)); BOOST_TEST(v1.as_floating(std::nothrow) == 3.14); BOOST_TEST(v2.as_floating(std::nothrow) == 3.14, boost::test_tools::tolerance(1e-2)); v1 = 2.718f; v2 = 2.718; BOOST_TEST(v1.type() == toml::value_t::floating); BOOST_TEST(v2.type() == toml::value_t::floating); BOOST_TEST(v1.is(toml::value_t::floating)); BOOST_TEST(v2.is(toml::value_t::floating)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_floating()); BOOST_TEST(v2.is_floating()); BOOST_TEST(v1.cast() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v2.cast() == 2.718); BOOST_TEST(v1.as_floating() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v2.as_floating() == 2.718); toml::value v3(v1); toml::value v4(v2); BOOST_TEST(v3 == v1); BOOST_TEST(v4 == v2); BOOST_TEST(v3.type() == toml::value_t::floating); BOOST_TEST(v4.type() == toml::value_t::floating); BOOST_TEST(v3.is(toml::value_t::floating)); BOOST_TEST(v4.is(toml::value_t::floating)); BOOST_TEST(v3.is()); BOOST_TEST(v4.is()); BOOST_TEST(v3.is_floating()); BOOST_TEST(v4.is_floating()); BOOST_TEST(v3.cast() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v4.cast() == 2.718); BOOST_TEST(v3.as_floating() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v4.as_floating() == 2.718); toml::value v5(std::move(v1)); toml::value v6(std::move(v2)); BOOST_TEST(v5.type() == toml::value_t::floating); BOOST_TEST(v6.type() == toml::value_t::floating); BOOST_TEST(v5.is(toml::value_t::floating)); BOOST_TEST(v6.is(toml::value_t::floating)); BOOST_TEST(v5.is()); BOOST_TEST(v6.is()); BOOST_TEST(v5.is_floating()); BOOST_TEST(v6.is_floating()); BOOST_TEST(v5.cast() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v6.cast() == 2.718); BOOST_TEST(v5.as_floating() == 2.718, boost::test_tools::tolerance(1e-3)); BOOST_TEST(v6.as_floating() == 2.718); v1 = true; v2 = false; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v2.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v2.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v2.cast() == false); BOOST_TEST(v1.as_boolean() == true); BOOST_TEST(v2.as_boolean() == false); } BOOST_AUTO_TEST_CASE(test_value_string) { toml::value v1(std::string("foo")); toml::value v2(std::string("foo"), toml::string_t::literal); toml::value v3("foo"); BOOST_TEST(v1.type() == toml::value_t::string); BOOST_TEST(v2.type() == toml::value_t::string); BOOST_TEST(v3.type() == toml::value_t::string); BOOST_TEST(v1.is(toml::value_t::string)); BOOST_TEST(v2.is(toml::value_t::string)); BOOST_TEST(v3.is(toml::value_t::string)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v3.is()); BOOST_TEST(v1.is_string()); BOOST_TEST(v2.is_string()); BOOST_TEST(v3.is_string()); BOOST_TEST(v1.cast() == "foo"); BOOST_TEST(v2.cast() == "foo"); BOOST_TEST(v3.cast() == "foo"); BOOST_TEST(v1.as_string() == "foo"); BOOST_TEST(v2.as_string() == "foo"); BOOST_TEST(v3.as_string() == "foo"); BOOST_TEST(v1.as_string(std::nothrow) == "foo"); BOOST_TEST(v2.as_string(std::nothrow) == "foo"); BOOST_TEST(v3.as_string(std::nothrow) == "foo"); v1 = "bar"; v2 = "bar"; v3 = "bar"; BOOST_TEST(v1.type() == toml::value_t::string); BOOST_TEST(v2.type() == toml::value_t::string); BOOST_TEST(v3.type() == toml::value_t::string); BOOST_TEST(v1.is(toml::value_t::string)); BOOST_TEST(v2.is(toml::value_t::string)); BOOST_TEST(v3.is(toml::value_t::string)); BOOST_TEST(v1.is_string()); BOOST_TEST(v2.is_string()); BOOST_TEST(v3.is_string()); BOOST_TEST(v1.cast() == "bar"); BOOST_TEST(v2.cast() == "bar"); BOOST_TEST(v3.cast() == "bar"); BOOST_TEST(v1.as_string() == "bar"); BOOST_TEST(v2.as_string() == "bar"); BOOST_TEST(v3.as_string() == "bar"); toml::value v4(v1); toml::value v5(v2); toml::value v6(v3); BOOST_TEST(v4 == v1); BOOST_TEST(v5 == v2); BOOST_TEST(v6 == v3); BOOST_TEST(v4.type() == toml::value_t::string); BOOST_TEST(v5.type() == toml::value_t::string); BOOST_TEST(v6.type() == toml::value_t::string); BOOST_TEST(v4.is(toml::value_t::string)); BOOST_TEST(v5.is(toml::value_t::string)); BOOST_TEST(v6.is(toml::value_t::string)); BOOST_TEST(v4.is()); BOOST_TEST(v5.is()); BOOST_TEST(v6.is()); BOOST_TEST(v4.is_string()); BOOST_TEST(v5.is_string()); BOOST_TEST(v6.is_string()); BOOST_TEST(v4.cast() == "bar"); BOOST_TEST(v5.cast() == "bar"); BOOST_TEST(v6.cast() == "bar"); BOOST_TEST(v4.as_string() == "bar"); BOOST_TEST(v5.as_string() == "bar"); BOOST_TEST(v6.as_string() == "bar"); v4.cast().str.at(2) = 'z'; v5.cast().str.at(2) = 'z'; v6.cast().str.at(2) = 'z'; BOOST_TEST(v4.type() == toml::value_t::string); BOOST_TEST(v5.type() == toml::value_t::string); BOOST_TEST(v6.type() == toml::value_t::string); BOOST_TEST(v4.is(toml::value_t::string)); BOOST_TEST(v5.is(toml::value_t::string)); BOOST_TEST(v6.is(toml::value_t::string)); BOOST_TEST(v4.is()); BOOST_TEST(v5.is()); BOOST_TEST(v6.is()); BOOST_TEST(v4.is_string()); BOOST_TEST(v5.is_string()); BOOST_TEST(v6.is_string()); BOOST_TEST(v4.as_string() == "baz"); BOOST_TEST(v5.as_string() == "baz"); BOOST_TEST(v6.as_string() == "baz"); v1 = true; v2 = true; v3 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v2.type() == toml::value_t::boolean); BOOST_TEST(v3.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v2.is(toml::value_t::boolean)); BOOST_TEST(v3.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v3.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v2.is_boolean()); BOOST_TEST(v3.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v2.cast() == true); BOOST_TEST(v3.cast() == true); BOOST_TEST(v1.as_boolean() == true); BOOST_TEST(v2.as_boolean() == true); BOOST_TEST(v3.as_boolean() == true); #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L std::string_view sv = "foo"; toml::value v7(sv); toml::value v8(sv, toml::string_t::literal); BOOST_TEST(v7.type() == toml::value_t::string); BOOST_TEST(v8.type() == toml::value_t::string); BOOST_TEST(v7.is(toml::value_t::string)); BOOST_TEST(v8.is(toml::value_t::string)); BOOST_TEST(v7.is()); BOOST_TEST(v8.is()); BOOST_TEST(v7.is_string()); BOOST_TEST(v8.is_string()); BOOST_TEST(v7.cast() == "foo"); BOOST_TEST(v8.cast() == "foo"); #endif } BOOST_AUTO_TEST_CASE(test_value_local_date) { toml::value v1(toml::local_date(2018, toml::month_t::Jan, 31)); BOOST_TEST(v1.type() == toml::value_t::local_date); BOOST_TEST(v1.is(toml::value_t::local_date)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_local_date()); BOOST_TEST(v1.cast() == toml::local_date(2018, toml::month_t::Jan, 31)); BOOST_TEST(v1.as_local_date() == toml::local_date(2018, toml::month_t::Jan, 31)); BOOST_TEST(v1.as_local_date(std::nothrow) == toml::local_date(2018, toml::month_t::Jan, 31)); v1 = toml::local_date(2018, toml::month_t::Apr, 1); BOOST_TEST(v1.type() == toml::value_t::local_date); BOOST_TEST(v1.is(toml::value_t::local_date)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_local_date()); BOOST_TEST(v1.cast() == toml::local_date(2018, toml::month_t::Apr, 1)); BOOST_TEST(v1.as_local_date() == toml::local_date(2018, toml::month_t::Apr, 1)); toml::value v2(v1); BOOST_TEST(v2 == v1); BOOST_TEST(v2.type() == toml::value_t::local_date); BOOST_TEST(v2.is(toml::value_t::local_date)); BOOST_TEST(v2.is()); BOOST_TEST(v2.is_local_date()); BOOST_TEST(v2.cast() == toml::local_date(2018, toml::month_t::Apr, 1)); BOOST_TEST(v2.as_local_date() == toml::local_date(2018, toml::month_t::Apr, 1)); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_local_time) { toml::value v1(toml::local_time(12, 30, 45)); toml::value v2(std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45)); BOOST_TEST(v1.type() == toml::value_t::local_time); BOOST_TEST(v2.type() == toml::value_t::local_time); BOOST_TEST(v1.is(toml::value_t::local_time)); BOOST_TEST(v2.is(toml::value_t::local_time)); BOOST_TEST(v1.is()); BOOST_TEST(v2.is()); BOOST_TEST(v1.is_local_time()); BOOST_TEST(v2.is_local_time()); BOOST_TEST(v1.cast() == toml::local_time(12, 30, 45)); BOOST_TEST(v1.as_local_time() == toml::local_time(12, 30, 45)); BOOST_TEST(v2.cast() == toml::local_time(12, 30, 45)); BOOST_TEST(v2.as_local_time() == toml::local_time(12, 30, 45)); BOOST_TEST(v1.cast() == v2.cast()); BOOST_TEST(v1.as_local_time() == v2.as_local_time()); BOOST_TEST(v1.as_local_time(std::nothrow) == v2.as_local_time(std::nothrow)); v1 = toml::local_time(1, 30, 0, /*ms*/ 100, /*us*/ 0); BOOST_TEST(v1.type() == toml::value_t::local_time); BOOST_TEST(v1.is(toml::value_t::local_time)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_local_time()); BOOST_TEST(v1.cast() == toml::local_time(1, 30, 0, 100, 0)); BOOST_TEST(v1.as_local_time() == toml::local_time(1, 30, 0, 100, 0)); toml::value v3(v1); BOOST_TEST(v3 == v1); BOOST_TEST(v3.type() == toml::value_t::local_time); BOOST_TEST(v3.is(toml::value_t::local_time)); BOOST_TEST(v3.is()); BOOST_TEST(v3.is_local_time()); BOOST_TEST(v3.cast() == toml::local_time(1, 30, 0, 100, 0)); BOOST_TEST(v3.as_local_time() == toml::local_time(1, 30, 0, 100, 0)); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_local_datetime) { toml::value v1(toml::local_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45) )); BOOST_TEST(v1.type() == toml::value_t::local_datetime); BOOST_TEST(v1.is(toml::value_t::local_datetime)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_local_datetime()); BOOST_TEST(v1.cast() == toml::local_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45))); BOOST_TEST(v1.as_local_datetime() == toml::local_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45))); BOOST_TEST(v1.as_local_datetime(std::nothrow) == toml::local_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45))); v1 = toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30)); BOOST_TEST(v1.type() == toml::value_t::local_datetime); BOOST_TEST(v1.is(toml::value_t::local_datetime)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_local_datetime()); BOOST_TEST(v1.cast() == toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30))); BOOST_TEST(v1.as_local_datetime() == toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30))); toml::value v2(v1); BOOST_TEST(v2 == v1); BOOST_TEST(v2.type() == toml::value_t::local_datetime); BOOST_TEST(v2.is(toml::value_t::local_datetime)); BOOST_TEST(v2.is()); BOOST_TEST(v2.is_local_datetime()); BOOST_TEST(v2.cast() == toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30))); BOOST_TEST(v2.as_local_datetime() == toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30))); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_offset_datetime) { toml::value v1(toml::offset_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45), toml::time_offset(9, 0) )); BOOST_TEST(v1.type() == toml::value_t::offset_datetime); BOOST_TEST(v1.is(toml::value_t::offset_datetime)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(v1.cast() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45), toml::time_offset(9, 0) )); BOOST_TEST(v1.as_offset_datetime() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45), toml::time_offset(9, 0) )); BOOST_TEST(v1.as_offset_datetime(std::nothrow) == toml::offset_datetime( toml::local_date(2018, toml::month_t::Jan, 31), toml::local_time(12, 30, 45), toml::time_offset(9, 0) )); v1 = toml::offset_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30), toml::time_offset(9, 0)); BOOST_TEST(v1.type() == toml::value_t::offset_datetime); BOOST_TEST(v1.is(toml::value_t::offset_datetime)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(v1.cast() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30), toml::time_offset(9, 0))); BOOST_TEST(v1.as_offset_datetime() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30), toml::time_offset(9, 0))); toml::value v2(v1); BOOST_TEST(v2 == v1); BOOST_TEST(v2.type() == toml::value_t::offset_datetime); BOOST_TEST(v2.is(toml::value_t::offset_datetime)); BOOST_TEST(v2.is()); BOOST_TEST(v2.is_offset_datetime()); BOOST_TEST(v2.cast() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30), toml::time_offset(9, 0))); BOOST_TEST(v2.as_offset_datetime() == toml::offset_datetime( toml::local_date(2018, toml::month_t::Apr, 1), toml::local_time(1, 15, 30), toml::time_offset(9, 0))); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_array) { std::vector v{1,2,3,4,5}; toml::value v1(v); toml::value v2{6,7,8,9,0}; BOOST_TEST(v1.type() == toml::value_t::array); BOOST_TEST(v1.is(toml::value_t::array)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_array()); BOOST_TEST(v2.type() == toml::value_t::array); BOOST_TEST(v2.is(toml::value_t::array)); BOOST_TEST(v2.is()); BOOST_TEST(v2.is_array()); BOOST_TEST(v1.cast().at(0).cast() == 1); BOOST_TEST(v1.cast().at(1).cast() == 2); BOOST_TEST(v1.cast().at(2).cast() == 3); BOOST_TEST(v1.cast().at(3).cast() == 4); BOOST_TEST(v1.cast().at(4).cast() == 5); BOOST_TEST(v1.as_array().at(0).as_integer() == 1); BOOST_TEST(v1.as_array().at(1).as_integer() == 2); BOOST_TEST(v1.as_array().at(2).as_integer() == 3); BOOST_TEST(v1.as_array().at(3).as_integer() == 4); BOOST_TEST(v1.as_array().at(4).as_integer() == 5); BOOST_TEST(v1.as_array(std::nothrow).at(0).as_integer() == 1); BOOST_TEST(v1.as_array(std::nothrow).at(1).as_integer() == 2); BOOST_TEST(v1.as_array(std::nothrow).at(2).as_integer() == 3); BOOST_TEST(v1.as_array(std::nothrow).at(3).as_integer() == 4); BOOST_TEST(v1.as_array(std::nothrow).at(4).as_integer() == 5); BOOST_TEST(v2.cast().at(0).cast() == 6); BOOST_TEST(v2.cast().at(1).cast() == 7); BOOST_TEST(v2.cast().at(2).cast() == 8); BOOST_TEST(v2.cast().at(3).cast() == 9); BOOST_TEST(v2.cast().at(4).cast() == 0); v1 = {6,7,8,9,0}; v2 = v; BOOST_TEST(v1.type() == toml::value_t::array); BOOST_TEST(v1.is(toml::value_t::array)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_array()); BOOST_TEST(v2.type() == toml::value_t::array); BOOST_TEST(v2.is(toml::value_t::array)); BOOST_TEST(v2.is()); BOOST_TEST(v2.is_array()); BOOST_TEST(v1.cast().at(0).cast() == 6); BOOST_TEST(v1.cast().at(1).cast() == 7); BOOST_TEST(v1.cast().at(2).cast() == 8); BOOST_TEST(v1.cast().at(3).cast() == 9); BOOST_TEST(v1.cast().at(4).cast() == 0); BOOST_TEST(v1.as_array().at(0).as_integer() == 6); BOOST_TEST(v1.as_array().at(1).as_integer() == 7); BOOST_TEST(v1.as_array().at(2).as_integer() == 8); BOOST_TEST(v1.as_array().at(3).as_integer() == 9); BOOST_TEST(v1.as_array().at(4).as_integer() == 0); BOOST_TEST(v2.cast().at(0).cast() == 1); BOOST_TEST(v2.cast().at(1).cast() == 2); BOOST_TEST(v2.cast().at(2).cast() == 3); BOOST_TEST(v2.cast().at(3).cast() == 4); BOOST_TEST(v2.cast().at(4).cast() == 5); BOOST_TEST(v2.as_array().at(0).as_integer() == 1); BOOST_TEST(v2.as_array().at(1).as_integer() == 2); BOOST_TEST(v2.as_array().at(2).as_integer() == 3); BOOST_TEST(v2.as_array().at(3).as_integer() == 4); BOOST_TEST(v2.as_array().at(4).as_integer() == 5); toml::value v3(v1); BOOST_TEST(v3 == v1); BOOST_TEST(v3.type() == toml::value_t::array); BOOST_TEST(v3.is(toml::value_t::array)); BOOST_TEST(v3.is()); BOOST_TEST(v3.is_array()); BOOST_TEST(v3.cast().at(0).cast() == 6); BOOST_TEST(v3.cast().at(1).cast() == 7); BOOST_TEST(v3.cast().at(2).cast() == 8); BOOST_TEST(v3.cast().at(3).cast() == 9); BOOST_TEST(v3.cast().at(4).cast() == 0); BOOST_TEST(v3.as_array().at(0).as_integer() == 6); BOOST_TEST(v3.as_array().at(1).as_integer() == 7); BOOST_TEST(v3.as_array().at(2).as_integer() == 8); BOOST_TEST(v3.as_array().at(3).as_integer() == 9); BOOST_TEST(v3.as_array().at(4).as_integer() == 0); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_table) { toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; BOOST_TEST(v1.type() == toml::value_t::table); BOOST_TEST(v1.is(toml::value_t::table)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_table()); BOOST_TEST(v1.cast().at("foo").cast() == 42); BOOST_TEST(v1.cast().at("bar").cast() == 3.14); BOOST_TEST(v1.cast().at("baz").cast().str == "qux"); BOOST_TEST(v1.as_table().at("foo").as_integer() == 42); BOOST_TEST(v1.as_table().at("bar").as_floating() == 3.14); BOOST_TEST(v1.as_table().at("baz").as_string().str == "qux"); BOOST_TEST(v1.as_table(std::nothrow).at("foo").as_integer() == 42); BOOST_TEST(v1.as_table(std::nothrow).at("bar").as_floating() == 3.14); BOOST_TEST(v1.as_table(std::nothrow).at("baz").as_string().str == "qux"); v1 = {{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}}; BOOST_TEST(v1.type() == toml::value_t::table); BOOST_TEST(v1.is(toml::value_t::table)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_table()); BOOST_TEST(v1.cast().at("foo").cast() == 2.71); BOOST_TEST(v1.cast().at("bar").cast() == 54); BOOST_TEST(v1.cast().at("baz").cast().str == "quux"); BOOST_TEST(v1.as_table().at("foo").as_floating() == 2.71); BOOST_TEST(v1.as_table().at("bar").as_integer() == 54); BOOST_TEST(v1.as_table().at("baz").as_string().str == "quux"); v1 = toml::table{{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}}; BOOST_TEST(v1.type() == toml::value_t::table); BOOST_TEST(v1.is(toml::value_t::table)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_table()); BOOST_TEST(v1.cast().at("foo").cast() == 2.71); BOOST_TEST(v1.cast().at("bar").cast() == 54); BOOST_TEST(v1.cast().at("baz").cast().str == "quux"); BOOST_TEST(v1.as_table().at("foo").as_floating() == 2.71); BOOST_TEST(v1.as_table().at("bar").as_integer() == 54); BOOST_TEST(v1.as_table().at("baz").as_string().str == "quux"); toml::value v3(v1); BOOST_TEST(v3 == v1); BOOST_TEST(v3.type() == toml::value_t::table); BOOST_TEST(v3.is(toml::value_t::table)); BOOST_TEST(v3.is()); BOOST_TEST(v3.is_table()); BOOST_TEST(v3.cast().at("foo").cast() == 2.71); BOOST_TEST(v3.cast().at("bar").cast() == 54); BOOST_TEST(v3.cast().at("baz").cast().str == "quux"); BOOST_TEST(v3.as_table().at("foo").as_floating() == 2.71); BOOST_TEST(v3.as_table().at("bar").as_integer() == 54); BOOST_TEST(v3.as_table().at("baz").as_string().str == "quux"); v1 = true; BOOST_TEST(v1.type() == toml::value_t::boolean); BOOST_TEST(v1.is(toml::value_t::boolean)); BOOST_TEST(v1.is()); BOOST_TEST(v1.is_boolean()); BOOST_TEST(v1.cast() == true); BOOST_TEST(v1.as_boolean() == true); } BOOST_AUTO_TEST_CASE(test_value_empty) { toml::value v1; BOOST_TEST(v1.is_uninitialized()); BOOST_TEST(v1.is(toml::value_t::empty)); BOOST_CHECK_THROW(v1.as_boolean(), toml::type_error); BOOST_CHECK_THROW(v1.as_integer(), toml::type_error); BOOST_CHECK_THROW(v1.as_floating(), toml::type_error); BOOST_CHECK_THROW(v1.as_string(), toml::type_error); BOOST_CHECK_THROW(v1.as_offset_datetime(), toml::type_error); BOOST_CHECK_THROW(v1.as_local_datetime(), toml::type_error); BOOST_CHECK_THROW(v1.as_local_date(), toml::type_error); BOOST_CHECK_THROW(v1.as_local_time(), toml::type_error); BOOST_CHECK_THROW(v1.as_array(), toml::type_error); BOOST_CHECK_THROW(v1.as_table(), toml::type_error); } BOOST_AUTO_TEST_CASE(test_value_at) { { toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; BOOST_TEST(v1.at("foo").as_integer() == 42); BOOST_TEST(v1.at("bar").as_floating() == 3.14); BOOST_TEST(v1.at("baz").as_string() == "qux"); BOOST_CHECK_THROW(v1.at(0), toml::type_error); BOOST_CHECK_THROW(v1.at("quux"), std::out_of_range); } { toml::value v1{1,2,3,4,5}; BOOST_TEST(v1.at(0).as_integer() == 1); BOOST_TEST(v1.at(1).as_integer() == 2); BOOST_TEST(v1.at(2).as_integer() == 3); BOOST_TEST(v1.at(3).as_integer() == 4); BOOST_TEST(v1.at(4).as_integer() == 5); BOOST_CHECK_THROW(v1.at("foo"), toml::type_error); BOOST_CHECK_THROW(v1.at(5), std::out_of_range); } } BOOST_AUTO_TEST_CASE(test_value_bracket) { { toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; BOOST_TEST(v1["foo"].as_integer() == 42); BOOST_TEST(v1["bar"].as_floating() == 3.14); BOOST_TEST(v1["baz"].as_string() == "qux"); v1["qux"] = 54; BOOST_TEST(v1["qux"].as_integer() == 54); } { toml::value v1; v1["foo"] = 42; BOOST_TEST(v1.is_table()); BOOST_TEST(v1["foo"].as_integer() == 42); } { toml::value v1{1,2,3,4,5}; BOOST_TEST(v1[0].as_integer() == 1); BOOST_TEST(v1[1].as_integer() == 2); BOOST_TEST(v1[2].as_integer() == 3); BOOST_TEST(v1[3].as_integer() == 4); BOOST_TEST(v1[4].as_integer() == 5); BOOST_CHECK_THROW(v1["foo"], toml::type_error); } } BOOST_AUTO_TEST_CASE(test_value_map_methods) { { toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; BOOST_TEST(v1.count("foo") == 1u); BOOST_TEST(v1.count("bar") == 1u); BOOST_TEST(v1.count("baz") == 1u); BOOST_TEST(v1.count("qux") == 0u); BOOST_TEST( v1.contains("foo")); BOOST_TEST( v1.contains("bar")); BOOST_TEST( v1.contains("baz")); BOOST_TEST(!v1.contains("qux")); BOOST_TEST(v1.size() == 3); v1["qux"] = 54; BOOST_TEST(v1.count("qux") == 1u); BOOST_TEST(v1.contains("qux")); BOOST_TEST(v1.size() == 4); } { toml::value v1(42); BOOST_CHECK_THROW(v1.size() , toml::type_error); BOOST_CHECK_THROW(v1.count("k") , toml::type_error); BOOST_CHECK_THROW(v1.contains("k"), toml::type_error); } } BOOST_AUTO_TEST_CASE(test_value_vector_methods) { { toml::value v1{1, 2, 3, 4, 5}; BOOST_TEST(v1.size() == 5); v1.push_back(6); BOOST_TEST(v1.size() == 6); v1.emplace_back(6); BOOST_TEST(v1.size() == 7); } { toml::value v1(42); BOOST_CHECK_THROW(v1.size(), toml::type_error); BOOST_CHECK_THROW(v1.push_back(1), toml::type_error); BOOST_CHECK_THROW(v1.emplace_back(1), toml::type_error); } } toml11-3.8.1/tests/test_windows.cpp000066400000000000000000000004431454646465200172600ustar00rootroot00000000000000#include #include #include int main() { using namespace toml::literals::toml_literals; const auto data = R"(windows = "defines min and max as a macro")"_toml; std::cout << toml::find(data, "windows") << std::endl; return 0; } toml11-3.8.1/tests/unit_test.hpp000066400000000000000000000012771454646465200165600ustar00rootroot00000000000000#ifndef BOOST_TEST_MODULE # error "Please #define BOOST_TEST_MODULE before you #include " #endif #ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST # include #else # include #endif #include #include static inline auto testinput(const std::string& basename) -> std::string { const auto this_or_that = [](const char *const s, const char *const t) { return s ? s : t; }; std::string directory = this_or_that(std::getenv("TOMLDIR"), "toml"); if (!directory.empty() && directory.back() != '/') { directory.push_back('/'); } return directory.append("tests/").append(basename); } toml11-3.8.1/toml.hpp000066400000000000000000000026661454646465200143560ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2017 Toru Niina * * 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. */ #ifndef TOML_FOR_MODERN_CPP #define TOML_FOR_MODERN_CPP #define TOML11_VERSION_MAJOR 3 #define TOML11_VERSION_MINOR 8 #define TOML11_VERSION_PATCH 1 #include "toml/parser.hpp" #include "toml/literal.hpp" #include "toml/serializer.hpp" #include "toml/get.hpp" #include "toml/macros.hpp" #endif// TOML_FOR_MODERN_CPP toml11-3.8.1/toml/000077500000000000000000000000001454646465200136335ustar00rootroot00000000000000toml11-3.8.1/toml/color.hpp000066400000000000000000000053261454646465200154700ustar00rootroot00000000000000#ifndef TOML11_COLOR_HPP #define TOML11_COLOR_HPP #include #include #ifdef TOML11_COLORIZE_ERROR_MESSAGE #define TOML11_ERROR_MESSAGE_COLORIZED true #else #define TOML11_ERROR_MESSAGE_COLORIZED false #endif namespace toml { // put ANSI escape sequence to ostream namespace color_ansi { namespace detail { inline int colorize_index() { static const int index = std::ios_base::xalloc(); return index; } // Control color mode globally class color_mode { public: inline void enable() { should_color_ = true; } inline void disable() { should_color_ = false; } inline bool should_color() const { return should_color_; } static color_mode& status() { static color_mode status_; return status_; } private: bool should_color_ = false; }; } // detail inline std::ostream& colorize(std::ostream& os) { // by default, it is zero. os.iword(detail::colorize_index()) = 1; return os; } inline std::ostream& nocolorize(std::ostream& os) { os.iword(detail::colorize_index()) = 0; return os; } inline std::ostream& reset (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} inline std::ostream& bold (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} inline std::ostream& grey (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} inline std::ostream& red (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} inline std::ostream& green (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} inline std::ostream& yellow (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} inline std::ostream& blue (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} inline std::ostream& magenta(std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} inline std::ostream& cyan (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} inline std::ostream& white (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} inline void enable() { return detail::color_mode::status().enable(); } inline void disable() { return detail::color_mode::status().disable(); } inline bool should_color() { return detail::color_mode::status().should_color(); } } // color_ansi // ANSI escape sequence is the only and default colorization method currently namespace color = color_ansi; } // toml #endif// TOML11_COLOR_HPP toml11-3.8.1/toml/combinator.hpp000066400000000000000000000167041454646465200165110ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_COMBINATOR_HPP #define TOML11_COMBINATOR_HPP #include #include #include #include #include #include #include #include #include "region.hpp" #include "result.hpp" #include "traits.hpp" #include "utility.hpp" // they scans characters and returns region if it matches to the condition. // when they fail, it does not change the location. // in lexer.hpp, these are used. namespace toml { namespace detail { // to output character as an error message. inline std::string show_char(const char c) { // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. // I'm not completely sure but they check the value of char to be in the // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes // has negative value (if char has sign). So here it re-interprets c as // unsigned char through pointer. In general, converting pointer to a // pointer that has different type cause UB, but `(signed|unsigned)?char` // are one of the exceptions. Converting pointer only to char and std::byte // (c++17) are valid. if(std::isgraph(*reinterpret_cast(std::addressof(c)))) { return std::string(1, c); } else { std::array buf; buf.fill('\0'); const auto r = std::snprintf( buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); (void) r; // Unused variable warning assert(r == static_cast(buf.size()) - 1); return std::string(buf.data()); } } template struct character { static constexpr char target = C; static result invoke(location& loc) { if(loc.iter() == loc.end()) {return none();} const auto first = loc.iter(); const char c = *(loc.iter()); if(c != target) { return none(); } loc.advance(); // update location return ok(region(loc, first, loc.iter())); } }; template constexpr char character::target; // closed interval [Low, Up]. both Low and Up are included. template struct in_range { // assuming ascii part of UTF-8... static_assert(Low <= Up, "lower bound should be less than upper bound."); static constexpr char upper = Up; static constexpr char lower = Low; static result invoke(location& loc) { if(loc.iter() == loc.end()) {return none();} const auto first = loc.iter(); const char c = *(loc.iter()); if(c < lower || upper < c) { return none(); } loc.advance(); return ok(region(loc, first, loc.iter())); } }; template constexpr char in_range::upper; template constexpr char in_range::lower; // keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. // for detecting invalid characters, like control sequences in toml string. template struct exclude { static result invoke(location& loc) { if(loc.iter() == loc.end()) {return none();} auto first = loc.iter(); auto rslt = Combinator::invoke(loc); if(rslt.is_ok()) { loc.reset(first); return none(); } loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... return ok(region(loc, first, loc.iter())); } }; // increment `iter`, if matches. otherwise, just return empty string. template struct maybe { static result invoke(location& loc) { const auto rslt = Combinator::invoke(loc); if(rslt.is_ok()) { return rslt; } return ok(region(loc)); } }; template struct sequence; template struct sequence { static result invoke(location& loc) { const auto first = loc.iter(); auto rslt = Head::invoke(loc); if(rslt.is_err()) { loc.reset(first); return none(); } return sequence::invoke(loc, std::move(rslt.unwrap()), first); } // called from the above function only, recursively. template static result invoke(location& loc, region reg, Iterator first) { const auto rslt = Head::invoke(loc); if(rslt.is_err()) { loc.reset(first); return none(); } reg += rslt.unwrap(); // concat regions return sequence::invoke(loc, std::move(reg), first); } }; template struct sequence { // would be called from sequence::invoke only. template static result invoke(location& loc, region reg, Iterator first) { const auto rslt = Head::invoke(loc); if(rslt.is_err()) { loc.reset(first); return none(); } reg += rslt.unwrap(); // concat regions return ok(reg); } }; template struct either; template struct either { static result invoke(location& loc) { const auto rslt = Head::invoke(loc); if(rslt.is_ok()) {return rslt;} return either::invoke(loc); } }; template struct either { static result invoke(location& loc) { return Head::invoke(loc); } }; template struct repeat; template struct exactly{}; template struct at_least{}; struct unlimited{}; template struct repeat> { static result invoke(location& loc) { region retval(loc); const auto first = loc.iter(); for(std::size_t i=0; i struct repeat> { static result invoke(location& loc) { region retval(loc); const auto first = loc.iter(); for(std::size_t i=0; i struct repeat { static result invoke(location& loc) { region retval(loc); while(true) { auto rslt = T::invoke(loc); if(rslt.is_err()) { return ok(std::move(retval)); } retval += rslt.unwrap(); } } }; } // detail } // toml #endif// TOML11_COMBINATOR_HPP toml11-3.8.1/toml/comments.hpp000066400000000000000000000517121454646465200161770ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP #include #include #include #include #include #include #include #ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT # define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments #else # define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments #endif // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` // but bahaves in the opposite way. `preserve_comments` is just the same as // `std::vector` and each `std::string` corresponds to a comment line. // Conversely, `discard_comments` discards all the strings and ignores everything // assigned in it. `discard_comments` is always empty and you will encounter an // error whenever you access to the element. namespace toml { struct discard_comments; // forward decl // use it in the following way // // const toml::basic_value data = // toml::parse("example.toml"); // // the interface is almost the same as std::vector. struct preserve_comments { // `container_type` is not provided in discard_comments. // do not use this inner-type in a generic code. using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; preserve_comments() = default; ~preserve_comments() = default; preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; explicit preserve_comments(const std::vector& c): comments(c){} explicit preserve_comments(std::vector&& c) : comments(std::move(c)) {} preserve_comments& operator=(const std::vector& c) { comments = c; return *this; } preserve_comments& operator=(std::vector&& c) { comments = std::move(c); return *this; } explicit preserve_comments(const discard_comments&) {} explicit preserve_comments(size_type n): comments(n) {} preserve_comments(size_type n, const std::string& x): comments(n, x) {} preserve_comments(std::initializer_list x): comments(x) {} template preserve_comments(InputIterator first, InputIterator last) : comments(first, last) {} template void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} void assign(std::initializer_list ini) {comments.assign(ini);} void assign(size_type n, const std::string& val) {comments.assign(n, val);} // Related to the issue #97. // // It is known that `std::vector::insert` and `std::vector::erase` in // the standard library implementation included in GCC 4.8.5 takes // `std::vector::iterator` instead of `std::vector::const_iterator`. // Because of the const-correctness, we cannot convert a `const_iterator` to // an `iterator`. It causes compilation error in GCC 4.8.5. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION # endif #endif #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION iterator insert(iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } void insert(iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template void insert(iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } void insert(iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(iterator pos) {return comments.erase(pos);} iterator erase(iterator first, iterator last) { return comments.erase(first, last); } #else iterator insert(const_iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(const_iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } iterator insert(const_iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template iterator insert(const_iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } iterator insert(const_iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(const_iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(const_iterator pos) {return comments.erase(pos);} iterator erase(const_iterator first, const_iterator last) { return comments.erase(first, last); } #endif void swap(preserve_comments& other) {comments.swap(other.comments);} void push_back(const std::string& v) {comments.push_back(v);} void push_back(std::string&& v) {comments.push_back(std::move(v));} void pop_back() {comments.pop_back();} template void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} void clear() {comments.clear();} size_type size() const noexcept {return comments.size();} size_type max_size() const noexcept {return comments.max_size();} size_type capacity() const noexcept {return comments.capacity();} bool empty() const noexcept {return comments.empty();} void reserve(size_type n) {comments.reserve(n);} void resize(size_type n) {comments.resize(n);} void resize(size_type n, const std::string& c) {comments.resize(n, c);} void shrink_to_fit() {comments.shrink_to_fit();} reference operator[](const size_type n) noexcept {return comments[n];} const_reference operator[](const size_type n) const noexcept {return comments[n];} reference at(const size_type n) {return comments.at(n);} const_reference at(const size_type n) const {return comments.at(n);} reference front() noexcept {return comments.front();} const_reference front() const noexcept {return comments.front();} reference back() noexcept {return comments.back();} const_reference back() const noexcept {return comments.back();} pointer data() noexcept {return comments.data();} const_pointer data() const noexcept {return comments.data();} iterator begin() noexcept {return comments.begin();} iterator end() noexcept {return comments.end();} const_iterator begin() const noexcept {return comments.begin();} const_iterator end() const noexcept {return comments.end();} const_iterator cbegin() const noexcept {return comments.cbegin();} const_iterator cend() const noexcept {return comments.cend();} reverse_iterator rbegin() noexcept {return comments.rbegin();} reverse_iterator rend() noexcept {return comments.rend();} const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} const_reverse_iterator rend() const noexcept {return comments.rend();} const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} const_reverse_iterator crend() const noexcept {return comments.crend();} friend bool operator==(const preserve_comments&, const preserve_comments&); friend bool operator!=(const preserve_comments&, const preserve_comments&); friend bool operator< (const preserve_comments&, const preserve_comments&); friend bool operator<=(const preserve_comments&, const preserve_comments&); friend bool operator> (const preserve_comments&, const preserve_comments&); friend bool operator>=(const preserve_comments&, const preserve_comments&); friend void swap(preserve_comments&, std::vector&); friend void swap(std::vector&, preserve_comments&); private: container_type comments; }; inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} inline void swap(preserve_comments& lhs, preserve_comments& rhs) { lhs.swap(rhs); return; } inline void swap(preserve_comments& lhs, std::vector& rhs) { lhs.comments.swap(rhs); return; } inline void swap(std::vector& lhs, preserve_comments& rhs) { lhs.swap(rhs.comments); return; } template std::basic_ostream& operator<<(std::basic_ostream& os, const preserve_comments& com) { for(const auto& c : com) { os << '#' << c << '\n'; } return os; } namespace detail { // To provide the same interface with `preserve_comments`, `discard_comments` // should have an iterator. But it does not contain anything, so we need to // add an iterator that points nothing. // // It always points null, so DO NOT unwrap this iterator. It always crashes // your program. template struct empty_iterator { using value_type = T; using reference_type = typename std::conditional::type; using pointer_type = typename std::conditional::type; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; empty_iterator() = default; ~empty_iterator() = default; empty_iterator(empty_iterator const&) = default; empty_iterator(empty_iterator &&) = default; empty_iterator& operator=(empty_iterator const&) = default; empty_iterator& operator=(empty_iterator &&) = default; // DO NOT call these operators. reference_type operator*() const noexcept {std::terminate();} pointer_type operator->() const noexcept {return nullptr;} reference_type operator[](difference_type) const noexcept {return this->operator*();} // These operators do nothing. empty_iterator& operator++() noexcept {return *this;} empty_iterator operator++(int) noexcept {return *this;} empty_iterator& operator--() noexcept {return *this;} empty_iterator operator--(int) noexcept {return *this;} empty_iterator& operator+=(difference_type) noexcept {return *this;} empty_iterator& operator-=(difference_type) noexcept {return *this;} empty_iterator operator+(difference_type) const noexcept {return *this;} empty_iterator operator-(difference_type) const noexcept {return *this;} }; template bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template typename empty_iterator::difference_type operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} template empty_iterator operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} template empty_iterator operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} } // detail // The default comment type. It discards all the comments. It requires only one // byte to contain, so the memory footprint is smaller than preserve_comments. // // It just ignores `push_back`, `insert`, `erase`, and any other modifications. // IT always returns size() == 0, the iterator taken by `begin()` is always the // same as that of `end()`, and accessing through `operator[]` or iterators // always causes a segmentation fault. DO NOT access to the element of this. // // Why this is chose as the default type is because the last version (2.x.y) // does not contain any comments in a value. To minimize the impact on the // efficiency, this is chosen as a default. // // To reduce the memory footprint, later we can try empty base optimization (EBO). struct discard_comments { using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = detail::empty_iterator; using const_iterator = detail::empty_iterator; using reverse_iterator = detail::empty_iterator; using const_reverse_iterator = detail::empty_iterator; discard_comments() = default; ~discard_comments() = default; discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; explicit discard_comments(const std::vector&) noexcept {} explicit discard_comments(std::vector&&) noexcept {} discard_comments& operator=(const std::vector&) noexcept {return *this;} discard_comments& operator=(std::vector&&) noexcept {return *this;} explicit discard_comments(const preserve_comments&) noexcept {} explicit discard_comments(size_type) noexcept {} discard_comments(size_type, const std::string&) noexcept {} discard_comments(std::initializer_list) noexcept {} template discard_comments(InputIterator, InputIterator) noexcept {} template void assign(InputIterator, InputIterator) noexcept {} void assign(std::initializer_list) noexcept {} void assign(size_type, const std::string&) noexcept {} iterator insert(const_iterator, const std::string&) {return iterator{};} iterator insert(const_iterator, std::string&&) {return iterator{};} iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} template iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} iterator insert(const_iterator, std::initializer_list) {return iterator{};} template iterator emplace(const_iterator, Ts&& ...) {return iterator{};} iterator erase(const_iterator) {return iterator{};} iterator erase(const_iterator, const_iterator) {return iterator{};} void swap(discard_comments&) {return;} void push_back(const std::string&) {return;} void push_back(std::string&& ) {return;} void pop_back() {return;} template void emplace_back(Ts&& ...) {return;} void clear() {return;} size_type size() const noexcept {return 0;} size_type max_size() const noexcept {return 0;} size_type capacity() const noexcept {return 0;} bool empty() const noexcept {return true;} void reserve(size_type) {return;} void resize(size_type) {return;} void resize(size_type, const std::string&) {return;} void shrink_to_fit() {return;} // DO NOT access to the element of this container. This container is always // empty, so accessing through operator[], front/back, data causes address // error. reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} reference front() noexcept {never_call("toml::discard_comment::front");} const_reference front() const noexcept {never_call("toml::discard_comment::front");} reference back() noexcept {never_call("toml::discard_comment::back");} const_reference back() const noexcept {never_call("toml::discard_comment::back");} pointer data() noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;} iterator begin() noexcept {return iterator{};} iterator end() noexcept {return iterator{};} const_iterator begin() const noexcept {return const_iterator{};} const_iterator end() const noexcept {return const_iterator{};} const_iterator cbegin() const noexcept {return const_iterator{};} const_iterator cend() const noexcept {return const_iterator{};} reverse_iterator rbegin() noexcept {return iterator{};} reverse_iterator rend() noexcept {return iterator{};} const_reverse_iterator rbegin() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};} private: [[noreturn]] static void never_call(const char *const this_function) { #ifdef __has_builtin # if __has_builtin(__builtin_unreachable) __builtin_unreachable(); # endif #endif throw std::logic_error{this_function}; } }; inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} template std::basic_ostream& operator<<(std::basic_ostream& os, const discard_comments&) { return os; } } // toml11 #endif// TOML11_COMMENTS_HPP toml11-3.8.1/toml/datetime.hpp000066400000000000000000000530771454646465200161540ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_DATETIME_HPP #define TOML11_DATETIME_HPP #include #include #include #include #include #include #include #include namespace toml { // To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is // provided in the absolutely same purpose, but C++11 is actually not compatible // with C11. We need to dispatch the function depending on the OS. namespace detail { // TODO: find more sophisticated way to handle this #if defined(_MSC_VER) inline std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_s(&dst, src); if (result) { throw std::runtime_error("localtime_s failed."); } return dst; } inline std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_s(&dst, src); if (result) { throw std::runtime_error("gmtime_s failed."); } return dst; } #elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) inline std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_r(src, &dst); if (!result) { throw std::runtime_error("localtime_r failed."); } return dst; } inline std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_r(src, &dst); if (!result) { throw std::runtime_error("gmtime_r failed."); } return dst; } #else // fallback. not threadsafe inline std::tm localtime_s(const std::time_t* src) { const auto result = std::localtime(src); if (!result) { throw std::runtime_error("localtime failed."); } return *result; } inline std::tm gmtime_s(const std::time_t* src) { const auto result = std::gmtime(src); if (!result) { throw std::runtime_error("gmtime failed."); } return *result; } #endif } // detail enum class month_t : std::uint8_t { Jan = 0, Feb = 1, Mar = 2, Apr = 3, May = 4, Jun = 5, Jul = 6, Aug = 7, Sep = 8, Oct = 9, Nov = 10, Dec = 11 }; struct local_date { std::int16_t year{}; // A.D. (like, 2018) std::uint8_t month{}; // [0, 11] std::uint8_t day{}; // [1, 31] local_date(int y, month_t m, int d) : year (static_cast(y)), month(static_cast(m)), day (static_cast(d)) {} explicit local_date(const std::tm& t) : year (static_cast(t.tm_year + 1900)), month(static_cast(t.tm_mon)), day (static_cast(t.tm_mday)) {} explicit local_date(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); const auto time = detail::localtime_s(&t); *this = local_date(time); } explicit local_date(const std::time_t t) : local_date(std::chrono::system_clock::from_time_t(t)) {} operator std::chrono::system_clock::time_point() const { // std::mktime returns date as local time zone. no conversion needed std::tm t; t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0; t.tm_mday = static_cast(this->day); t.tm_mon = static_cast(this->month); t.tm_year = static_cast(this->year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; return std::chrono::system_clock::from_time_t(std::mktime(&t)); } operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } local_date() = default; ~local_date() = default; local_date(local_date const&) = default; local_date(local_date&&) = default; local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; }; inline bool operator==(const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) == std::make_tuple(rhs.year, rhs.month, rhs.day); } inline bool operator!=(const local_date& lhs, const local_date& rhs) { return !(lhs == rhs); } inline bool operator< (const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) < std::make_tuple(rhs.year, rhs.month, rhs.day); } inline bool operator<=(const local_date& lhs, const local_date& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator> (const local_date& lhs, const local_date& rhs) { return !(lhs <= rhs); } inline bool operator>=(const local_date& lhs, const local_date& rhs) { return !(lhs < rhs); } template std::basic_ostream& operator<<(std::basic_ostream& os, const local_date& date) { os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; return os; } struct local_time { std::uint8_t hour{}; // [0, 23] std::uint8_t minute{}; // [0, 59] std::uint8_t second{}; // [0, 60] std::uint16_t millisecond{}; // [0, 999] std::uint16_t microsecond{}; // [0, 999] std::uint16_t nanosecond{}; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0) : hour (static_cast(h)), minute(static_cast(m)), second(static_cast(s)), millisecond(static_cast(ms)), microsecond(static_cast(us)), nanosecond (static_cast(ns)) {} explicit local_time(const std::tm& t) : hour (static_cast(t.tm_hour)), minute(static_cast(t.tm_min)), second(static_cast(t.tm_sec)), millisecond(0), microsecond(0), nanosecond(0) {} template explicit local_time(const std::chrono::duration& t) { const auto h = std::chrono::duration_cast(t); this->hour = static_cast(h.count()); const auto t2 = t - h; const auto m = std::chrono::duration_cast(t2); this->minute = static_cast(m.count()); const auto t3 = t2 - m; const auto s = std::chrono::duration_cast(t3); this->second = static_cast(s.count()); const auto t4 = t3 - s; const auto ms = std::chrono::duration_cast(t4); this->millisecond = static_cast(ms.count()); const auto t5 = t4 - ms; const auto us = std::chrono::duration_cast(t5); this->microsecond = static_cast(us.count()); const auto t6 = t5 - us; const auto ns = std::chrono::duration_cast(t6); this->nanosecond = static_cast(ns.count()); } operator std::chrono::nanoseconds() const { return std::chrono::nanoseconds (this->nanosecond) + std::chrono::microseconds(this->microsecond) + std::chrono::milliseconds(this->millisecond) + std::chrono::seconds(this->second) + std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } local_time() = default; ~local_time() = default; local_time(local_time const&) = default; local_time(local_time&&) = default; local_time& operator=(local_time const&) = default; local_time& operator=(local_time&&) = default; }; inline bool operator==(const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } inline bool operator!=(const local_time& lhs, const local_time& rhs) { return !(lhs == rhs); } inline bool operator< (const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } inline bool operator<=(const local_time& lhs, const local_time& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator> (const local_time& lhs, const local_time& rhs) { return !(lhs <= rhs); } inline bool operator>=(const local_time& lhs, const local_time& rhs) { return !(lhs < rhs); } template std::basic_ostream& operator<<(std::basic_ostream& os, const local_time& time) { os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.second); if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) { os << '.'; os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); if(time.microsecond != 0 || time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); if(time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); } } } return os; } struct time_offset { std::int8_t hour{}; // [-12, 12] std::int8_t minute{}; // [-59, 59] time_offset(int h, int m) : hour (static_cast(h)), minute(static_cast(m)) {} operator std::chrono::minutes() const { return std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } time_offset() = default; ~time_offset() = default; time_offset(time_offset const&) = default; time_offset(time_offset&&) = default; time_offset& operator=(time_offset const&) = default; time_offset& operator=(time_offset&&) = default; }; inline bool operator==(const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) == std::make_tuple(rhs.hour, rhs.minute); } inline bool operator!=(const time_offset& lhs, const time_offset& rhs) { return !(lhs == rhs); } inline bool operator< (const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) < std::make_tuple(rhs.hour, rhs.minute); } inline bool operator<=(const time_offset& lhs, const time_offset& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator> (const time_offset& lhs, const time_offset& rhs) { return !(lhs <= rhs); } inline bool operator>=(const time_offset& lhs, const time_offset& rhs) { return !(lhs < rhs); } template std::basic_ostream& operator<<(std::basic_ostream& os, const time_offset& offset) { if(offset.hour == 0 && offset.minute == 0) { os << 'Z'; return os; } int minute = static_cast(offset.hour) * 60 + offset.minute; if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; os << std::setfill('0') << std::setw(2) << minute % 60; return os; } struct local_datetime { local_date date{}; local_time time{}; local_datetime(local_date d, local_time t): date(d), time(t) {} explicit local_datetime(const std::tm& t): date(t), time(t){} explicit local_datetime(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); std::tm ltime = detail::localtime_s(&t); this->date = local_date(ltime); this->time = local_time(ltime); // std::tm lacks subsecond information, so diff between tp and tm // can be used to get millisecond & microsecond information. const auto t_diff = tp - std::chrono::system_clock::from_time_t(std::mktime(<ime)); this->time.millisecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.microsecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.nanosecond = static_cast( std::chrono::duration_cast(t_diff).count()); } explicit local_datetime(const std::time_t t) : local_datetime(std::chrono::system_clock::from_time_t(t)) {} operator std::chrono::system_clock::time_point() const { using internal_duration = typename std::chrono::system_clock::time_point::duration; // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator // of local_date and local_time independently, the conversion fails if // it is the day when DST begins or ends. Since local_date considers the // time is 00:00 A.M. and local_time does not consider DST because it // does not have any date information. We need to consider both date and // time information at the same time to convert it correctly. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; // std::mktime returns date as local time zone. no conversion needed auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); dt += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); return dt; } operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } local_datetime() = default; ~local_datetime() = default; local_datetime(local_datetime const&) = default; local_datetime(local_datetime&&) = default; local_datetime& operator=(local_datetime const&) = default; local_datetime& operator=(local_datetime&&) = default; }; inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) == std::make_tuple(rhs.date, rhs.time); } inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs == rhs); } inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) < std::make_tuple(rhs.date, rhs.time); } inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) { return !(lhs <= rhs); } inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs < rhs); } template std::basic_ostream& operator<<(std::basic_ostream& os, const local_datetime& dt) { os << dt.date << 'T' << dt.time; return os; } struct offset_datetime { local_date date{}; local_time time{}; time_offset offset{}; offset_datetime(local_date d, local_time t, time_offset o) : date(d), time(t), offset(o) {} offset_datetime(const local_datetime& dt, time_offset o) : date(dt.date), time(dt.time), offset(o) {} explicit offset_datetime(const local_datetime& ld) : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) // use the current local timezone offset {} explicit offset_datetime(const std::chrono::system_clock::time_point& tp) : offset(0, 0) // use gmtime { const auto timet = std::chrono::system_clock::to_time_t(tp); const auto tm = detail::gmtime_s(&timet); this->date = local_date(tm); this->time = local_time(tm); } explicit offset_datetime(const std::time_t& t) : offset(0, 0) // use gmtime { const auto tm = detail::gmtime_s(&t); this->date = local_date(tm); this->time = local_time(tm); } explicit offset_datetime(const std::tm& t) : offset(0, 0) // assume gmtime { this->date = local_date(t); this->time = local_time(t); } operator std::chrono::system_clock::time_point() const { // get date-time using internal_duration = typename std::chrono::system_clock::time_point::duration; // first, convert it to local date-time information in the same way as // local_datetime does. later we will use time_t to adjust time offset. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; const std::time_t tp_loc = std::mktime(std::addressof(t)); auto tp = std::chrono::system_clock::from_time_t(tp_loc); tp += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); // Since mktime uses local time zone, it should be corrected. // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need // to add `+09:00` to `03:00:00Z`. // Here, it uses the time_t converted from date-time info to handle // daylight saving time. const auto ofs = get_local_offset(std::addressof(tp_loc)); tp += std::chrono::hours (ofs.hour); tp += std::chrono::minutes(ofs.minute); // We got `12:00:00Z` by correcting local timezone applied by mktime. // Then we will apply the offset. Let's say `12:00:00-08:00` is given. // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. // So we need to subtract the offset. tp -= std::chrono::minutes(this->offset); return tp; } operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } offset_datetime() = default; ~offset_datetime() = default; offset_datetime(offset_datetime const&) = default; offset_datetime(offset_datetime&&) = default; offset_datetime& operator=(offset_datetime const&) = default; offset_datetime& operator=(offset_datetime&&) = default; private: static time_offset get_local_offset(const std::time_t* tp) { // get local timezone with the same date-time information as mktime const auto t = detail::localtime_s(tp); std::array buf; const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 if(result != 5) { throw std::runtime_error("toml::offset_datetime: cannot obtain " "timezone information of current env"); } const int ofs = std::atoi(buf.data()); const int ofs_h = ofs / 100; const int ofs_m = ofs - (ofs_h * 100); return time_offset(ofs_h, ofs_m); } }; inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) == std::make_tuple(rhs.date, rhs.time, rhs.offset); } inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs == rhs); } inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) < std::make_tuple(rhs.date, rhs.time, rhs.offset); } inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs <= rhs); } inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs < rhs); } template std::basic_ostream& operator<<(std::basic_ostream& os, const offset_datetime& dt) { os << dt.date << 'T' << dt.time << dt.offset; return os; } }//toml #endif// TOML11_DATETIME toml11-3.8.1/toml/exception.hpp000066400000000000000000000040661454646465200163500ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP #include #include #include #include #include "source_location.hpp" namespace toml { struct file_io_error : public std::runtime_error { public: file_io_error(int errnum, const std::string& msg, const std::string& fname) : std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)), errno_(errnum) {} int get_errno() const noexcept {return errno_;} private: int errno_; }; struct exception : public std::exception { public: explicit exception(const source_location& loc): loc_(loc) {} virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} virtual source_location const& location() const noexcept {return loc_;} protected: source_location loc_; }; struct syntax_error : public toml::exception { public: explicit syntax_error(const std::string& what_arg, const source_location& loc) : exception(loc), what_(what_arg) {} virtual ~syntax_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; struct type_error : public toml::exception { public: explicit type_error(const std::string& what_arg, const source_location& loc) : exception(loc), what_(what_arg) {} virtual ~type_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; struct internal_error : public toml::exception { public: explicit internal_error(const std::string& what_arg, const source_location& loc) : exception(loc), what_(what_arg) {} virtual ~internal_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; } // toml #endif // TOML_EXCEPTION toml11-3.8.1/toml/from.hpp000066400000000000000000000005121454646465200153050ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_FROM_HPP #define TOML11_FROM_HPP namespace toml { template struct from; // { // static T from_toml(const toml::value& v) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_FROM_HPP toml11-3.8.1/toml/get.hpp000066400000000000000000001214141454646465200151260ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_GET_HPP #define TOML11_GET_HPP #include #include "from.hpp" #include "result.hpp" #include "value.hpp" namespace toml { // ============================================================================ // exact toml::* type template class M, template class V> detail::enable_if_t>::value, T> & get(basic_value& v) { return v.template cast>::value>(); } template class M, template class V> detail::enable_if_t>::value, T> const& get(const basic_value& v) { return v.template cast>::value>(); } template class M, template class V> detail::enable_if_t>::value, T> get(basic_value&& v) { return T(std::move(v).template cast>::value>()); } // ============================================================================ // T == toml::value; identity transformation. template class M, template class V> inline detail::enable_if_t>::value, T>& get(basic_value& v) { return v; } template class M, template class V> inline detail::enable_if_t>::value, T> const& get(const basic_value& v) { return v; } template class M, template class V> inline detail::enable_if_t>::value, T> get(basic_value&& v) { return basic_value(std::move(v)); } // ============================================================================ // T == toml::basic_value; basic_value -> basic_value template class M, template class V> inline detail::enable_if_t, detail::negation>> >::value, T> get(const basic_value& v) { return T(v); } // ============================================================================ // integer convertible from toml::Integer template class M, template class V> inline detail::enable_if_t, // T is integral detail::negation>, // but not bool detail::negation< // but not toml::integer detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { return static_cast(v.as_integer()); } // ============================================================================ // floating point convertible from toml::Float template class M, template class V> inline detail::enable_if_t, // T is floating_point detail::negation< // but not toml::floating detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { return static_cast(v.as_floating()); } // ============================================================================ // std::string; toml uses its own toml::string, but it should be convertible to // std::string seamlessly template class M, template class V> inline detail::enable_if_t::value, std::string>& get(basic_value& v) { return v.as_string().str; } template class M, template class V> inline detail::enable_if_t::value, std::string> const& get(const basic_value& v) { return v.as_string().str; } template class M, template class V> inline detail::enable_if_t::value, std::string> get(basic_value&& v) { return std::string(std::move(v.as_string().str)); } // ============================================================================ // std::string_view #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 template class M, template class V> inline detail::enable_if_t::value, std::string_view> get(const basic_value& v) { return std::string_view(v.as_string().str); } #endif // ============================================================================ // std::chrono::duration from toml::local_time. template class M, template class V> inline detail::enable_if_t::value, T> get(const basic_value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.as_local_time())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template class M, template class V> inline detail::enable_if_t< std::is_same::value, T> get(const basic_value& v) { switch(v.type()) { case value_t::local_date: { return std::chrono::system_clock::time_point(v.as_local_date()); } case value_t::local_datetime: { return std::chrono::system_clock::time_point(v.as_local_datetime()); } case value_t::offset_datetime: { return std::chrono::system_clock::time_point(v.as_offset_datetime()); } default: { throw type_error(detail::format_underline("toml::value: " "bad_cast to std::chrono::system_clock::time_point", { {v.location(), concat_to_string("the actual type is ", v.type())} }), v.location()); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. // array-like type with push_back(value) method template class M, template class V> detail::enable_if_t, // T is a container detail::has_push_back_method, // T::push_back(value) works detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // array-like type without push_back(value) method template class M, template class V> detail::enable_if_t, // T is a container detail::negation>, // w/o push_back(...) detail::negation>, // T does not have special conversion detail::negation< // not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // std::pair template class M, template class V> detail::enable_if_t::value, T> get(const basic_value&); // std::tuple template class M, template class V> detail::enable_if_t::value, T> get(const basic_value&); // map-like classes template class M, template class V> detail::enable_if_t, // T is map detail::negation< // but not toml::table detail::is_exact_toml_type>> >::value, T> get(const basic_value&); // T.from_toml(v) template class M, template class V> detail::enable_if_t>>, detail::has_from_toml_method, // but has from_toml(toml::value) std::is_default_constructible // and default constructible >::value, T> get(const basic_value&); // toml::from::from_toml(v) template class M, template class V> detail::enable_if_t::value, T> get(const basic_value&); template class M, template class V> detail::enable_if_t::value, T> get(basic_value&); // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template class M, template class V> detail::enable_if_t>, std::is_constructible&>, detail::negation>, detail::negation> >::value, T> get(const basic_value&); template class M, template class V> detail::enable_if_t>, std::is_constructible&>, detail::negation>, detail::negation> >::value, T> get(basic_value&); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template class M, template class V> detail::enable_if_t, // T is a container detail::has_push_back_method, // container.push_back(elem) works detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& ary = v.as_array(); T container; try_reserve(container, ary.size()); for(const auto& elem : ary) { container.push_back(get(elem)); } return container; } // ============================================================================ // std::forward_list does not have push_back, insert, or emplace. // It has insert_after, emplace_after, push_front. template class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; T container; for(const auto& elem : v.as_array()) { container.push_front(get(elem)); } container.reverse(); return container; } // ============================================================================ // array-like types, without push_back(). most likely [std|boost]::array. template class M, template class V> detail::enable_if_t, // T is a container detail::negation>, // w/o push_back detail::negation>, // T does not have special conversion detail::negation< // T is not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& ar = v.as_array(); T container; if(ar.size() != container.size()) { throw std::out_of_range(detail::format_underline(concat_to_string( "toml::get: specified container size is ", container.size(), " but there are ", ar.size(), " elements in toml array."), { {v.location(), "here"} })); } for(std::size_t i=0; i(ar[i]); } return container; } // ============================================================================ // std::pair. template class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.as_array(); if(ar.size() != 2) { throw std::out_of_range(detail::format_underline(concat_to_string( "toml::get: specified std::pair but there are ", ar.size(), " elements in toml array."), {{v.location(), "here"}})); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); } // ============================================================================ // std::tuple. namespace detail { template T get_tuple_impl(const Array& a, index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { const auto& ar = v.as_array(); if(ar.size() != std::tuple_size::value) { throw std::out_of_range(detail::format_underline(concat_to_string( "toml::get: specified std::tuple with ", std::tuple_size::value, " elements, but there are ", ar.size(), " elements in toml array."), {{v.location(), "here"}})); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); } // ============================================================================ // map-like types; most likely STL map, like std::map or std::unordered_map. template class M, template class V> detail::enable_if_t, // T is map detail::negation< // but not toml::array detail::is_exact_toml_type>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; static_assert(std::is_convertible::value, "toml::get only supports map type of which key_type is " "convertible from std::string."); T map; for(const auto& kv : v.as_table()) { map.emplace(key_type(kv.first), get(kv.second)); } return map; } // ============================================================================ // user-defined, but compatible types. template class M, template class V> detail::enable_if_t>>, detail::has_from_toml_method, // but has from_toml(toml::value) memfn std::is_default_constructible // and default constructible >::value, T> get(const basic_value& v) { T ud; ud.from_toml(v); return ud; } template class M, template class V> detail::enable_if_t::value, T> get(const basic_value& v) { return ::toml::from::from_toml(v); } template class M, template class V> detail::enable_if_t::value, T> get(basic_value& v) { return ::toml::from::from_toml(v); } template class M, template class V> detail::enable_if_t>, // T is not a toml::value std::is_constructible&>, // T is constructible from toml::value detail::negation>, // and T does not have T.from_toml(v); detail::negation> // and T does not have toml::from{}; >::value, T> get(const basic_value& v) { return T(v); } template class M, template class V> detail::enable_if_t>, // T is not a toml::value std::is_constructible&>, // T is constructible from toml::value detail::negation>, // and T does not have T.from_toml(v); detail::negation> // and T does not have toml::from{}; >::value, T> get(basic_value& v) { return T(v); } // ============================================================================ // find // ---------------------------------------------------------------------------- // these overloads do not require to set T. and returns value itself. template class M, template class V> basic_value const& find(const basic_value& v, const key& ky) { const auto& tab = v.as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return tab.at(ky); } template class M, template class V> basic_value& find(basic_value& v, const key& ky) { auto& tab = v.as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return tab.at(ky); } template class M, template class V> basic_value find(basic_value&& v, const key& ky) { typename basic_value::table_type tab = std::move(v).as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return basic_value(std::move(tab.at(ky))); } // ---------------------------------------------------------------------------- // find(value, idx) template class M, template class V> basic_value const& find(const basic_value& v, const std::size_t idx) { const auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return ary.at(idx); } template class M, template class V> basic_value& find(basic_value& v, const std::size_t idx) { auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return ary.at(idx); } template class M, template class V> basic_value find(basic_value&& v, const std::size_t idx) { auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return basic_value(std::move(ary.at(idx))); } // ---------------------------------------------------------------------------- // find(value, key); template class M, template class V> decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const key& ky) { const auto& tab = v.as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return ::toml::get(tab.at(ky)); } template class M, template class V> decltype(::toml::get(std::declval&>())) find(basic_value& v, const key& ky) { auto& tab = v.as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return ::toml::get(tab.at(ky)); } template class M, template class V> decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const key& ky) { typename basic_value::table_type tab = std::move(v).as_table(); if(tab.count(ky) == 0) { detail::throw_key_not_found_error(v, ky); } return ::toml::get(std::move(tab.at(ky))); } // ---------------------------------------------------------------------------- // find(value, idx) template class M, template class V> decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const std::size_t idx) { const auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return ::toml::get(ary.at(idx)); } template class M, template class V> decltype(::toml::get(std::declval&>())) find(basic_value& v, const std::size_t idx) { auto& ary = v.as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return ::toml::get(ary.at(idx)); } template class M, template class V> decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const std::size_t idx) { typename basic_value::array_type ary = std::move(v).as_array(); if(ary.size() <= idx) { throw std::out_of_range(detail::format_underline(concat_to_string( "index ", idx, " is out of range"), {{v.location(), "in this array"}})); } return ::toml::get(std::move(ary.at(idx))); } // -------------------------------------------------------------------------- // toml::find(toml::value, toml::key, Ts&& ... keys) namespace detail { // It suppresses warnings by -Wsign-conversion. Let's say we have the following // code. // ```cpp // const auto x = toml::find(data, "array", 0); // ``` // Here, the type of literal number `0` is `int`. `int` is a signed integer. // `toml::find` takes `std::size_t` as an index. So it causes implicit sign // conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` // suppresses the warning, but it makes user code messy. // To suppress this warning, we need to be aware of type conversion caused // by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of // keys can be any combination of {string-like, size_t-like}. Of course we can't // write down all the combinations. Thus we need to use some function that // recognize the type of argument and cast it into `std::string` or // `std::size_t` depending on the context. // `key_cast` does the job. It has 2 overloads. One is invoked when the // argument type is an integer and cast the argument into `std::size_t`. The // other is invoked when the argument type is not an integer, possibly one of // std::string, const char[N] or const char*, and construct std::string from // the argument. // `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to // `toml::find(v, k)` to suppress -Wsign-conversion. template enable_if_t>, negation, bool>>>::value, std::size_t> key_cast(T&& v) noexcept { return std::size_t(v); } template enable_if_t>, negation, bool>>>>::value, std::string> key_cast(T&& v) noexcept { return std::string(std::forward(v)); } } // detail template class M, template class V, typename Key1, typename Key2, typename ... Keys> const basic_value& find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(v, detail::key_cast(k1)), detail::key_cast(k2), std::forward(keys)...); } template class M, template class V, typename Key1, typename Key2, typename ... Keys> basic_value& find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(v, detail::key_cast(k1)), detail::key_cast(k2), std::forward(keys)...); } template class M, template class V, typename Key1, typename Key2, typename ... Keys> basic_value find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(std::move(v), std::forward(k1)), detail::key_cast(k2), std::forward(keys)...); } template class M, template class V, typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&>())) find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(v, detail::key_cast(k1)), detail::key_cast(k2), std::forward(keys)...); } template class M, template class V, typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&>())) find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(v, detail::key_cast(k1)), detail::key_cast(k2), std::forward(keys)...); } template class M, template class V, typename Key1, typename Key2, typename ... Keys> decltype(::toml::get(std::declval&&>())) find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) { return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), detail::key_cast(k2), std::forward(keys)...); } // ============================================================================ // get_or(value, fallback) template class M, template class V> basic_value const& get_or(const basic_value& v, const basic_value&) { return v; } template class M, template class V> basic_value& get_or(basic_value& v, basic_value&) { return v; } template class M, template class V> basic_value get_or(basic_value&& v, basic_value&&) { return v; } // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T> const& get_or(const basic_value& v, const T& opt) { try { return get>(v); } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>& get_or(basic_value& v, T& opt) { try { return get>(v); } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t, basic_value>::value, detail::remove_cvref_t> get_or(basic_value&& v, T&& opt) { try { return get>(std::move(v)); } catch(...) { return detail::remove_cvref_t(std::forward(opt)); } } // ---------------------------------------------------------------------------- // specialization for std::string (return type becomes lvalue ref) template class M, template class V> detail::enable_if_t, std::string>::value, std::string> const& get_or(const basic_value& v, const T& opt) { try { return v.as_string().str; } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t::value, std::string>& get_or(basic_value& v, T& opt) { try { return v.as_string().str; } catch(...) { return opt; } } template class M, template class V> detail::enable_if_t< std::is_same, std::string>::value, std::string> get_or(basic_value&& v, T&& opt) { try { return std::move(v.as_string().str); } catch(...) { return std::string(std::forward(opt)); } } // ---------------------------------------------------------------------------- // specialization for string literal template class M, template class V> detail::enable_if_t::type>::value, std::string> get_or(const basic_value& v, T&& opt) { try { return std::move(v.as_string().str); } catch(...) { return std::string(std::forward(opt)); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template class M, template class V> detail::enable_if_t, basic_value>>, detail::negation>>, detail::negation::type>> >::value, detail::remove_cvref_t> get_or(const basic_value& v, T&& opt) { try { return get>(v); } catch(...) { return detail::remove_cvref_t(std::forward(opt)); } } // =========================================================================== // find_or(value, key, fallback) template class M, template class V> basic_value const& find_or(const basic_value& v, const key& ky, const basic_value& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return tab.at(ky); } template class M, template class V> basic_value& find_or(basic_value& v, const toml::key& ky, basic_value& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return tab.at(ky); } template class M, template class V> basic_value find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) { if(!v.is_table()) {return opt;} auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) {return opt;} return basic_value(std::move(tab.at(ky))); } // --------------------------------------------------------------------------- // exact types (return type can be a reference) template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T> const& find_or(const basic_value& v, const key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, T>& find_or(basic_value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t< detail::is_exact_toml_type>::value, detail::remove_cvref_t> find_or(basic_value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::forward(opt);} auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(std::move(tab.at(ky)), std::forward(opt)); } // --------------------------------------------------------------------------- // std::string (return type can be a reference) template class M, template class V> detail::enable_if_t::value, std::string> const& find_or(const basic_value& v, const key& ky, const T& opt) { if(!v.is_table()) {return opt;} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t::value, std::string>& find_or(basic_value& v, const toml::key& ky, T& opt) { if(!v.is_table()) {return opt;} auto& tab = v.as_table(); if(tab.count(ky) == 0) {return opt;} return get_or(tab.at(ky), opt); } template class M, template class V> detail::enable_if_t::value, std::string> find_or(basic_value&& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::forward(opt);} auto tab = std::move(v).as_table(); if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(std::move(tab.at(ky)), std::forward(opt)); } // --------------------------------------------------------------------------- // string literal (deduced as std::string) template class M, template class V> detail::enable_if_t< detail::is_string_literal::type>::value, std::string> find_or(const basic_value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::string(opt);} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return std::string(opt);} return get_or(tab.at(ky), std::forward(opt)); } // --------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template class M, template class V> detail::enable_if_t, basic_value>>, // T is not std::string detail::negation>>, // T is not a string literal detail::negation::type>> >::value, detail::remove_cvref_t> find_or(const basic_value& v, const toml::key& ky, T&& opt) { if(!v.is_table()) {return std::forward(opt);} const auto& tab = v.as_table(); if(tab.count(ky) == 0) {return std::forward(opt);} return get_or(tab.at(ky), std::forward(opt)); } // --------------------------------------------------------------------------- // recursive find-or with type deduction (find_or(value, keys, opt)) template 1), std::nullptr_t> = nullptr> // here we need to add SFINAE in the template parameter to avoid // infinite recursion in type deduction on gcc auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) { if(!v.is_table()) { return detail::last_one(std::forward(keys)...); } auto&& tab = std::forward(v).as_table(); if(tab.count(ky) == 0) { return detail::last_one(std::forward(keys)...); } return find_or(std::forward(tab).at(ky), std::forward(keys)...); } // --------------------------------------------------------------------------- // recursive find_or with explicit type specialization, find_or(value, keys...) template 1), std::nullptr_t> = nullptr> // here we need to add SFINAE in the template parameter to avoid // infinite recursion in type deduction on gcc auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) { if(!v.is_table()) { return detail::last_one(std::forward(keys)...); } auto&& tab = std::forward(v).as_table(); if(tab.count(ky) == 0) { return detail::last_one(std::forward(keys)...); } return find_or(std::forward(tab).at(ky), std::forward(keys)...); } // ============================================================================ // expect template class M, template class V> result expect(const basic_value& v) noexcept { try { return ok(get(v)); } catch(const std::exception& e) { return err(e.what()); } } template class M, template class V> result expect(const basic_value& v, const toml::key& k) noexcept { try { return ok(find(v, k)); } catch(const std::exception& e) { return err(e.what()); } } } // toml #endif// TOML11_GET toml11-3.8.1/toml/into.hpp000066400000000000000000000005321454646465200153150ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_INTO_HPP #define TOML11_INTO_HPP namespace toml { template struct into; // { // static toml::value into_toml(const T& user_defined_type) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_INTO_HPP toml11-3.8.1/toml/lexer.hpp000066400000000000000000000334261454646465200154730ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_LEXER_HPP #define TOML11_LEXER_HPP #include #include #include #include "combinator.hpp" namespace toml { namespace detail { // these scans contents from current location in a container of char // and extract a region that matches their own pattern. // to see the implementation of each component, see combinator.hpp. using lex_wschar = either, character<'\t'>>; using lex_ws = repeat>; using lex_newline = either, sequence, character<'\n'>>>; using lex_lower = in_range<'a', 'z'>; using lex_upper = in_range<'A', 'Z'>; using lex_alpha = either; using lex_digit = in_range<'0', '9'>; using lex_nonzero = in_range<'1', '9'>; using lex_oct_dig = in_range<'0', '7'>; using lex_bin_dig = in_range<'0', '1'>; using lex_hex_dig = either, in_range<'a', 'f'>>; using lex_hex_prefix = sequence, character<'x'>>; using lex_oct_prefix = sequence, character<'o'>>; using lex_bin_prefix = sequence, character<'b'>>; using lex_underscore = character<'_'>; using lex_plus = character<'+'>; using lex_minus = character<'-'>; using lex_sign = either; // digit | nonzero 1*(digit | _ digit) using lex_unsigned_dec_int = either>, at_least<1>>>, lex_digit>; // (+|-)? unsigned_dec_int using lex_dec_int = sequence, lex_unsigned_dec_int>; // hex_prefix hex_dig *(hex_dig | _ hex_dig) using lex_hex_int = sequence>, unlimited>>>; // oct_prefix oct_dig *(oct_dig | _ oct_dig) using lex_oct_int = sequence>, unlimited>>>; // bin_prefix bin_dig *(bin_dig | _ bin_dig) using lex_bin_int = sequence>, unlimited>>>; // (dec_int | hex_int | oct_int | bin_int) using lex_integer = either; // =========================================================================== using lex_inf = sequence, character<'n'>, character<'f'>>; using lex_nan = sequence, character<'a'>, character<'n'>>; using lex_special_float = sequence, either>; using lex_zero_prefixable_int = sequence>, unlimited>>; using lex_fractional_part = sequence, lex_zero_prefixable_int>; using lex_exponent_part = sequence, character<'E'>>, maybe, lex_zero_prefixable_int>; using lex_float = either>>>>; // =========================================================================== using lex_true = sequence, character<'r'>, character<'u'>, character<'e'>>; using lex_false = sequence, character<'a'>, character<'l'>, character<'s'>, character<'e'>>; using lex_boolean = either; // =========================================================================== using lex_date_fullyear = repeat>; using lex_date_month = repeat>; using lex_date_mday = repeat>; using lex_time_delim = either, character<'t'>, character<' '>>; using lex_time_hour = repeat>; using lex_time_minute = repeat>; using lex_time_second = repeat>; using lex_time_secfrac = sequence, repeat>>; using lex_time_numoffset = sequence, character<'-'>>, sequence, lex_time_minute>>; using lex_time_offset = either, character<'z'>, lex_time_numoffset>; using lex_partial_time = sequence, lex_time_minute, character<':'>, lex_time_second, maybe>; using lex_full_date = sequence, lex_date_month, character<'-'>, lex_date_mday>; using lex_full_time = sequence; using lex_offset_date_time = sequence; using lex_local_date_time = sequence; using lex_local_date = lex_full_date; using lex_local_time = lex_partial_time; // =========================================================================== using lex_quotation_mark = character<'"'>; using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed in_range<0x0A, 0x1F>, character<0x22>, character<0x5C>, character<0x7F>>>; using lex_escape = character<'\\'>; using lex_escape_unicode_short = sequence, repeat>>; using lex_escape_unicode_long = sequence, repeat>>; using lex_escape_seq_char = either, character<'\\'>, character<'b'>, character<'f'>, character<'n'>, character<'r'>, character<'t'>, #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES character<'e'>, // ESC (0x1B) #endif lex_escape_unicode_short, lex_escape_unicode_long >; using lex_escaped = sequence; using lex_basic_char = either; using lex_basic_string = sequence, lex_quotation_mark>; // After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings // are allowed to be used. // After this, the following strings are *explicitly* allowed. // - One or two `"`s in a multi-line basic string is allowed wherever it is. // - Three consecutive `"`s in a multi-line basic string is considered as a delimiter. // - One or two `"`s can appear just before or after the delimiter. // ```toml // str4 = """Here are two quotation marks: "". Simple enough.""" // str5 = """Here are three quotation marks: ""\".""" // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" // str7 = """"This," she said, "is just a pointless statement."""" // ``` // In the current implementation (v3.3.0), it is difficult to parse `str7` in // the above example. It is difficult to recognize `"` at the end of string body // collectly. It will be misunderstood as a `"""` delimiter and an additional, // invalid `"`. Like this: // ```console // what(): [error] toml::parse_table: invalid line format // --> hoge.toml // | // 13 | str7 = """"This," she said, "is just a pointless statement."""" // | ^- expected newline, but got '"'. // ``` // As a quick workaround for this problem, `lex_ml_basic_string_delim` was // split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. // `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. // In parse_ml_basic_string() function, the trailing `"`s will be attached to // the string body. // using lex_ml_basic_string_delim = repeat>; using lex_ml_basic_string_open = lex_ml_basic_string_delim; using lex_ml_basic_string_close = sequence< repeat>, maybe, maybe >; using lex_ml_basic_unescaped = exclude, // 0x09 is tab in_range<0x0A, 0x1F>, character<0x5C>, // backslash character<0x7F>, // DEL lex_ml_basic_string_delim>>; using lex_ml_basic_escaped_newline = sequence< lex_escape, maybe, lex_newline, repeat, unlimited>>; using lex_ml_basic_char = either; using lex_ml_basic_body = repeat, unlimited>; using lex_ml_basic_string = sequence; using lex_literal_char = exclude, in_range<0x0A, 0x1F>, character<0x7F>, character<0x27>>>; using lex_apostrophe = character<'\''>; using lex_literal_string = sequence, lex_apostrophe>; // the same reason as above. using lex_ml_literal_string_delim = repeat>; using lex_ml_literal_string_open = lex_ml_literal_string_delim; using lex_ml_literal_string_close = sequence< repeat>, maybe, maybe >; using lex_ml_literal_char = exclude, in_range<0x0A, 0x1F>, character<0x7F>, lex_ml_literal_string_delim>>; using lex_ml_literal_body = repeat, unlimited>; using lex_ml_literal_string = sequence; using lex_string = either; // =========================================================================== using lex_dot_sep = sequence, character<'.'>, maybe>; using lex_unquoted_key = repeat, character<'_'>>, at_least<1>>; using lex_quoted_key = either; using lex_simple_key = either; using lex_dotted_key = sequence, at_least<1> > >; using lex_key = either; using lex_keyval_sep = sequence, character<'='>, maybe>; using lex_std_table_open = character<'['>; using lex_std_table_close = character<']'>; using lex_std_table = sequence, lex_key, maybe, lex_std_table_close>; using lex_array_table_open = sequence; using lex_array_table_close = sequence; using lex_array_table = sequence, lex_key, maybe, lex_array_table_close>; using lex_utf8_1byte = in_range<0x00, 0x7F>; using lex_utf8_2byte = sequence< in_range<'\xC2', '\xDF'>, in_range<'\x80', '\xBF'> >; using lex_utf8_3byte = sequence, in_range<'\xA0', '\xBF'>>, sequence, in_range<'\x80', '\xBF'>>, sequence, in_range<'\x80', '\x9F'>>, sequence, in_range<'\x80', '\xBF'>> >, in_range<'\x80', '\xBF'>>; using lex_utf8_4byte = sequence, in_range<'\x90', '\xBF'>>, sequence, in_range<'\x80', '\xBF'>>, sequence, in_range<'\x80', '\x8F'>> >, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>; using lex_utf8_code = either< lex_utf8_1byte, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte >; using lex_comment_start_symbol = character<'#'>; using lex_non_eol_ascii = either, in_range<0x20, 0x7E>>; using lex_comment = sequence, unlimited>>; } // detail } // toml #endif // TOML_LEXER_HPP toml11-3.8.1/toml/literal.hpp000066400000000000000000000075771454646465200160200ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_LITERAL_HPP #define TOML11_LITERAL_HPP #include "parser.hpp" namespace toml { inline namespace literals { inline namespace toml_literals { // implementation inline ::toml::basic_value literal_internal_impl(::toml::detail::location loc) { using value_type = ::toml::basic_value< TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; // if there are some comments or empty lines, skip them. using skip_line = ::toml::detail::repeat, ::toml::detail::maybe<::toml::detail::lex_comment>, ::toml::detail::lex_newline >, ::toml::detail::at_least<1>>; skip_line::invoke(loc); // if there are some whitespaces before a value, skip them. using skip_ws = ::toml::detail::repeat< ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; skip_ws::invoke(loc); // to distinguish arrays and tables, first check it is a table or not. // // "[1,2,3]"_toml; // this is an array // "[table]"_toml; // a table that has an empty table named "table" inside. // "[[1,2,3]]"_toml; // this is an array of arrays // "[[table]]"_toml; // this is a table that has an array of tables inside. // // "[[1]]"_toml; // this can be both... (currently it becomes a table) // "1 = [{}]"_toml; // this is a table that has an array of table named 1. // "[[1,]]"_toml; // this is an array of arrays. // "[[1],]"_toml; // this also. const auto the_front = loc.iter(); const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); loc.reset(the_front); const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); loc.reset(the_front); // If it is neither a table-key or a array-of-table-key, it may be a value. if(!is_table_key && !is_aots_key) { if(auto data = ::toml::detail::parse_value(loc, 0)) { return data.unwrap(); } } // Note that still it can be a table, because the literal might be something // like the following. // ```cpp // R"( // c++11 raw string literals // key = "value" // int = 42 // )"_toml; // ``` // It is a valid toml file. // It should be parsed as if we parse a file with this content. if(auto data = ::toml::detail::parse_toml_file(loc)) { return data.unwrap(); } else // none of them. { throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); } } inline ::toml::basic_value operator"" _toml(const char* str, std::size_t len) { ::toml::detail::location loc( std::string("TOML literal encoded in a C++ code"), std::vector(str, str + len)); // literal length does not include the null character at the end. return literal_internal_impl(std::move(loc)); } // value of __cplusplus in C++2a/20 mode is not fixed yet along compilers. // So here we use the feature test macro for `char8_t` itself. #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char inline ::toml::basic_value operator"" _toml(const char8_t* str, std::size_t len) { ::toml::detail::location loc( std::string("TOML literal encoded in a C++ code"), std::vector(reinterpret_cast(str), reinterpret_cast(str) + len)); return literal_internal_impl(std::move(loc)); } #endif } // toml_literals } // literals } // toml #endif//TOML11_LITERAL_HPP toml11-3.8.1/toml/macros.hpp000066400000000000000000000201541454646465200156320ustar00rootroot00000000000000#ifndef TOML11_MACROS_HPP #define TOML11_MACROS_HPP #define TOML11_STRINGIZE_AUX(x) #x #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) #define TOML11_CONCATENATE_AUX(x, y) x##y #define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) // ============================================================================ // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE // ---------------------------------------------------------------------------- // TOML11_ARGS_SIZE #define TOML11_INDEX_RSEQ() \ 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define TOML11_ARGS_SIZE_IMPL(\ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ ARG31, ARG32, N, ...) N #define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) #define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) // ---------------------------------------------------------------------------- // TOML11_FOR_EACH_VA_ARGS #define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) #define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) // ---------------------------------------------------------------------------- // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE // use it in the following way. // ```cpp // namespace foo // { // struct Foo // { // std::string s; // double d; // int i; // }; // } // foo // // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) // ``` // And then you can use `toml::find(file, "foo");` // #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ namespace toml { \ template<> \ struct from \ { \ template class T, \ template class A> \ static NAME from_toml(const basic_value& v) \ { \ NAME obj; \ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ return obj; \ } \ }; \ template<> \ struct into \ { \ static value into_toml(const NAME& obj) \ { \ ::toml::value v = ::toml::table{}; \ TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ return v; \ } \ }; \ } /* toml */ #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE #endif// TOML11_MACROS_HPP toml11-3.8.1/toml/parser.hpp000066400000000000000000003061771454646465200156560ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_PARSER_HPP #define TOML11_PARSER_HPP #include #include #include #include "combinator.hpp" #include "lexer.hpp" #include "macros.hpp" #include "region.hpp" #include "result.hpp" #include "types.hpp" #include "value.hpp" #ifndef TOML11_DISABLE_STD_FILESYSTEM #ifdef __cpp_lib_filesystem #if __has_include() #define TOML11_HAS_STD_FILESYSTEM #include #endif // has_include() #endif // __cpp_lib_filesystem #endif // TOML11_DISABLE_STD_FILESYSTEM // the previous commit works with 500+ recursions. so it may be too small. // but in most cases, i think we don't need such a deep recursion of // arrays or inline-tables. #define TOML11_VALUE_RECURSION_LIMIT 64 namespace toml { namespace detail { inline result, std::string> parse_boolean(location& loc) { const auto first = loc.iter(); if(const auto token = lex_boolean::invoke(loc)) { const auto reg = token.unwrap(); if (reg.str() == "true") {return ok(std::make_pair(true, reg));} else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} else // internal error. { throw internal_error(format_underline( "toml::parse_boolean: internal error", {{source_location(reg), "invalid token"}}), source_location(reg)); } } loc.reset(first); //rollback return err(format_underline("toml::parse_boolean: ", {{source_location(loc), "the next token is not a boolean"}})); } inline result, std::string> parse_binary_integer(location& loc) { const auto first = loc.iter(); if(const auto token = lex_bin_int::invoke(loc)) { auto str = token.unwrap().str(); assert(str.size() > 2); // minimum -> 0b1 assert(str.at(0) == '0' && str.at(1) == 'b'); // skip all the zeros and `_` locating at the MSB str.erase(str.begin(), std::find_if( str.begin() + 2, // to skip prefix `0b` str.end(), [](const char c) { return c == '1'; }) ); assert(str.empty() || str.front() == '1'); // since toml11 uses int64_t, 64bit (unsigned) input cannot be read. const auto max_length = 63 + std::count(str.begin(), str.end(), '_'); if(static_cast(max_length) < str.size()) { loc.reset(first); return err(format_underline("toml::parse_binary_integer: " "only signed 64bit integer is available", {{source_location(loc), "too large input (> int64_t)"}})); } integer retval(0), base(1); for(auto i(str.rbegin()), e(str.rend()); i!=e; ++i) { assert(base != 0); // means overflow, checked in the above code if(*i == '1') { retval += base; if( (std::numeric_limits::max)() / 2 < base ) { base = 0; } base *= 2; } else if(*i == '0') { if( (std::numeric_limits::max)() / 2 < base ) { base = 0; } base *= 2; } else if(*i == '_') { // do nothing. } else // should be detected by lex_bin_int. [[unlikely]] { throw internal_error(format_underline( "toml::parse_binary_integer: internal error", {{source_location(token.unwrap()), "invalid token"}}), source_location(loc)); } } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); return err(format_underline("toml::parse_binary_integer:", {{source_location(loc), "the next token is not an integer"}})); } inline result, std::string> parse_octal_integer(location& loc) { const auto first = loc.iter(); if(const auto token = lex_oct_int::invoke(loc)) { auto str = token.unwrap().str(); str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix std::istringstream iss(str); integer retval(0); iss >> std::oct >> retval; if(iss.fail()) { // `istream` sets `failbit` if internally-called `std::num_get::get` // fails. // `std::num_get::get` calls `std::strtoll` if the argument type is // signed. // `std::strtoll` fails if // - the value is out_of_range or // - no conversion is possible. // since we already checked that the string is valid octal integer, // so the error reason is out_of_range. loc.reset(first); return err(format_underline("toml::parse_octal_integer:", {{source_location(loc), "out of range"}})); } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); return err(format_underline("toml::parse_octal_integer:", {{source_location(loc), "the next token is not an integer"}})); } inline result, std::string> parse_hexadecimal_integer(location& loc) { const auto first = loc.iter(); if(const auto token = lex_hex_int::invoke(loc)) { auto str = token.unwrap().str(); str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix std::istringstream iss(str); integer retval(0); iss >> std::hex >> retval; if(iss.fail()) { // see parse_octal_integer for detail of this error message. loc.reset(first); return err(format_underline("toml::parse_hexadecimal_integer:", {{source_location(loc), "out of range"}})); } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); return err(format_underline("toml::parse_hexadecimal_integer", {{source_location(loc), "the next token is not an integer"}})); } inline result, std::string> parse_integer(location& loc) { const auto first = loc.iter(); if(first != loc.end() && *first == '0') { const auto second = std::next(first); if(second == loc.end()) // the token is just zero. { loc.advance(); return ok(std::make_pair(0, region(loc, first, second))); } if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE if(std::isdigit(*second)) { return err(format_underline("toml::parse_integer: " "leading zero in an Integer is not allowed.", {{source_location(loc), "leading zero"}})); } else if(std::isalpha(*second)) { return err(format_underline("toml::parse_integer: " "unknown integer prefix appeared.", {{source_location(loc), "none of 0x, 0o, 0b"}})); } } if(const auto token = lex_dec_int::invoke(loc)) { auto str = token.unwrap().str(); str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); std::istringstream iss(str); integer retval(0); iss >> retval; if(iss.fail()) { // see parse_octal_integer for detail of this error message. loc.reset(first); return err(format_underline("toml::parse_integer:", {{source_location(loc), "out of range"}})); } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); return err(format_underline("toml::parse_integer: ", {{source_location(loc), "the next token is not an integer"}})); } inline result, std::string> parse_floating(location& loc) { const auto first = loc.iter(); if(const auto token = lex_float::invoke(loc)) { auto str = token.unwrap().str(); if(str == "inf" || str == "+inf") { if(std::numeric_limits::has_infinity) { return ok(std::make_pair( std::numeric_limits::infinity(), token.unwrap())); } else { throw std::domain_error("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard."); } } else if(str == "-inf") { if(std::numeric_limits::has_infinity) { return ok(std::make_pair( -std::numeric_limits::infinity(), token.unwrap())); } else { throw std::domain_error("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard."); } } else if(str == "nan" || str == "+nan") { if(std::numeric_limits::has_quiet_NaN) { return ok(std::make_pair( std::numeric_limits::quiet_NaN(), token.unwrap())); } else if(std::numeric_limits::has_signaling_NaN) { return ok(std::make_pair( std::numeric_limits::signaling_NaN(), token.unwrap())); } else { throw std::domain_error("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard."); } } else if(str == "-nan") { if(std::numeric_limits::has_quiet_NaN) { return ok(std::make_pair( -std::numeric_limits::quiet_NaN(), token.unwrap())); } else if(std::numeric_limits::has_signaling_NaN) { return ok(std::make_pair( -std::numeric_limits::signaling_NaN(), token.unwrap())); } else { throw std::domain_error("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard."); } } str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); std::istringstream iss(str); floating v(0.0); iss >> v; if(iss.fail()) { // see parse_octal_integer for detail of this error message. loc.reset(first); return err(format_underline("toml::parse_floating:", {{source_location(loc), "out of range"}})); } return ok(std::make_pair(v, token.unwrap())); } loc.reset(first); return err(format_underline("toml::parse_floating: ", {{source_location(loc), "the next token is not a float"}})); } inline std::string read_utf8_codepoint(const region& reg, const location& loc) { const auto str = reg.str().substr(1); std::uint_least32_t codepoint; std::istringstream iss(str); iss >> std::hex >> codepoint; const auto to_char = [](const std::uint_least32_t i) noexcept -> char { const auto uc = static_cast(i); return *reinterpret_cast(std::addressof(uc)); }; std::string character; if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. { character += static_cast(codepoint); } else if(codepoint < 0x800) //U+0080 ... U+07FF { // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 character += to_char(0xC0| codepoint >> 6); character += to_char(0x80|(codepoint & 0x3F)); } else if(codepoint < 0x10000) // U+0800...U+FFFF { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { throw syntax_error(format_underline( "toml::read_utf8_codepoint: codepoints in the range " "[0xD800, 0xDFFF] are not valid UTF-8.", {{ source_location(loc), "not a valid UTF-8 codepoint" }}), source_location(loc)); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx character += to_char(0xE0| codepoint >> 12); character += to_char(0x80|(codepoint >> 6 & 0x3F)); character += to_char(0x80|(codepoint & 0x3F)); } else if(codepoint < 0x110000) // U+010000 ... U+10FFFF { // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += to_char(0xF0| codepoint >> 18); character += to_char(0x80|(codepoint >> 12 & 0x3F)); character += to_char(0x80|(codepoint >> 6 & 0x3F)); character += to_char(0x80|(codepoint & 0x3F)); } else // out of UTF-8 region { throw syntax_error(format_underline("toml::read_utf8_codepoint:" " input codepoint is too large.", {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), source_location(loc)); } return character; } inline result parse_escape_sequence(location& loc) { const auto first = loc.iter(); if(first == loc.end() || *first != '\\') { return err(format_underline("toml::parse_escape_sequence: ", {{ source_location(loc), "the next token is not a backslash \"\\\""}})); } loc.advance(); switch(*loc.iter()) { case '\\':{loc.advance(); return ok(std::string("\\"));} case '"' :{loc.advance(); return ok(std::string("\""));} case 'b' :{loc.advance(); return ok(std::string("\b"));} case 't' :{loc.advance(); return ok(std::string("\t"));} case 'n' :{loc.advance(); return ok(std::string("\n"));} case 'f' :{loc.advance(); return ok(std::string("\f"));} case 'r' :{loc.advance(); return ok(std::string("\r"));} #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES case 'e' :{loc.advance(); return ok(std::string("\x1b"));} // ESC #endif case 'u' : { if(const auto token = lex_escape_unicode_short::invoke(loc)) { return ok(read_utf8_codepoint(token.unwrap(), loc)); } else { return err(format_underline("parse_escape_sequence: " "invalid token found in UTF-8 codepoint uXXXX.", {{source_location(loc), "here"}})); } } case 'U': { if(const auto token = lex_escape_unicode_long::invoke(loc)) { return ok(read_utf8_codepoint(token.unwrap(), loc)); } else { return err(format_underline("parse_escape_sequence: " "invalid token found in UTF-8 codepoint Uxxxxxxxx", {{source_location(loc), "here"}})); } } } const auto msg = format_underline("parse_escape_sequence: " "unknown escape sequence appeared.", {{source_location(loc), "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, /* Hints = */{"if you want to write backslash as just one backslash, " "use literal string like: regex = '<\\i\\c*\\s*>'"}); loc.reset(first); return err(msg); } inline std::ptrdiff_t check_utf8_validity(const std::string& reg) { location loc("tmp", reg); const auto u8 = repeat::invoke(loc); if(!u8 || loc.iter() != loc.end()) { const auto error_location = std::distance(loc.begin(), loc.iter()); assert(0 <= error_location); return error_location; } return -1; } inline result, std::string> parse_ml_basic_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_ml_basic_string::invoke(loc)) { auto inner_loc = loc; inner_loc.reset(first); std::string retval; retval.reserve(token.unwrap().size()); auto delim = lex_ml_basic_string_open::invoke(inner_loc); if(!delim) { throw internal_error(format_underline( "parse_ml_basic_string: invalid token", {{source_location(inner_loc), "should be \"\"\""}}), source_location(inner_loc)); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); delim = none(); while(!delim) { using lex_unescaped_seq = repeat< either, unlimited>; if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) { retval += unescaped.unwrap().str(); } if(auto escaped = parse_escape_sequence(inner_loc)) { retval += escaped.unwrap(); } if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) { // ignore newline after escape until next non-ws char } if(inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "parse_ml_basic_string: unexpected end of region", {{source_location(inner_loc), "not sufficient token"}}), source_location(inner_loc)); } delim = lex_ml_basic_string_close::invoke(inner_loc); } // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s // at just before the delimiter. Here, we need to attach `"`s at the // end of the string body, if it exists. // For detail, see the definition of `lex_ml_basic_string_close`. assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), [](const char c) noexcept {return c == '\"';})); switch(delim.unwrap().size()) { case 3: {break;} case 4: {retval += "\""; break;} case 5: {retval += "\"\""; break;} default: { throw internal_error(format_underline( "parse_ml_basic_string: closing delimiter has invalid length", {{source_location(inner_loc), "end of this"}}), source_location(inner_loc)); } } const auto err_loc = check_utf8_validity(token.unwrap().str()); if(err_loc == -1) { return ok(std::make_pair(toml::string(retval), token.unwrap())); } else { inner_loc.reset(first); inner_loc.advance(err_loc); throw syntax_error(format_underline( "parse_ml_basic_string: invalid utf8 sequence found", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } } else { loc.reset(first); return err(format_underline("toml::parse_ml_basic_string: " "the next token is not a valid multiline string", {{source_location(loc), "here"}})); } } inline result, std::string> parse_basic_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_basic_string::invoke(loc)) { auto inner_loc = loc; inner_loc.reset(first); auto quot = lex_quotation_mark::invoke(inner_loc); if(!quot) { throw internal_error(format_underline("parse_basic_string: " "invalid token", {{source_location(inner_loc), "should be \""}}), source_location(inner_loc)); } std::string retval; retval.reserve(token.unwrap().size()); quot = none(); while(!quot) { using lex_unescaped_seq = repeat; if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) { retval += unescaped.unwrap().str(); } if(auto escaped = parse_escape_sequence(inner_loc)) { retval += escaped.unwrap(); } if(inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "parse_basic_string: unexpected end of region", {{source_location(inner_loc), "not sufficient token"}}), source_location(inner_loc)); } quot = lex_quotation_mark::invoke(inner_loc); } const auto err_loc = check_utf8_validity(token.unwrap().str()); if(err_loc == -1) { return ok(std::make_pair(toml::string(retval), token.unwrap())); } else { inner_loc.reset(first); inner_loc.advance(err_loc); throw syntax_error(format_underline( "parse_basic_string: invalid utf8 sequence found", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } } else { loc.reset(first); // rollback return err(format_underline("toml::parse_basic_string: " "the next token is not a valid string", {{source_location(loc), "here"}})); } } inline result, std::string> parse_ml_literal_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_ml_literal_string::invoke(loc)) { auto inner_loc = loc; inner_loc.reset(first); const auto open = lex_ml_literal_string_open::invoke(inner_loc); if(!open) { throw internal_error(format_underline( "parse_ml_literal_string: invalid token", {{source_location(inner_loc), "should be '''"}}), source_location(inner_loc)); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); const auto body = lex_ml_literal_body::invoke(inner_loc); const auto close = lex_ml_literal_string_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline( "parse_ml_literal_string: invalid token", {{source_location(inner_loc), "should be '''"}}), source_location(inner_loc)); } // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s // at just before the delimiter. Here, we need to attach `'`s at the // end of the string body, if it exists. // For detail, see the definition of `lex_ml_basic_string_close`. std::string retval = body.unwrap().str(); assert(std::all_of(close.unwrap().first(), close.unwrap().last(), [](const char c) noexcept {return c == '\'';})); switch(close.unwrap().size()) { case 3: {break;} case 4: {retval += "'"; break;} case 5: {retval += "''"; break;} default: { throw internal_error(format_underline( "parse_ml_literal_string: closing delimiter has invalid length", {{source_location(inner_loc), "end of this"}}), source_location(inner_loc)); } } const auto err_loc = check_utf8_validity(token.unwrap().str()); if(err_loc == -1) { return ok(std::make_pair(toml::string(retval, toml::string_t::literal), token.unwrap())); } else { inner_loc.reset(first); inner_loc.advance(err_loc); throw syntax_error(format_underline( "parse_ml_literal_string: invalid utf8 sequence found", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } } else { loc.reset(first); // rollback return err(format_underline("toml::parse_ml_literal_string: " "the next token is not a valid multiline literal string", {{source_location(loc), "here"}})); } } inline result, std::string> parse_literal_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_literal_string::invoke(loc)) { auto inner_loc = loc; inner_loc.reset(first); const auto open = lex_apostrophe::invoke(inner_loc); if(!open) { throw internal_error(format_underline( "parse_literal_string: invalid token", {{source_location(inner_loc), "should be '"}}), source_location(inner_loc)); } const auto body = repeat::invoke(inner_loc); const auto close = lex_apostrophe::invoke(inner_loc); if(!close) { throw internal_error(format_underline( "parse_literal_string: invalid token", {{source_location(inner_loc), "should be '"}}), source_location(inner_loc)); } const auto err_loc = check_utf8_validity(token.unwrap().str()); if(err_loc == -1) { return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), token.unwrap())); } else { inner_loc.reset(first); inner_loc.advance(err_loc); throw syntax_error(format_underline( "parse_literal_string: invalid utf8 sequence found", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } } else { loc.reset(first); // rollback return err(format_underline("toml::parse_literal_string: " "the next token is not a valid literal string", {{source_location(loc), "here"}})); } } inline result, std::string> parse_string(location& loc) { if(loc.iter() != loc.end() && *(loc.iter()) == '"') { if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') { return parse_ml_basic_string(loc); } else { return parse_basic_string(loc); } } else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') { if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') { return parse_ml_literal_string(loc); } else { return parse_literal_string(loc); } } return err(format_underline("toml::parse_string: ", {{source_location(loc), "the next token is not a string"}})); } inline result, std::string> parse_local_date(location& loc) { const auto first = loc.iter(); if(const auto token = lex_local_date::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto y = lex_date_fullyear::invoke(inner_loc); if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { throw internal_error(format_underline( "toml::parse_local_date: invalid year format", {{source_location(inner_loc), "should be `-`"}}), source_location(inner_loc)); } inner_loc.advance(); const auto m = lex_date_month::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { throw internal_error(format_underline( "toml::parse_local_date: invalid month format", {{source_location(inner_loc), "should be `-`"}}), source_location(inner_loc)); } inner_loc.advance(); const auto d = lex_date_mday::invoke(inner_loc); if(!d) { throw internal_error(format_underline( "toml::parse_local_date: invalid day format", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } const auto year = static_cast(from_string(y.unwrap().str(), 0)); const auto month = static_cast(from_string(m.unwrap().str(), 0)); const auto day = static_cast(from_string(d.unwrap().str(), 0)); // We briefly check whether the input date is valid or not. But here, we // only check if the RFC3339 compliance. // Actually there are several special date that does not exist, // because of historical reasons, such as 1582/10/5-1582/10/14 (only in // several countries). But here, we do not care about such a complicated // rule. It makes the code complicated and there is only low probability // that such a specific date is needed in practice. If someone need to // validate date accurately, that means that the one need a specialized // library for their purpose in a different layer. { const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); if((month < 1 || 12 < month) || (day < 1 || max_day < day)) { throw syntax_error(format_underline("toml::parse_date: " "invalid date: it does not conform RFC3339.", {{ source_location(loc), "month should be 01-12, day should be" " 01-28,29,30,31, depending on month/year." }}), source_location(inner_loc)); } } return ok(std::make_pair(local_date(year, static_cast(month - 1), day), token.unwrap())); } else { loc.reset(first); return err(format_underline("toml::parse_local_date: ", {{source_location(loc), "the next token is not a local_date"}})); } } inline result, std::string> parse_local_time(location& loc) { const auto first = loc.iter(); if(const auto token = lex_local_time::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto h = lex_time_hour::invoke(inner_loc); if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { throw internal_error(format_underline( "toml::parse_local_time: invalid year format", {{source_location(inner_loc), "should be `:`"}}), source_location(inner_loc)); } inner_loc.advance(); const auto m = lex_time_minute::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { throw internal_error(format_underline( "toml::parse_local_time: invalid month format", {{source_location(inner_loc), "should be `:`"}}), source_location(inner_loc)); } inner_loc.advance(); const auto s = lex_time_second::invoke(inner_loc); if(!s) { throw internal_error(format_underline( "toml::parse_local_time: invalid second format", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } const int hour = from_string(h.unwrap().str(), 0); const int minute = from_string(m.unwrap().str(), 0); const int second = from_string(s.unwrap().str(), 0); if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || (second < 0 || 60 < second)) // it may be leap second { throw syntax_error(format_underline("toml::parse_local_time: " "invalid time: it does not conform RFC3339.", {{ source_location(loc), "hour should be 00-23, minute should be" " 00-59, second should be 00-60 (depending on the leap" " second rules.)"}}), source_location(inner_loc)); } local_time time(hour, minute, second, 0, 0); const auto before_secfrac = inner_loc.iter(); if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) { auto sf = secfrac.unwrap().str(); sf.erase(sf.begin()); // sf.front() == '.' switch(sf.size() % 3) { case 2: sf += '0'; break; case 1: sf += "00"; break; case 0: break; default: break; } if(sf.size() >= 9) { time.millisecond = from_string(sf.substr(0, 3), 0u); time.microsecond = from_string(sf.substr(3, 3), 0u); time.nanosecond = from_string(sf.substr(6, 3), 0u); } else if(sf.size() >= 6) { time.millisecond = from_string(sf.substr(0, 3), 0u); time.microsecond = from_string(sf.substr(3, 3), 0u); } else if(sf.size() >= 3) { time.millisecond = from_string(sf, 0u); time.microsecond = 0u; } } else { if(before_secfrac != inner_loc.iter()) { throw internal_error(format_underline( "toml::parse_local_time: invalid subsecond format", {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } } return ok(std::make_pair(time, token.unwrap())); } else { loc.reset(first); return err(format_underline("toml::parse_local_time: ", {{source_location(loc), "the next token is not a local_time"}})); } } inline result, std::string> parse_local_datetime(location& loc) { const auto first = loc.iter(); if(const auto token = lex_local_date_time::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto date = parse_local_date(inner_loc); if(!date || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{source_location(inner_loc), "date, not datetime"}}), source_location(inner_loc)); } const char delim = *(inner_loc.iter()); if(delim != 'T' && delim != 't' && delim != ' ') { throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), source_location(inner_loc)); } inner_loc.advance(); const auto time = parse_local_time(inner_loc); if(!time) { throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{source_location(inner_loc), "invalid time format"}}), source_location(inner_loc)); } return ok(std::make_pair( local_datetime(date.unwrap().first, time.unwrap().first), token.unwrap())); } else { loc.reset(first); return err(format_underline("toml::parse_local_datetime: ", {{source_location(loc), "the next token is not a local_datetime"}})); } } inline result, std::string> parse_offset_datetime(location& loc) { const auto first = loc.iter(); if(const auto token = lex_offset_date_time::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto datetime = parse_local_datetime(inner_loc); if(!datetime || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "toml::parse_offset_datetime: invalid datetime format", {{source_location(inner_loc), "date, not datetime"}}), source_location(inner_loc)); } time_offset offset(0, 0); if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) { const auto str = ofs.unwrap().str(); const auto hour = from_string(str.substr(1,2), 0); const auto minute = from_string(str.substr(4,2), 0); if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) { throw syntax_error(format_underline("toml::parse_offset_datetime: " "invalid offset: it does not conform RFC3339.", {{ source_location(loc), "month should be 01-12, day should be" " 01-28,29,30,31, depending on month/year." }}), source_location(inner_loc)); } if(str.front() == '+') { offset = time_offset(hour, minute); } else { offset = time_offset(-hour, -minute); } } else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') { throw internal_error(format_underline( "toml::parse_offset_datetime: invalid datetime format", {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), source_location(inner_loc)); } return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), token.unwrap())); } else { loc.reset(first); return err(format_underline("toml::parse_offset_datetime: ", {{source_location(loc), "the next token is not a offset_datetime"}})); } } inline result, std::string> parse_simple_key(location& loc) { if(const auto bstr = parse_basic_string(loc)) { return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); } if(const auto lstr = parse_literal_string(loc)) { return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); } if(const auto bare = lex_unquoted_key::invoke(loc)) { const auto reg = bare.unwrap(); return ok(std::make_pair(reg.str(), reg)); } return err(format_underline("toml::parse_simple_key: ", {{source_location(loc), "the next token is not a simple key"}})); } // dotted key become vector of keys inline result, region>, std::string> parse_key(location& loc) { const auto first = loc.iter(); // dotted key -> `foo.bar.baz` where several single keys are chained by // dots. Whitespaces between keys and dots are allowed. if(const auto token = lex_dotted_key::invoke(loc)) { const auto reg = token.unwrap(); location inner_loc(loc.name(), reg.str()); std::vector keys; while(inner_loc.iter() != inner_loc.end()) { lex_ws::invoke(inner_loc); if(const auto k = parse_simple_key(inner_loc)) { keys.push_back(k.unwrap().first); } else { throw internal_error(format_underline( "toml::parse_key: dotted key contains invalid key", {{source_location(inner_loc), k.unwrap_err()}}), source_location(inner_loc)); } lex_ws::invoke(inner_loc); if(inner_loc.iter() == inner_loc.end()) { break; } else if(*inner_loc.iter() == '.') { inner_loc.advance(); // to skip `.` } else { throw internal_error(format_underline("toml::parse_key: " "dotted key contains invalid key ", {{source_location(inner_loc), "should be `.`"}}), source_location(inner_loc)); } } return ok(std::make_pair(keys, reg)); } loc.reset(first); // simple_key: a single (basic_string|literal_string|bare key) if(const auto smpl = parse_simple_key(loc)) { return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } return err(format_underline("toml::parse_key: an invalid key appeared.", {{source_location(loc), "is not a valid key"}}, { "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", "quoted keys: same as \"basic strings\" or 'literal strings'.", "dotted keys: sequence of bare or quoted keys joined with a dot." })); } // forward-decl to implement parse_array and parse_table template result parse_value(location&, const std::size_t n_rec); template result, std::string> parse_array(location& loc, const std::size_t n_rec) { using value_type = Value; using array_type = typename value_type::array_type; if(n_rec > TOML11_VALUE_RECURSION_LIMIT) { // parse_array does not have any way to handle recursive error currently... throw syntax_error(std::string("toml::parse_array: recursion limit (" TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), source_location(loc)); } const auto first = loc.iter(); if(loc.iter() == loc.end()) { return err("toml::parse_array: input is empty"); } if(*loc.iter() != '[') { return err("toml::parse_array: token is not an array"); } loc.advance(); using lex_ws_comment_newline = repeat< either, unlimited>; array_type retval; while(loc.iter() != loc.end()) { lex_ws_comment_newline::invoke(loc); // skip if(loc.iter() != loc.end() && *loc.iter() == ']') { loc.advance(); // skip ']' return ok(std::make_pair(retval, region(loc, first, loc.iter()))); } if(auto val = parse_value(loc, n_rec+1)) { // After TOML v1.0.0-rc.1, array becomes to be able to have values // with different types. So here we will omit this by default. // // But some of the test-suite checks if the parser accepts a hetero- // geneous arrays, so we keep this for a while. #ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS if(!retval.empty() && retval.front().type() != val.as_ok().type()) { auto array_start_loc = loc; array_start_loc.reset(first); throw syntax_error(format_underline("toml::parse_array: " "type of elements should be the same each other.", { {source_location(array_start_loc), "array starts here"}, { retval.front().location(), "value has type " + stringize(retval.front().type()) }, { val.unwrap().location(), "value has different type, " + stringize(val.unwrap().type()) } }), source_location(loc)); } #endif retval.push_back(std::move(val.unwrap())); } else { auto array_start_loc = loc; array_start_loc.reset(first); throw syntax_error(format_underline("toml::parse_array: " "value having invalid format appeared in an array", { {source_location(array_start_loc), "array starts here"}, {source_location(loc), "it is not a valid value."} }), source_location(loc)); } using lex_array_separator = sequence, character<','>>; const auto sp = lex_array_separator::invoke(loc); if(!sp) { lex_ws_comment_newline::invoke(loc); if(loc.iter() != loc.end() && *loc.iter() == ']') { loc.advance(); // skip ']' return ok(std::make_pair(retval, region(loc, first, loc.iter()))); } else { auto array_start_loc = loc; array_start_loc.reset(first); throw syntax_error(format_underline("toml::parse_array:" " missing array separator `,` after a value", { {source_location(array_start_loc), "array starts here"}, {source_location(loc), "should be `,`"} }), source_location(loc)); } } } loc.reset(first); throw syntax_error(format_underline("toml::parse_array: " "array did not closed by `]`", {{source_location(loc), "should be closed"}}), source_location(loc)); } template result, region>, Value>, std::string> parse_key_value_pair(location& loc, const std::size_t n_rec) { using value_type = Value; const auto first = loc.iter(); auto key_reg = parse_key(loc); if(!key_reg) { std::string msg = std::move(key_reg.unwrap_err()); // if the next token is keyvalue-separator, it means that there are no // key. then we need to show error as "empty key is not allowed". if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) { loc.reset(first); msg = format_underline("toml::parse_key_value_pair: " "empty key is not allowed.", {{source_location(loc), "key expected before '='"}}); } return err(std::move(msg)); } const auto kvsp = lex_keyval_sep::invoke(loc); if(!kvsp) { std::string msg; // if the line contains '=' after the invalid sequence, possibly the // error is in the key (like, invalid character in bare key). const auto line_end = std::find(loc.iter(), loc.end(), '\n'); if(std::find(loc.iter(), line_end, '=') != line_end) { msg = format_underline("toml::parse_key_value_pair: " "invalid format for key", {{source_location(loc), "invalid character in key"}}, {"Did you forget '.' to separate dotted-key?", "Allowed characters for bare key are [0-9a-zA-Z_-]."}); } else // if not, the error is lack of key-value separator. { msg = format_underline("toml::parse_key_value_pair: " "missing key-value separator `=`", {{source_location(loc), "should be `=`"}}); } loc.reset(first); return err(std::move(msg)); } const auto after_kvsp = loc.iter(); // err msg auto val = parse_value(loc, n_rec); if(!val) { std::string msg; loc.reset(after_kvsp); // check there is something not a comment/whitespace after `=` if(sequence, maybe, lex_newline>::invoke(loc)) { loc.reset(after_kvsp); msg = format_underline("toml::parse_key_value_pair: " "missing value after key-value separator '='", {{source_location(loc), "expected value, but got nothing"}}); } else // there is something not a comment/whitespace, so invalid format. { msg = std::move(val.unwrap_err()); } loc.reset(first); return err(msg); } return ok(std::make_pair(std::move(key_reg.unwrap()), std::move(val.unwrap()))); } // for error messages. template std::string format_dotted_keys(InputIterator first, const InputIterator last) { static_assert(std::is_same::value_type>::value,""); std::string retval(*first++); for(; first != last; ++first) { retval += '.'; retval += *first; } return retval; } // forward decl for is_valid_forward_table_definition result, region>, std::string> parse_table_key(location& loc); result, region>, std::string> parse_array_table_key(location& loc); template result, std::string> parse_inline_table(location& loc, const std::size_t n_rec); // The following toml file is allowed. // ```toml // [a.b.c] # here, table `a` has element `b`. // foo = "bar" // [a] # merge a = {baz = "qux"} to a = {b = {...}} // baz = "qux" // ``` // But the following is not allowed. // ```toml // [a] // b.c.foo = "bar" // [a] # error! the same table [a] defined! // baz = "qux" // ``` // The following is neither allowed. // ```toml // a = { b.c.foo = "bar"} // [a] # error! the same table [a] defined! // baz = "qux" // ``` // Here, it parses region of `tab->at(k)` as a table key and check the depth // of the key. If the key region points deeper node, it would be allowed. // Otherwise, the key points the same node. It would be rejected. template bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, Iterator key_first, Iterator key_curr, Iterator key_last) { // ------------------------------------------------------------------------ // check type of the value to be inserted/merged std::string inserting_reg = ""; if(const auto ptr = detail::get_region(inserting)) { inserting_reg = ptr->str(); } location inserting_def("internal", std::move(inserting_reg)); if(const auto inlinetable = parse_inline_table(inserting_def, 0)) { // check if we are overwriting existing table. // ```toml // # NG // a.b = 42 // a = {d = 3.14} // ``` // Inserting an inline table to a existing super-table is not allowed in // any case. If we found it, we can reject it without further checking. return false; } // Valid and invalid cases when inserting to the [a.b] table: // // ## Invalid // // ```toml // # invalid // [a] // b.c.d = "foo" // [a.b] # a.b is already defined and closed // d = "bar" // ``` // ```toml // # invalid // a = {b.c.d = "foo"} // [a.b] # a is already defined and inline table is closed // d = "bar" // ``` // ```toml // # invalid // a.b.c.d = "foo" // [a.b] # a.b is already defined and dotted-key table is closed // d = "bar" // ``` // // ## Valid // // ```toml // # OK. a.b is defined, but is *overwritable* // [a.b.c] // d = "foo" // [a.b] // d = "bar" // ``` // ```toml // # OK. a.b is defined, but is *overwritable* // [a] // b.c.d = "foo" // b.e = "bar" // ``` // ------------------------------------------------------------------------ // check table defined before std::string internal = ""; if(const auto ptr = detail::get_region(fwd)) { internal = ptr->str(); } location def("internal", std::move(internal)); if(const auto tabkeys = parse_table_key(def)) // [table.key] { // table keys always contains all the nodes from the root. const auto& tks = tabkeys.unwrap().first; if(std::size_t(std::distance(key_first, key_last)) == tks.size() && std::equal(tks.begin(), tks.end(), key_first)) { // the keys are equivalent. it is not allowed. return false; } // the keys are not equivalent. it is allowed. return true; } // nested array-of-table definition implicitly defines tables. // those tables can be reopened. if(const auto atabkeys = parse_array_table_key(def)) { // table keys always contains all the nodes from the root. const auto& tks = atabkeys.unwrap().first; if(std::size_t(std::distance(key_first, key_last)) == tks.size() && std::equal(tks.begin(), tks.end(), key_first)) { // the keys are equivalent. it is not allowed. return false; } // the keys are not equivalent. it is allowed. return true; } if(const auto dotkeys = parse_key(def)) // a.b.c = "foo" { // consider the following case. // [a] // b.c = {d = 42} // [a.b.c] // e = 2.71 // this defines the table [a.b.c] twice. no? if(const auto reopening_dotkey_by_table = parse_table_key(inserting_def)) { // re-opening a dotkey-defined table by a table is invalid. // only dotkey can append a key-val. Like: // ```toml // a.b.c = "foo" // a.b.d = "bar" # OK. reopen `a.b` by dotkey // [a.b] // e = "bar" # Invalid. re-opening `a.b` by [a.b] is not allowed. // ``` return false; } // a dotted key starts from the node representing a table in which the // dotted key belongs to. const auto& dks = dotkeys.unwrap().first; if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && std::equal(dks.begin(), dks.end(), key_curr)) { // the keys are equivalent. it is not allowed. return false; } // the keys are not equivalent. it is allowed. return true; } return false; } template result insert_nested_key(typename Value::table_type& root, const Value& v, InputIterator iter, const InputIterator last, region key_reg, const bool is_array_of_table = false) { static_assert(std::is_same::value_type>::value,""); using value_type = Value; using table_type = typename value_type::table_type; using array_type = typename value_type::array_type; const auto first = iter; assert(iter != last); table_type* tab = std::addressof(root); for(; iter != last; ++iter) // search recursively { const key& k = *iter; if(std::next(iter) == last) // k is the last key { // XXX if the value is array-of-tables, there can be several // tables that are in the same array. in that case, we need to // find the last element and insert it to there. if(is_array_of_table) { if(tab->count(k) == 1) // there is already an array of table { if(tab->at(k).is_table()) { // show special err msg for conflicting table throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") cannot be defined"), { {tab->at(k).location(), "table already defined"}, {v.location(), "this conflicts with the previous table"} }), v.location()); } else if(!(tab->at(k).is_array())) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" " existing value"), { {tab->at(k).location(), concat_to_string("this ", tab->at(k).type(), " value already exists")}, {v.location(), "while inserting this array-of-tables"} }), v.location()); } // the above if-else-if checks tab->at(k) is an array auto& a = tab->at(k).as_array(); // If table element is defined as [[array_of_tables]], it // cannot be an empty array. If an array of tables is // defined as `aot = []`, it cannot be appended. if(a.empty() || !(a.front().is_table())) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" " existing value"), { {tab->at(k).location(), concat_to_string("this ", tab->at(k).type(), " value already exists")}, {v.location(), "while inserting this array-of-tables"} }), v.location()); } // avoid conflicting array of table like the following. // ```toml // a = [{b = 42}] # define a as an array of *inline* tables // [[a]] # a is an array of *multi-line* tables // b = 54 // ``` // Here, from the type information, these cannot be detected // because inline table is also a table. // But toml v0.5.0 explicitly says it is invalid. The above // array-of-tables has a static size and appending to the // array is invalid. // In this library, multi-line table value has a region // that points to the key of the table (e.g. [[a]]). By // comparing the first two letters in key, we can detect // the array-of-table is inline or multiline. if(const auto ptr = detail::get_region(a.front())) { if(ptr->str().substr(0,2) != "[[") { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides " "with existing array-of-tables"), { {tab->at(k).location(), concat_to_string("this ", tab->at(k).type(), " value has static size")}, {v.location(), "appending it to the statically sized array"} }), v.location()); } } a.push_back(v); return ok(true); } else // if not, we need to create the array of table { // XXX: Consider the following array of tables. // ```toml // # This is a comment. // [[aot]] // foo = "bar" // ``` // Here, the comment is for `aot`. But here, actually two // values are defined. An array that contains tables, named // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. // Those two are different from each other. But both of them // points to the same portion of the TOML file, `[[aot]]`, // so `key_reg.comments()` returns `# This is a comment`. // If it is assigned as a comment of `aot` defined here, the // comment will be duplicated. Both the `aot` itself and // the 0-th element will have the same comment. This causes // "duplication of the same comments" bug when the data is // serialized. // Next, consider the following. // ```toml // # comment 1 // aot = [ // # comment 2 // {foo = "bar"}, // ] // ``` // In this case, we can distinguish those two comments. So // here we need to add "comment 1" to the `aot` and // "comment 2" to the 0th element of that. // To distinguish those two, we check the key region. std::vector comments{/* empty by default */}; if(key_reg.str().substr(0, 2) != "[[") { comments = key_reg.comments(); } value_type aot(array_type(1, v), key_reg, std::move(comments)); tab->insert(std::make_pair(k, aot)); return ok(true); } } // end if(array of table) if(tab->count(k) == 1) { if(tab->at(k).is_table() && v.is_table()) { if(!is_valid_forward_table_definition( tab->at(k), v, first, iter, last)) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: table (\"", format_dotted_keys(first, last), "\") already exists."), { {tab->at(k).location(), "table already exists here"}, {v.location(), "table defined twice"} }), v.location()); } // to allow the following toml file. // [a.b.c] // d = 42 // [a] // e = 2.71 auto& t = tab->at(k).as_table(); for(const auto& kv : v.as_table()) { if(tab->at(k).contains(kv.first)) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: value (\"", format_dotted_keys(first, last), "\") already exists."), { {t.at(kv.first).location(), "already exists here"}, {v.location(), "this defined twice"} }), v.location()); } t[kv.first] = kv.second; } detail::change_region(tab->at(k), key_reg); return ok(true); } else if(v.is_table() && tab->at(k).is_array() && tab->at(k).as_array().size() > 0 && tab->at(k).as_array().front().is_table()) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of tables (\"", format_dotted_keys(first, last), "\") already exists."), { {tab->at(k).location(), "array of tables defined here"}, {v.location(), "table conflicts with the previous array of table"} }), v.location()); } else { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: value (\"", format_dotted_keys(first, last), "\") already exists."), { {tab->at(k).location(), "value already exists here"}, {v.location(), "value defined twice"} }), v.location()); } } tab->insert(std::make_pair(k, v)); return ok(true); } else // k is not the last one, we should insert recursively { // if there is no corresponding value, insert it first. // related: you don't need to write // # [x] // # [x.y] // to write // [x.y.z] if(tab->count(k) == 0) { // a table that is defined implicitly doesn't have any comments. (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); } // type checking... if(tab->at(k).is_table()) { // According to toml-lang/toml:36d3091b3 "Clarify that inline // tables are immutable", check if it adds key-value pair to an // inline table. if(const auto* ptr = get_region(tab->at(k))) { // here, if the value is a (multi-line) table, the region // should be something like `[table-name]`. if(ptr->front() == '{') { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: inserting to an inline table (", format_dotted_keys(first, std::next(iter)), ") but inline tables are immutable"), { {tab->at(k).location(), "inline tables are immutable"}, {v.location(), "inserting this"} }), v.location()); } } tab = std::addressof((*tab)[k].as_table()); } else if(tab->at(k).is_array()) // inserting to array-of-tables? { auto& a = (*tab)[k].as_array(); if(!a.back().is_table()) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), ") is neither table nor an array of tables"), { {a.back().location(), concat_to_string( "actual type is ", a.back().type())}, {v.location(), "inserting this"} }), v.location()); } if(a.empty()) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: table (\"", format_dotted_keys(first, last), "\") conflicts with" " existing value"), { {tab->at(k).location(), std::string("this array is not insertable")}, {v.location(), std::string("appending it to the statically sized array")} }), v.location()); } if(const auto ptr = detail::get_region(a.at(0))) { if(ptr->str().substr(0,2) != "[[") { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: a table (\"", format_dotted_keys(first, last), "\") cannot be " "inserted to an existing inline array-of-tables"), { {tab->at(k).location(), std::string("this array of table has a static size")}, {v.location(), std::string("appending it to the statically sized array")} }), v.location()); } } tab = std::addressof(a.back().as_table()); } else { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), ") is neither table nor an array of tables"), { {tab->at(k).location(), concat_to_string( "actual type is ", tab->at(k).type())}, {v.location(), "inserting this"} }), v.location()); } } } return err(std::string("toml::detail::insert_nested_key: never reach here")); } template result, std::string> parse_inline_table(location& loc, const std::size_t n_rec) { using value_type = Value; using table_type = typename value_type::table_type; if(n_rec > TOML11_VALUE_RECURSION_LIMIT) { throw syntax_error(std::string("toml::parse_inline_table: recursion limit (" TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), source_location(loc)); } const auto first = loc.iter(); table_type retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { return err(format_underline("toml::parse_inline_table: ", {{source_location(loc), "the next token is not an inline table"}})); } loc.advance(); // check if the inline table is an empty table = { } maybe::invoke(loc); if(loc.iter() != loc.end() && *loc.iter() == '}') { loc.advance(); // skip `}` return ok(std::make_pair(retval, region(loc, first, loc.iter()))); } // it starts from "{". it should be formatted as inline-table while(loc.iter() != loc.end()) { const auto kv_r = parse_key_value_pair(loc, n_rec+1); if(!kv_r) { return err(kv_r.unwrap_err()); } const auto& kvpair = kv_r.unwrap(); const std::vector& keys = kvpair.first.first; const auto& key_reg = kvpair.first.second; const value_type& val = kvpair.second; const auto inserted = insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); if(!inserted) { throw internal_error("toml::parse_inline_table: " "failed to insert value into table: " + inserted.unwrap_err(), source_location(loc)); } using lex_table_separator = sequence, character<','>>; const auto sp = lex_table_separator::invoke(loc); if(!sp) { maybe::invoke(loc); if(loc.iter() == loc.end()) { throw syntax_error(format_underline( "toml::parse_inline_table: missing table separator `}` ", {{source_location(loc), "should be `}`"}}), source_location(loc)); } else if(*loc.iter() == '}') { loc.advance(); // skip `}` return ok(std::make_pair( retval, region(loc, first, loc.iter()))); } else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') { throw syntax_error(format_underline( "toml::parse_inline_table: missing curly brace `}`", {{source_location(loc), "should be `}`"}}), source_location(loc)); } else { throw syntax_error(format_underline( "toml::parse_inline_table: missing table separator `,` ", {{source_location(loc), "should be `,`"}}), source_location(loc)); } } else // `,` is found { maybe::invoke(loc); if(loc.iter() != loc.end() && *loc.iter() == '}') { throw syntax_error(format_underline( "toml::parse_inline_table: trailing comma is not allowed in" " an inline table", {{source_location(loc), "should be `}`"}}), source_location(loc)); } } } loc.reset(first); throw syntax_error(format_underline("toml::parse_inline_table: " "inline table did not closed by `}`", {{source_location(loc), "should be closed"}}), source_location(loc)); } inline result guess_number_type(const location& l) { // This function tries to find some (common) mistakes by checking characters // that follows the last character of a value. But it is often difficult // because some non-newline characters can appear after a value. E.g. // spaces, tabs, commas (in an array or inline table), closing brackets // (of an array or inline table), comment-sign (#). Since this function // does not parse further, those characters are always allowed to be there. location loc = l; if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} loc.reset(l.iter()); if(lex_local_date_time::invoke(loc)) { // bad offset may appear after this. if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' || *loc.iter() == 'Z' || *loc.iter() == 'z')) { return err(format_underline("bad offset: should be [+-]HH:MM or Z", {{source_location(loc), "[+-]HH:MM or Z"}}, {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); } return ok(value_t::local_datetime); } loc.reset(l.iter()); if(lex_local_date::invoke(loc)) { // bad time may appear after this. // A space is allowed as a delimiter between local time. But there are // both cases in which a space becomes valid or invalid. // - invalid: 2019-06-16 7:00:00 // - valid : 2019-06-16 07:00:00 if(loc.iter() != loc.end()) { const auto c = *loc.iter(); if(c == 'T' || c == 't') { return err(format_underline("bad time: should be HH:MM:SS.subsec", {{source_location(loc), "HH:MM:SS.subsec"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); } if('0' <= c && c <= '9') { return err(format_underline("bad time: missing T", {{source_location(loc), "T or space required here"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); } if(c == ' ' && std::next(loc.iter()) != loc.end() && ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) { loc.advance(); return err(format_underline("bad time: should be HH:MM:SS.subsec", {{source_location(loc), "HH:MM:SS.subsec"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); } } return ok(value_t::local_date); } loc.reset(l.iter()); if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} loc.reset(l.iter()); if(lex_float::invoke(loc)) { if(loc.iter() != loc.end() && *loc.iter() == '_') { return err(format_underline("bad float: `_` should be surrounded by digits", {{source_location(loc), "here"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); } return ok(value_t::floating); } loc.reset(l.iter()); if(lex_integer::invoke(loc)) { if(loc.iter() != loc.end()) { const auto c = *loc.iter(); if(c == '_') { return err(format_underline("bad integer: `_` should be surrounded by digits", {{source_location(loc), "here"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); } if('0' <= c && c <= '9') { // leading zero. point '0' loc.retrace(); return err(format_underline("bad integer: leading zero", {{source_location(loc), "here"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); } if(c == ':' || c == '-') { return err(format_underline("bad datetime: invalid format", {{source_location(loc), "here"}}, {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); } if(c == '.' || c == 'e' || c == 'E') { return err(format_underline("bad float: invalid format", {{source_location(loc), "here"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); } } return ok(value_t::integer); } if(loc.iter() != loc.end() && *loc.iter() == '.') { return err(format_underline("bad float: invalid format", {{source_location(loc), "integer part required before this"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); } if(loc.iter() != loc.end() && *loc.iter() == '_') { return err(format_underline("bad number: `_` should be surrounded by digits", {{source_location(loc), "`_` is not surrounded by digits"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); } return err(format_underline("bad format: unknown value appeared", {{source_location(loc), "here"}})); } inline result guess_value_type(const location& loc) { switch(*loc.iter()) { case '"' : {return ok(value_t::string); } case '\'': {return ok(value_t::string); } case 't' : {return ok(value_t::boolean); } case 'f' : {return ok(value_t::boolean); } case '[' : {return ok(value_t::array); } case '{' : {return ok(value_t::table); } case 'i' : {return ok(value_t::floating);} // inf. case 'n' : {return ok(value_t::floating);} // nan. default : {return guess_number_type(loc);} } } template result parse_value_helper(result, std::string> rslt) { if(rslt.is_ok()) { auto comments = rslt.as_ok().second.comments(); return ok(Value(std::move(rslt.as_ok()), std::move(comments))); } else { return err(std::move(rslt.as_err())); } } template result parse_value(location& loc, const std::size_t n_rec) { const auto first = loc.iter(); if(first == loc.end()) { return err(format_underline("toml::parse_value: input is empty", {{source_location(loc), ""}})); } const auto type = guess_value_type(loc); if(!type) { return err(type.unwrap_err()); } switch(type.unwrap()) { case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} case value_t::integer : {return parse_value_helper(parse_integer(loc) );} case value_t::floating : {return parse_value_helper(parse_floating(loc) );} case value_t::string : {return parse_value_helper(parse_string(loc) );} case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} case value_t::array : {return parse_value_helper(parse_array(loc, n_rec));} case value_t::table : {return parse_value_helper(parse_inline_table(loc, n_rec));} default: { const auto msg = format_underline("toml::parse_value: " "unknown token appeared", {{source_location(loc), "unknown"}}); loc.reset(first); return err(msg); } } } inline result, region>, std::string> parse_table_key(location& loc) { if(auto token = lex_std_table::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto open = lex_std_table_open::invoke(inner_loc); if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "toml::parse_table_key: no `[`", {{source_location(inner_loc), "should be `[`"}}), source_location(inner_loc)); } // to skip [ a . b . c ] // ^----------- this whitespace lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline( "toml::parse_table_key: invalid key", {{source_location(inner_loc), "not key"}}), source_location(inner_loc)); } // to skip [ a . b . c ] // ^-- this whitespace lex_ws::invoke(inner_loc); const auto close = lex_std_table_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline( "toml::parse_table_key: no `]`", {{source_location(inner_loc), "should be `]`"}}), source_location(inner_loc)); } // after [table.key], newline or EOF(empty table) required. if(loc.iter() != loc.end()) { using lex_newline_after_table_key = sequence, maybe, lex_newline>; const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { throw syntax_error(format_underline( "toml::parse_table_key: newline required after [table.key]", {{source_location(loc), "expected newline"}}), source_location(loc)); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); } else { return err(format_underline("toml::parse_table_key: " "not a valid table key", {{source_location(loc), "here"}})); } } inline result, region>, std::string> parse_array_table_key(location& loc) { if(auto token = lex_array_table::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); const auto open = lex_array_table_open::invoke(inner_loc); if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline( "toml::parse_array_table_key: no `[[`", {{source_location(inner_loc), "should be `[[`"}}), source_location(inner_loc)); } lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline( "toml::parse_array_table_key: invalid key", {{source_location(inner_loc), "not a key"}}), source_location(inner_loc)); } lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline( "toml::parse_array_table_key: no `]]`", {{source_location(inner_loc), "should be `]]`"}}), source_location(inner_loc)); } // after [[table.key]], newline or EOF(empty table) required. if(loc.iter() != loc.end()) { using lex_newline_after_table_key = sequence, maybe, lex_newline>; const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { throw syntax_error(format_underline("toml::" "parse_array_table_key: newline required after [[table.key]]", {{source_location(loc), "expected newline"}}), source_location(loc)); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); } else { return err(format_underline("toml::parse_array_table_key: " "not a valid table key", {{source_location(loc), "here"}})); } } // parse table body (key-value pairs until the iter hits the next [tablekey]) template result parse_ml_table(location& loc) { using value_type = Value; using table_type = typename value_type::table_type; const auto first = loc.iter(); if(first == loc.end()) { return ok(table_type{}); } // XXX at lest one newline is needed. using skip_line = repeat< sequence, maybe, lex_newline>, at_least<1>>; skip_line::invoke(loc); lex_ws::invoke(loc); table_type tab; while(loc.iter() != loc.end()) { lex_ws::invoke(loc); const auto before = loc.iter(); if(const auto tmp = parse_array_table_key(loc)) // next table found { loc.reset(before); return ok(tab); } if(const auto tmp = parse_table_key(loc)) // next table found { loc.reset(before); return ok(tab); } if(const auto kv = parse_key_value_pair(loc, 0)) { const auto& kvpair = kv.unwrap(); const std::vector& keys = kvpair.first.first; const auto& key_reg = kvpair.first.second; const value_type& val = kvpair.second; const auto inserted = insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); if(!inserted) { return err(inserted.unwrap_err()); } } else { return err(kv.unwrap_err()); } // comment lines are skipped by the above function call. // However, since the `skip_line` requires at least 1 newline, it fails // if the file ends with ws and/or comment without newline. // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` // itself. To skip the last ws and/or comment, call lexers. // It does not matter if these fails, so the return value is discarded. lex_ws::invoke(loc); lex_comment::invoke(loc); // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines // and comments after the last key-value pairs are allowed. const auto newline = skip_line::invoke(loc); if(!newline && loc.iter() != loc.end()) { const auto before2 = loc.iter(); lex_ws::invoke(loc); // skip whitespace const auto msg = format_underline("toml::parse_table: " "invalid line format", {{source_location(loc), concat_to_string( "expected newline, but got '", show_char(*loc.iter()), "'.")}}); loc.reset(before2); return err(msg); } // the skip_lines only matches with lines that includes newline. // to skip the last line that includes comment and/or whitespace // but no newline, call them one more time. lex_ws::invoke(loc); lex_comment::invoke(loc); } return ok(tab); } template result parse_toml_file(location& loc) { using value_type = Value; using table_type = typename value_type::table_type; const auto first = loc.iter(); if(first == loc.end()) { // For empty files, return an empty table with an empty region (zero-length). // Without the region, error messages would miss the filename. return ok(value_type(table_type{}, region(loc, first, first), {})); } // put the first line as a region of a file // Here first != loc.end(), so taking std::next is okay const region file(loc, first, std::next(loc.iter())); // The first successive comments that are separated from the first value // by an empty line are for a file itself. // ```toml // # this is a comment for a file. // // key = "the first value" // ``` // ```toml // # this is a comment for "the first value". // key = "the first value" // ``` std::vector comments; using lex_first_comments = sequence< repeat, lex_comment, lex_newline>, at_least<1>>, sequence, lex_newline> >; if(const auto token = lex_first_comments::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); while(inner_loc.iter() != inner_loc.end()) { maybe::invoke(inner_loc); // remove ws if exists if(lex_newline::invoke(inner_loc)) { assert(inner_loc.iter() == inner_loc.end()); break; // empty line found. } auto com = lex_comment::invoke(inner_loc).unwrap().str(); com.erase(com.begin()); // remove # sign comments.push_back(std::move(com)); lex_newline::invoke(inner_loc); } } table_type data; // root object is also a table, but without [tablename] if(const auto tab = parse_ml_table(loc)) { data = std::move(tab.unwrap()); } else // failed (empty table is regarded as success in parse_ml_table) { return err(tab.unwrap_err()); } while(loc.iter() != loc.end()) { // here, the region of [table] is regarded as the table-key because // the table body is normally too big and it is not so informative // if the first key-value pair of the table is shown in the error // message. if(const auto tabkey = parse_array_table_key(loc)) { const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} const auto& tk = tabkey.unwrap(); const auto& keys = tk.first; const auto& reg = tk.second; const auto inserted = insert_nested_key(data, value_type(tab.unwrap(), reg, reg.comments()), keys.begin(), keys.end(), reg, /*is_array_of_table=*/ true); if(!inserted) {return err(inserted.unwrap_err());} continue; } if(const auto tabkey = parse_table_key(loc)) { const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} const auto& tk = tabkey.unwrap(); const auto& keys = tk.first; const auto& reg = tk.second; const auto inserted = insert_nested_key(data, value_type(tab.unwrap(), reg, reg.comments()), keys.begin(), keys.end(), reg); if(!inserted) {return err(inserted.unwrap_err());} continue; } return err(format_underline("toml::parse_toml_file: " "unknown line appeared", {{source_location(loc), "unknown format"}})); } return ok(Value(std::move(data), file, comments)); } template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(std::vector& letters, const std::string& fname) { using value_type = basic_value; // append LF. // Although TOML does not require LF at the EOF, to make parsing logic // simpler, we "normalize" the content by adding LF if it does not exist. // It also checks if the last char is CR, to avoid changing the meaning. // This is not the *best* way to deal with the last character, but is a // simple and quick fix. if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') { letters.push_back('\n'); } detail::location loc(std::move(fname), std::move(letters)); // skip BOM if exists. // XXX component of BOM (like 0xEF) exceeds the representable range of // signed char, so on some (actually, most) of the environment, these cannot // be compared to char. However, since we are always out of luck, we need to // check our chars are equivalent to BOM. To do this, first we need to // convert char to unsigned char to guarantee the comparability. if(loc.source()->size() >= 3) { std::array BOM; std::memcpy(BOM.data(), loc.source()->data(), 3); if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) { loc.advance(3); // BOM found. skip. } } if (auto data = detail::parse_toml_file(loc)) { return std::move(data).unwrap(); } else { throw syntax_error(std::move(data).unwrap_err(), source_location(loc)); } } } // detail template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(FILE * file, const std::string& fname) { const long beg = std::ftell(file); if (beg == -1l) { throw file_io_error(errno, "Failed to access", fname); } const int res_seekend = std::fseek(file, 0, SEEK_END); if (res_seekend != 0) { throw file_io_error(errno, "Failed to seek", fname); } const long end = std::ftell(file); if (end == -1l) { throw file_io_error(errno, "Failed to access", fname); } const auto fsize = end - beg; const auto res_seekbeg = std::fseek(file, beg, SEEK_SET); if (res_seekbeg != 0) { throw file_io_error(errno, "Failed to seek", fname); } // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); std::fread(letters.data(), sizeof(char), static_cast(fsize), file); return detail::parse(letters, fname); } template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(std::istream& is, std::string fname = "unknown file") { const auto beg = is.tellg(); is.seekg(0, std::ios::end); const auto end = is.tellg(); const auto fsize = end - beg; is.seekg(beg); // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); is.read(letters.data(), fsize); return detail::parse(letters, fname); } template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(std::string fname) { std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { throw std::ios_base::failure( "toml::parse: Error opening file \"" + fname + "\""); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, std::move(fname)); } #ifdef TOML11_HAS_STD_FILESYSTEM // This function just forwards `parse("filename.toml")` to std::string version // to avoid the ambiguity in overload resolution. // // Both std::string and std::filesystem::path are convertible from const char*. // Without this, both parse(std::string) and parse(std::filesystem::path) // matches to parse("filename.toml"). This breaks the existing code. // // This function exactly matches to the invocation with c-string. // So this function is preferred than others and the ambiguity disappears. template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(const char* fname) { return parse(std::string(fname)); } template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(const std::filesystem::path& fpath) { std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { throw std::ios_base::failure( "toml::parse: Error opening file \"" + fpath.string() + "\""); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, fpath.string()); } #endif // TOML11_HAS_STD_FILESYSTEM } // toml #endif// TOML11_PARSER_HPP toml11-3.8.1/toml/region.hpp000066400000000000000000000351371454646465200156400ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_REGION_HPP #define TOML11_REGION_HPP #include #include #include #include #include #include #include #include "color.hpp" namespace toml { namespace detail { // helper function to avoid std::string(0, 'c') or std::string(iter, iter) template std::string make_string(Iterator first, Iterator last) { if(first == last) {return "";} return std::string(first, last); } inline std::string make_string(std::size_t len, char c) { if(len == 0) {return "";} return std::string(len, c); } // region_base is a base class of location and region that are defined below. // it will be used to generate better error messages. struct region_base { region_base() = default; virtual ~region_base() = default; region_base(const region_base&) = default; region_base(region_base&& ) = default; region_base& operator=(const region_base&) = default; region_base& operator=(region_base&& ) = default; virtual bool is_ok() const noexcept {return false;} virtual char front() const noexcept {return '\0';} virtual std::string str() const {return std::string("unknown region");} virtual std::string name() const {return std::string("unknown file");} virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} // length of the region virtual std::size_t size() const noexcept {return 0;} // number of characters in the line before the region virtual std::size_t before() const noexcept {return 0;} // number of characters in the line after the region virtual std::size_t after() const noexcept {return 0;} virtual std::vector comments() const {return {};} // ```toml // # comment_before // key = "value" # comment_inline // ``` }; // location represents a position in a container, which contains a file content. // it can be considered as a region that contains only one character. // // it contains pointer to the file content and iterator that points the current // location. struct location final : public region_base { using const_iterator = typename std::vector::const_iterator; using difference_type = typename std::iterator_traits::difference_type; using source_ptr = std::shared_ptr>; location(std::string source_name, std::vector cont) : source_(std::make_shared>(std::move(cont))), line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) {} location(std::string source_name, const std::string& cont) : source_(std::make_shared>(cont.begin(), cont.end())), line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) {} location(const location&) = default; location(location&&) = default; location& operator=(const location&) = default; location& operator=(location&&) = default; ~location() = default; bool is_ok() const noexcept override {return static_cast(source_);} char front() const noexcept override {return *iter_;} // this const prohibits codes like `++(loc.iter())`. std::add_const::type iter() const noexcept {return iter_;} const_iterator begin() const noexcept {return source_->cbegin();} const_iterator end() const noexcept {return source_->cend();} // XXX `location::line_num()` used to be implemented using `std::count` to // count a number of '\n'. But with a long toml file (typically, 10k lines), // it becomes intolerably slow because each time it generates error messages, // it counts '\n' from thousands of characters. To workaround it, I decided // to introduce `location::line_number_` member variable and synchronize it // to the location changes the point to look. So an overload of `iter()` // which returns mutable reference is removed and `advance()`, `retrace()` // and `reset()` is added. void advance(difference_type n = 1) noexcept { this->line_number_ += static_cast( std::count(this->iter_, std::next(this->iter_, n), '\n')); this->iter_ += n; return; } void retrace(difference_type n = 1) noexcept { this->line_number_ -= static_cast( std::count(std::prev(this->iter_, n), this->iter_, '\n')); this->iter_ -= n; return; } void reset(const_iterator rollback) noexcept { // since c++11, std::distance works in both ways for random-access // iterators and returns a negative value if `first > last`. if(0 <= std::distance(rollback, this->iter_)) // rollback < iter { this->line_number_ -= static_cast( std::count(rollback, this->iter_, '\n')); } else // iter < rollback [[unlikely]] { this->line_number_ += static_cast( std::count(this->iter_, rollback, '\n')); } this->iter_ = rollback; return; } std::string str() const override {return make_string(1, *this->iter());} std::string name() const override {return source_name_;} std::string line_num() const override { return std::to_string(this->line_number_); } std::string line() const override { return make_string(this->line_begin(), this->line_end()); } const_iterator line_begin() const noexcept { using reverse_iterator = std::reverse_iterator; return std::find(reverse_iterator(this->iter()), reverse_iterator(this->begin()), '\n').base(); } const_iterator line_end() const noexcept { return std::find(this->iter(), this->end(), '\n'); } // location is always points a character. so the size is 1. std::size_t size() const noexcept override { return 1u; } std::size_t before() const noexcept override { const auto sz = std::distance(this->line_begin(), this->iter()); assert(sz >= 0); return static_cast(sz); } std::size_t after() const noexcept override { const auto sz = std::distance(this->iter(), this->line_end()); assert(sz >= 0); return static_cast(sz); } source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} private: source_ptr source_; std::size_t line_number_; std::string source_name_; const_iterator iter_; }; // region represents a range in a container, which contains a file content. // // it contains pointer to the file content and iterator that points the first // and last location. struct region final : public region_base { using const_iterator = typename std::vector::const_iterator; using source_ptr = std::shared_ptr>; // delete default constructor. source_ never be null. region() = delete; explicit region(const location& loc) : source_(loc.source()), source_name_(loc.name()), first_(loc.iter()), last_(loc.iter()) {} explicit region(location&& loc) : source_(loc.source()), source_name_(loc.name()), first_(loc.iter()), last_(loc.iter()) {} region(const location& loc, const_iterator f, const_iterator l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) {} region(location&& loc, const_iterator f, const_iterator l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) {} region(const region&) = default; region(region&&) = default; region& operator=(const region&) = default; region& operator=(region&&) = default; ~region() = default; region& operator+=(const region& other) { // different regions cannot be concatenated assert(this->source_ == other.source_ && this->last_ == other.first_); this->last_ = other.last_; return *this; } bool is_ok() const noexcept override {return static_cast(source_);} char front() const noexcept override {return *first_;} std::string str() const override {return make_string(first_, last_);} std::string line() const override { if(this->contain_newline()) { return make_string(this->line_begin(), std::find(this->line_begin(), this->last(), '\n')); } return make_string(this->line_begin(), this->line_end()); } std::string line_num() const override { return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); } std::size_t size() const noexcept override { const auto sz = std::distance(first_, last_); assert(sz >= 0); return static_cast(sz); } std::size_t before() const noexcept override { const auto sz = std::distance(this->line_begin(), this->first()); assert(sz >= 0); return static_cast(sz); } std::size_t after() const noexcept override { const auto sz = std::distance(this->last(), this->line_end()); assert(sz >= 0); return static_cast(sz); } bool contain_newline() const noexcept { return std::find(this->first(), this->last(), '\n') != this->last(); } const_iterator line_begin() const noexcept { using reverse_iterator = std::reverse_iterator; return std::find(reverse_iterator(this->first()), reverse_iterator(this->begin()), '\n').base(); } const_iterator line_end() const noexcept { return std::find(this->last(), this->end(), '\n'); } const_iterator begin() const noexcept {return source_->cbegin();} const_iterator end() const noexcept {return source_->cend();} const_iterator first() const noexcept {return first_;} const_iterator last() const noexcept {return last_;} source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} std::string name() const override {return source_name_;} std::vector comments() const override { // assuming the current region (`*this`) points a value. // ```toml // a = "value" // ^^^^^^^- this region // ``` using rev_iter = std::reverse_iterator; std::vector com{}; { // find comments just before the current region. // ```toml // # this should be collected. // # this also. // a = value # not this. // ``` // # this is a comment for `a`, not array elements. // a = [1, 2, 3, 4, 5] if(this->first() == std::find_if(this->line_begin(), this->first(), [](const char c) noexcept -> bool {return c == '[' || c == '{';})) { auto iter = this->line_begin(); // points the first character while(iter != this->begin()) { iter = std::prev(iter); // range [line_start, iter) represents the previous line const auto line_start = std::find( rev_iter(iter), rev_iter(this->begin()), '\n').base(); const auto comment_found = std::find(line_start, iter, '#'); if(comment_found == iter) { break; // comment not found. } // exclude the following case. // > a = "foo" # comment // <-- this is not a comment for b but a. // > b = "current value" if(std::all_of(line_start, comment_found, [](const char c) noexcept -> bool { return c == ' ' || c == '\t'; })) { // unwrap the first '#' by std::next. auto s = make_string(std::next(comment_found), iter); if(!s.empty() && s.back() == '\r') {s.pop_back();} com.push_back(std::move(s)); } else { break; } iter = line_start; } } } if(com.size() > 1) { std::reverse(com.begin(), com.end()); } { // find comments just after the current region. // ```toml // # not this. // a = value # this one. // a = [ # not this (technically difficult) // // ] # and this. // ``` // The reason why it's difficult is that it requires parsing in the // following case. // ```toml // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. // # ... // ] # this is apparently a comment for a. // // b = [ // 3.14 ] # there is no way to add a comment to `3.14` currently. // // c = [ // 3.14 # do this if you need a comment here. // ] // ``` const auto comment_found = std::find(this->last(), this->line_end(), '#'); if(comment_found != this->line_end()) // '#' found { // table = {key = "value"} # what is this for? // the above comment is not for "value", but {key="value"}. if(comment_found == std::find_if(this->last(), comment_found, [](const char c) noexcept -> bool { return !(c == ' ' || c == '\t' || c == ','); })) { // unwrap the first '#' by std::next. auto s = make_string(std::next(comment_found), this->line_end()); if(!s.empty() && s.back() == '\r') {s.pop_back();} com.push_back(std::move(s)); } } } return com; } private: source_ptr source_; std::string source_name_; const_iterator first_, last_; }; } // detail } // toml #endif// TOML11_REGION_H toml11-3.8.1/toml/result.hpp000066400000000000000000000513051454646465200156660ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_RESULT_HPP #define TOML11_RESULT_HPP #include "traits.hpp" #include #include #include #include #include #include #include namespace toml { template struct success { using value_type = T; value_type value; explicit success(const value_type& v) noexcept(std::is_nothrow_copy_constructible::value) : value(v) {} explicit success(value_type&& v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template explicit success(U&& v): value(std::forward(v)) {} template explicit success(const success& v): value(v.value) {} template explicit success(success&& v): value(std::move(v.value)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; }; template struct failure { using value_type = T; value_type value; explicit failure(const value_type& v) noexcept(std::is_nothrow_copy_constructible::value) : value(v) {} explicit failure(value_type&& v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template explicit failure(U&& v): value(std::forward(v)) {} template explicit failure(const failure& v): value(v.value) {} template explicit failure(failure&& v): value(std::move(v.value)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; }; template success::type>::type> ok(T&& v) { return success< typename std::remove_cv::type>::type >(std::forward(v)); } template failure::type>::type> err(T&& v) { return failure< typename std::remove_cv::type>::type >(std::forward(v)); } inline success ok(const char* literal) { return success(std::string(literal)); } inline failure err(const char* literal) { return failure(std::string(literal)); } template struct result { using value_type = T; using error_type = E; using success_type = success; using failure_type = failure; result(const success_type& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(s); assert(tmp == std::addressof(this->succ)); (void)tmp; } result(const failure_type& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(f); assert(tmp == std::addressof(this->fail)); (void)tmp; } result(success_type&& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ)); (void)tmp; } result(failure_type&& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail)); (void)tmp; } template result(const success& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); assert(tmp == std::addressof(this->succ)); (void)tmp; } template result(const failure& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); assert(tmp == std::addressof(this->fail)); (void)tmp; } template result(success&& s): is_ok_(true) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ)); (void)tmp; } template result(failure&& f): is_ok_(false) { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail)); (void)tmp; } result& operator=(const success_type& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(s); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } result& operator=(const failure_type& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(f); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } result& operator=(success_type&& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } result& operator=(failure_type&& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } template result& operator=(const success& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } template result& operator=(const failure& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } template result& operator=(success&& s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ)); (void)tmp; return *this; } template result& operator=(failure&& f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail)); (void)tmp; return *this; } ~result() noexcept {this->cleanup();} result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } } result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } } template result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } } template result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } } result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } bool is_ok() const noexcept {return is_ok_;} bool is_err() const noexcept {return !is_ok_;} operator bool() const noexcept {return is_ok_;} value_type& unwrap() & { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; } value_type const& unwrap() const& { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; } value_type&& unwrap() && { if(is_err()) { throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return std::move(this->succ.value); } value_type& unwrap_or(value_type& opt) & { if(is_err()) {return opt;} return this->succ.value; } value_type const& unwrap_or(value_type const& opt) const& { if(is_err()) {return opt;} return this->succ.value; } value_type unwrap_or(value_type opt) && { if(is_err()) {return opt;} return this->succ.value; } error_type& unwrap_err() & { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type const& unwrap_err() const& { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type&& unwrap_err() && { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return std::move(this->fail.value); } value_type& as_ok() & noexcept {return this->succ.value;} value_type const& as_ok() const& noexcept {return this->succ.value;} value_type&& as_ok() && noexcept {return std::move(this->succ.value);} error_type& as_err() & noexcept {return this->fail.value;} error_type const& as_err() const& noexcept {return this->fail.value;} error_type&& as_err() && noexcept {return std::move(this->fail.value);} // prerequisities // F: T -> U // retval: result template result, error_type> map(F&& f) & { if(this->is_ok()){return ok(f(this->as_ok()));} return err(this->as_err()); } template result, error_type> map(F&& f) const& { if(this->is_ok()){return ok(f(this->as_ok()));} return err(this->as_err()); } template result, error_type> map(F&& f) && { if(this->is_ok()){return ok(f(std::move(this->as_ok())));} return err(std::move(this->as_err())); } // prerequisities // F: E -> F // retval: result template result> map_err(F&& f) & { if(this->is_err()){return err(f(this->as_err()));} return ok(this->as_ok()); } template result> map_err(F&& f) const& { if(this->is_err()){return err(f(this->as_err()));} return ok(this->as_ok()); } template result> map_err(F&& f) && { if(this->is_err()){return err(f(std::move(this->as_err())));} return ok(std::move(this->as_ok())); } // prerequisities // F: T -> U // retval: U template detail::return_type_of_t map_or_else(F&& f, U&& opt) & { if(this->is_err()){return std::forward(opt);} return f(this->as_ok()); } template detail::return_type_of_t map_or_else(F&& f, U&& opt) const& { if(this->is_err()){return std::forward(opt);} return f(this->as_ok()); } template detail::return_type_of_t map_or_else(F&& f, U&& opt) && { if(this->is_err()){return std::forward(opt);} return f(std::move(this->as_ok())); } // prerequisities // F: E -> U // retval: U template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) & { if(this->is_ok()){return std::forward(opt);} return f(this->as_err()); } template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) const& { if(this->is_ok()){return std::forward(opt);} return f(this->as_err()); } template detail::return_type_of_t map_err_or_else(F&& f, U&& opt) && { if(this->is_ok()){return std::forward(opt);} return f(std::move(this->as_err())); } // prerequisities: // F: func T -> U // toml::err(error_type) should be convertible to U. // normally, type U is another result and E is convertible to F template detail::return_type_of_t and_then(F&& f) & { if(this->is_ok()){return f(this->as_ok());} return err(this->as_err()); } template detail::return_type_of_t and_then(F&& f) const& { if(this->is_ok()){return f(this->as_ok());} return err(this->as_err()); } template detail::return_type_of_t and_then(F&& f) && { if(this->is_ok()){return f(std::move(this->as_ok()));} return err(std::move(this->as_err())); } // prerequisities: // F: func E -> U // toml::ok(value_type) should be convertible to U. // normally, type U is another result and T is convertible to S template detail::return_type_of_t or_else(F&& f) & { if(this->is_err()){return f(this->as_err());} return ok(this->as_ok()); } template detail::return_type_of_t or_else(F&& f) const& { if(this->is_err()){return f(this->as_err());} return ok(this->as_ok()); } template detail::return_type_of_t or_else(F&& f) && { if(this->is_err()){return f(std::move(this->as_err()));} return ok(std::move(this->as_ok())); } // if *this is error, returns *this. otherwise, returns other. result and_other(const result& other) const& { return this->is_err() ? *this : other; } result and_other(result&& other) && { return this->is_err() ? std::move(*this) : std::move(other); } // if *this is okay, returns *this. otherwise, returns other. result or_other(const result& other) const& { return this->is_ok() ? *this : other; } result or_other(result&& other) && { return this->is_ok() ? std::move(*this) : std::move(other); } void swap(result& other) { result tmp(std::move(*this)); *this = std::move(other); other = std::move(tmp); return ; } private: static std::string format_error(std::exception const& excpt) { return std::string(excpt.what()); } template::value, std::nullptr_t>::type = nullptr> static std::string format_error(U const& others) { std::ostringstream oss; oss << others; return oss.str(); } void cleanup() noexcept { if(this->is_ok_) {this->succ.~success_type();} else {this->fail.~failure_type();} return; } private: bool is_ok_; union { success_type succ; failure_type fail; }; }; template void swap(result& lhs, result& rhs) { lhs.swap(rhs); return; } // this might be confusing because it eagerly evaluated, while in the other // cases operator && and || are short-circuited. // // template // inline result // operator&&(const result& lhs, const result& rhs) noexcept // { // return lhs.is_ok() ? rhs : lhs; // } // // template // inline result // operator||(const result& lhs, const result& rhs) noexcept // { // return lhs.is_ok() ? lhs : rhs; // } // ---------------------------------------------------------------------------- // re-use result as a optional with none_t namespace detail { struct none_t {}; inline bool operator==(const none_t&, const none_t&) noexcept {return true;} inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} inline bool operator< (const none_t&, const none_t&) noexcept {return false;} inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} inline bool operator> (const none_t&, const none_t&) noexcept {return false;} inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} template std::basic_ostream& operator<<(std::basic_ostream& os, const none_t&) { os << "none"; return os; } inline failure none() noexcept {return failure{none_t{}};} } // detail } // toml11 #endif// TOML11_RESULT_H toml11-3.8.1/toml/serializer.hpp000066400000000000000000001020011454646465200165070ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP #include #include #include #if defined(_WIN32) #include #elif defined(__APPLE__) || defined(__FreeBSD__) #include #elif defined(__linux__) #include #endif #include "lexer.hpp" #include "value.hpp" namespace toml { // This function serialize a key. It checks a string is a bare key and // escapes special characters if the string is not compatible to a bare key. // ```cpp // std::string k("non.bare.key"); // the key itself includes `.`s. // std::string formatted = toml::format_key(k); // assert(formatted == "\"non.bare.key\""); // ``` // // This function is exposed to make it easy to write a user-defined serializer. // Since toml restricts characters available in a bare key, generally a string // should be escaped. But checking whether a string needs to be surrounded by // a `"` and escaping some special character is boring. template std::basic_string format_key(const std::basic_string& k) { if(k.empty()) { return std::string("\"\""); } // check the key can be a bare (unquoted) key detail::location loc(k, std::vector(k.begin(), k.end())); detail::lex_unquoted_key::invoke(loc); if(loc.iter() == loc.end()) { return k; // all the tokens are consumed. the key is unquoted-key. } //if it includes special characters, then format it in a "quoted" key. std::basic_string serialized("\""); for(const char c : k) { switch(c) { case '\\': {serialized += "\\\\"; break;} case '\"': {serialized += "\\\""; break;} case '\b': {serialized += "\\b"; break;} case '\t': {serialized += "\\t"; break;} case '\f': {serialized += "\\f"; break;} case '\n': {serialized += "\\n"; break;} case '\r': {serialized += "\\r"; break;} default: { if (c >= 0x00 && c < 0x20) { std::array buf; std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast(c)); serialized += buf.data(); } else { serialized += c; } break; } } } serialized += "\""; return serialized; } template std::basic_string format_keys(const std::vector>& keys) { if(keys.empty()) { return std::string("\"\""); } std::basic_string serialized; for(const auto& ky : keys) { serialized += format_key(ky); serialized += charT('.'); } serialized.pop_back(); // remove the last dot '.' return serialized; } template struct serializer { static_assert(detail::is_basic_value::value, "toml::serializer is for toml::value and its variants, " "toml::basic_value<...>."); using value_type = Value; using key_type = typename value_type::key_type ; using comment_type = typename value_type::comment_type ; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; serializer(const std::size_t w = 80u, const int float_prec = std::numeric_limits::max_digits10, const bool can_be_inlined = false, const bool no_comment = false, std::vector ks = {}, const bool value_has_comment = false) : can_be_inlined_(can_be_inlined), no_comment_(no_comment), value_has_comment_(value_has_comment && !no_comment), float_prec_(float_prec), width_(w), keys_(std::move(ks)) {} ~serializer() = default; std::string operator()(const boolean_type& b) const { return b ? "true" : "false"; } std::string operator()(const integer_type i) const { #if defined(_WIN32) _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); setlocale(LC_NUMERIC, "C"); #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); locale_t original_locale(0); if(c_locale != locale_t(0)) { original_locale = uselocale(c_locale); } #endif const auto str = std::to_string(i); #if defined(_WIN32) setlocale(LC_NUMERIC, original_locale.c_str()); _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) if(original_locale != locale_t(0)) { uselocale(original_locale); } #endif return str; } std::string operator()(const floating_type f) const { if(std::isnan(f)) { if(std::signbit(f)) { return std::string("-nan"); } else { return std::string("nan"); } } else if(!std::isfinite(f)) { if(std::signbit(f)) { return std::string("-inf"); } else { return std::string("inf"); } } // set locale to "C". // To make it thread-local, we use OS-specific features. // If we set process-global locale, it can break other thread that also // outputs something simultaneously. #if defined(_WIN32) _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); setlocale(LC_NUMERIC, "C"); #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); locale_t original_locale(0); if(c_locale != locale_t(0)) { original_locale = uselocale(c_locale); } #endif const auto fmt = "%.*g"; const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); // +1 for null character(\0) std::vector buf(static_cast(bsz + 1), '\0'); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); // restore the original locale #if defined(_WIN32) setlocale(LC_NUMERIC, original_locale.c_str()); _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) if(original_locale != locale_t(0)) { uselocale(original_locale); } #endif std::string token(buf.begin(), std::prev(buf.end())); if(!token.empty() && token.back() == '.') // 1. => 1.0 { token += '0'; } const auto e = std::find_if( token.cbegin(), token.cend(), [](const char c) noexcept -> bool { return c == 'e' || c == 'E'; }); const auto has_exponent = (token.cend() != e); const auto has_fraction = (token.cend() != std::find( token.cbegin(), token.cend(), '.')); if(!has_exponent && !has_fraction) { // the resulting value does not have any float specific part! token += ".0"; } return token; } std::string operator()(const string_type& s) const { if(s.kind == string_t::basic) { if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && this->width_ != (std::numeric_limits::max)()) { // if linefeed or double-quote is contained, // make it multiline basic string. const auto escaped = this->escape_ml_basic_string(s.str); std::string open("\"\"\""); std::string close("\"\"\""); if(escaped.find('\n') != std::string::npos || this->width_ < escaped.size() + 6) { // if the string body contains newline or is enough long, // add newlines after and before delimiters. open += "\n"; close = std::string("\\\n") + close; } return open + escaped + close; } // no linefeed. try to make it oneline-string. std::string oneline = this->escape_basic_string(s.str); if(oneline.size() + 2 < width_ || width_ < 2) { const std::string quote("\""); return quote + oneline + quote; } // the line is too long compared to the specified width. // split it into multiple lines. std::string token("\"\"\"\n"); while(!oneline.empty()) { if(oneline.size() < width_) { token += oneline; oneline.clear(); } else if(oneline.at(width_-2) == '\\') { token += oneline.substr(0, width_-2); token += "\\\n"; oneline.erase(0, width_-2); } else { token += oneline.substr(0, width_-1); token += "\\\n"; oneline.erase(0, width_-1); } } return token + std::string("\\\n\"\"\""); } else // the string `s` is literal-string. { if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) { std::string open("'''"); if(this->width_ + 6 < s.str.size()) { open += '\n'; // the first newline is ignored by TOML spec } const std::string close("'''"); return open + s.str + close; } else { const std::string quote("'"); return quote + s.str + quote; } } } std::string operator()(const local_date_type& d) const { std::ostringstream oss; oss << d; return oss.str(); } std::string operator()(const local_time_type& t) const { std::ostringstream oss; oss << t; return oss.str(); } std::string operator()(const local_datetime_type& dt) const { std::ostringstream oss; oss << dt; return oss.str(); } std::string operator()(const offset_datetime_type& odt) const { std::ostringstream oss; oss << odt; return oss.str(); } std::string operator()(const array_type& v) const { if(v.empty()) { return std::string("[]"); } if(this->is_array_of_tables(v)) { return make_array_of_tables(v); } // not an array of tables. normal array. // first, try to make it inline if none of the elements have a comment. if( ! this->has_comment_inside(v)) { const auto inl = this->make_inline_array(v); if(inl.size() < this->width_ && std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) { return inl; } } // if the length exceeds this->width_, print multiline array. // key = [ // # ... // 42, // ... // ] std::string token; std::string current_line; token += "[\n"; for(const auto& item : v) { if( ! item.comments().empty() && !no_comment_) { // if comment exists, the element must be the only element in the line. // e.g. the following is not allowed. // ```toml // array = [ // # comment for what? // 1, 2, 3, 4, 5 // ] // ``` if(!current_line.empty()) { if(current_line.back() != '\n') { current_line += '\n'; } token += current_line; current_line.clear(); } for(const auto& c : item.comments()) { token += '#'; token += c; token += '\n'; } token += toml::visit(*this, item); if(!token.empty() && token.back() == '\n') {token.pop_back();} token += ",\n"; continue; } std::string next_elem; if(item.is_table()) { serializer ser(*this); ser.can_be_inlined_ = true; ser.width_ = (std::numeric_limits::max)(); next_elem += toml::visit(ser, item); } else { next_elem += toml::visit(*this, item); } // comma before newline. if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} // if current line does not exceeds the width limit, continue. if(current_line.size() + next_elem.size() + 1 < this->width_) { current_line += next_elem; current_line += ','; } else if(current_line.empty()) { // if current line was empty, force put the next_elem because // next_elem is not splittable token += next_elem; token += ",\n"; // current_line is kept empty } else // reset current_line { assert(current_line.back() == ','); token += current_line; token += '\n'; current_line = next_elem; current_line += ','; } } if(!current_line.empty()) { if(!current_line.empty() && current_line.back() != '\n') { current_line += '\n'; } token += current_line; } token += "]\n"; return token; } // templatize for any table-like container std::string operator()(const table_type& v) const { // if an element has a comment, then it can't be inlined. // table = {# how can we write a comment for this? key = "value"} if(this->can_be_inlined_ && !(this->has_comment_inside(v))) { std::string token; if(!this->keys_.empty()) { token += format_key(this->keys_.back()); token += " = "; } token += this->make_inline_table(v); if(token.size() < this->width_ && token.end() == std::find(token.begin(), token.end(), '\n')) { return token; } } std::string token; if(!keys_.empty()) { token += '['; token += format_keys(keys_); token += "]\n"; } token += this->make_multiline_table(v); return token; } private: std::string escape_basic_string(const std::string& s) const { //XXX assuming `s` is a valid utf-8 sequence. std::string retval; for(const char c : s) { switch(c) { case '\\': {retval += "\\\\"; break;} case '\"': {retval += "\\\""; break;} case '\b': {retval += "\\b"; break;} case '\t': {retval += "\\t"; break;} case '\f': {retval += "\\f"; break;} case '\n': {retval += "\\n"; break;} case '\r': {retval += "\\r"; break;} default : { if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) { retval += "\\u00"; retval += char(48 + (c / 16)); retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); } else { retval += c; } } } } return retval; } std::string escape_ml_basic_string(const std::string& s) const { std::string retval; for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) { switch(*i) { case '\\': {retval += "\\\\"; break;} // One or two consecutive "s are allowed. // Later we will check there are no three consecutive "s. // case '\"': {retval += "\\\""; break;} case '\b': {retval += "\\b"; break;} case '\t': {retval += "\\t"; break;} case '\f': {retval += "\\f"; break;} case '\n': {retval += "\n"; break;} case '\r': { if(std::next(i) != e && *std::next(i) == '\n') { retval += "\r\n"; ++i; } else { retval += "\\r"; } break; } default : { const auto c = *i; if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) { retval += "\\u00"; retval += char(48 + (c / 16)); retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); } else { retval += c; } } } } // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. // 3 consecutive `"`s are considered as a closing delimiter. // We need to check if there are 3 or more consecutive `"`s and insert // backslash to break them down into several short `"`s like the `str6` // in the following example. // ```toml // str4 = """Here are two quotation marks: "". Simple enough.""" // # str5 = """Here are three quotation marks: """.""" # INVALID // str5 = """Here are three quotation marks: ""\".""" // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" // ``` auto found_3_quotes = retval.find("\"\"\""); while(found_3_quotes != std::string::npos) { retval.replace(found_3_quotes, 3, "\"\"\\\""); found_3_quotes = retval.find("\"\"\""); } return retval; } // if an element of a table or an array has a comment, it cannot be inlined. bool has_comment_inside(const array_type& a) const noexcept { // if no_comment is set, comments would not be written. if(this->no_comment_) {return false;} for(const auto& v : a) { if(!v.comments().empty()) {return true;} } return false; } bool has_comment_inside(const table_type& t) const noexcept { // if no_comment is set, comments would not be written. if(this->no_comment_) {return false;} for(const auto& kv : t) { if(!kv.second.comments().empty()) {return true;} } return false; } std::string make_inline_array(const array_type& v) const { assert(!has_comment_inside(v)); std::string token; token += '['; bool is_first = true; for(const auto& item : v) { if(is_first) {is_first = false;} else {token += ',';} token += visit(serializer( (std::numeric_limits::max)(), this->float_prec_, /* inlined */ true, /*no comment*/ false, /*keys*/ {}, /*has_comment*/ !item.comments().empty()), item); } token += ']'; return token; } std::string make_inline_table(const table_type& v) const { assert(!has_comment_inside(v)); assert(this->can_be_inlined_); std::string token; token += '{'; bool is_first = true; for(const auto& kv : v) { // in inline tables, trailing comma is not allowed (toml-lang #569). if(is_first) {is_first = false;} else {token += ',';} token += format_key(kv.first); token += '='; token += visit(serializer( (std::numeric_limits::max)(), this->float_prec_, /* inlined */ true, /*no comment*/ false, /*keys*/ {}, /*has_comment*/ !kv.second.comments().empty()), kv.second); } token += '}'; return token; } std::string make_multiline_table(const table_type& v) const { std::string token; // print non-table elements first. // ```toml // [foo] # a table we're writing now here // key = "value" # <- non-table element, "key" // # ... // [foo.bar] # <- table element, "bar" // ``` // because after printing [foo.bar], the remaining non-table values will // be assigned into [foo.bar], not [foo]. Those values should be printed // earlier. for(const auto& kv : v) { if(kv.second.is_table() || is_array_of_tables(kv.second)) { continue; } token += write_comments(kv.second); const auto key_and_sep = format_key(kv.first) + " = "; const auto residual_width = (this->width_ > key_and_sep.size()) ? this->width_ - key_and_sep.size() : 0; token += key_and_sep; token += visit(serializer(residual_width, this->float_prec_, /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, /*has_comment*/ !kv.second.comments().empty()), kv.second); if(token.back() != '\n') { token += '\n'; } } // normal tables / array of tables // after multiline table appeared, the other tables cannot be inline // because the table would be assigned into the table. // [foo] // ... // bar = {...} # <- bar will be a member of [foo]. bool multiline_table_printed = false; for(const auto& kv : v) { if(!kv.second.is_table() && !is_array_of_tables(kv.second)) { continue; // other stuff are already serialized. skip them. } std::vector ks(this->keys_); ks.push_back(kv.first); auto tmp = visit(serializer(this->width_, this->float_prec_, !multiline_table_printed, this->no_comment_, ks, /*has_comment*/ !kv.second.comments().empty()), kv.second); // If it is the first time to print a multi-line table, it would be // helpful to separate normal key-value pair and subtables by a // newline. // (this checks if the current key-value pair contains newlines. // but it is not perfect because multi-line string can also contain // a newline. in such a case, an empty line will be written) TODO if((!multiline_table_printed) && std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) { multiline_table_printed = true; token += '\n'; // separate key-value pairs and subtables token += write_comments(kv.second); token += tmp; // care about recursive tables (all tables in each level prints // newline and there will be a full of newlines) if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) { token += '\n'; } } else { token += write_comments(kv.second); token += tmp; token += '\n'; } } return token; } std::string make_array_of_tables(const array_type& v) const { // if it's not inlined, we need to add `[[table.key]]`. // but if it can be inlined, we can format it as the following. // ``` // table.key = [ // {...}, // # comment // {...}, // ] // ``` // This function checks if inlinization is possible or not, and then // format the array-of-tables in a proper way. // // Note about comments: // // If the array itself has a comment (value_has_comment_ == true), we // should try to make it inline. // ```toml // # comment about array // array = [ // # comment about table element // {of = "table"} // ] // ``` // If it is formatted as a multiline table, the two comments becomes // indistinguishable. // ```toml // # comment about array // # comment about table element // [[array]] // of = "table" // ``` // So we need to try to make it inline, and it force-inlines regardless // of the line width limit. // It may fail if the element of a table has comment. In that case, // the array-of-tables will be formatted as a multiline table. if(this->can_be_inlined_ || this->value_has_comment_) { std::string token; if(!keys_.empty()) { token += format_key(keys_.back()); token += " = "; } bool failed = false; token += "[\n"; for(const auto& item : v) { // if an element of the table has a comment, the table // cannot be inlined. if(this->has_comment_inside(item.as_table())) { failed = true; break; } // write comments for the table itself token += write_comments(item); const auto t = this->make_inline_table(item.as_table()); if(t.size() + 1 > width_ || // +1 for the last comma {...}, std::find(t.cbegin(), t.cend(), '\n') != t.cend()) { // if the value itself has a comment, ignore the line width limit if( ! this->value_has_comment_) { failed = true; break; } } token += t; token += ",\n"; } if( ! failed) { token += "]\n"; return token; } // if failed, serialize them as [[array.of.tables]]. } std::string token; for(const auto& item : v) { token += write_comments(item); token += "[["; token += format_keys(keys_); token += "]]\n"; token += this->make_multiline_table(item.as_table()); } return token; } std::string write_comments(const value_type& v) const { std::string retval; if(this->no_comment_) {return retval;} for(const auto& c : v.comments()) { retval += '#'; retval += c; retval += '\n'; } return retval; } bool is_array_of_tables(const value_type& v) const { if(!v.is_array() || v.as_array().empty()) {return false;} return is_array_of_tables(v.as_array()); } bool is_array_of_tables(const array_type& v) const { // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to // check all the element in an array to check if the array is an array // of tables. return std::all_of(v.begin(), v.end(), [](const value_type& elem) { return elem.is_table(); }); } private: bool can_be_inlined_; bool no_comment_; bool value_has_comment_; int float_prec_; std::size_t width_; std::vector keys_; }; template class M, template class V> std::string format(const basic_value& v, std::size_t w = 80u, int fprec = std::numeric_limits::max_digits10, bool no_comment = false, bool force_inline = false) { using value_type = basic_value; // if value is a table, it is considered to be a root object. // the root object can't be an inline table. if(v.is_table()) { std::ostringstream oss; if(!v.comments().empty()) { oss << v.comments(); oss << '\n'; // to split the file comment from the first element } const auto serialized = visit(serializer(w, fprec, false, no_comment), v); oss << serialized; return oss.str(); } return visit(serializer(w, fprec, force_inline), v); } namespace detail { template int comment_index(std::basic_ostream&) { static const int index = std::ios_base::xalloc(); return index; } } // detail template std::basic_ostream& nocomment(std::basic_ostream& os) { // by default, it is zero. and by default, it shows comments. os.iword(detail::comment_index(os)) = 1; return os; } template std::basic_ostream& showcomment(std::basic_ostream& os) { // by default, it is zero. and by default, it shows comments. os.iword(detail::comment_index(os)) = 0; return os; } template class M, template class V> std::basic_ostream& operator<<(std::basic_ostream& os, const basic_value& v) { using value_type = basic_value; // get status of std::setw(). const auto w = static_cast(os.width()); const int fprec = static_cast(os.precision()); os.width(0); // by default, iword is initialized by 0. And by default, toml11 outputs // comments. So `0` means showcomment. 1 means nocommnet. const bool no_comment = (1 == os.iword(detail::comment_index(os))); if(!no_comment && v.is_table() && !v.comments().empty()) { os << v.comments(); os << '\n'; // to split the file comment from the first element } // the root object can't be an inline table. so pass `false`. const auto serialized = visit(serializer(w, fprec, no_comment, false), v); os << serialized; // if v is a non-table value, and has only one comment, then // put a comment just after a value. in the following way. // // ```toml // key = "value" # comment. // ``` // // Since the top-level toml object is a table, one who want to put a // non-table toml value must use this in a following way. // // ```cpp // toml::value v; // std::cout << "user-defined-key = " << v << std::endl; // ``` // // In this case, it is impossible to put comments before key-value pair. // The only way to preserve comments is to put all of them after a value. if(!no_comment && !v.is_table() && !v.comments().empty()) { os << " #"; for(const auto& c : v.comments()) {os << c;} } return os; } } // toml #endif// TOML11_SERIALIZER_HPP toml11-3.8.1/toml/source_location.hpp000066400000000000000000000177201454646465200175430ustar00rootroot00000000000000// Copyright Toru Niina 2019. // Distributed under the MIT License. #ifndef TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP #include #include #include "region.hpp" namespace toml { // A struct to contain location in a toml file. // The interface imitates std::experimental::source_location, // but not completely the same. // // It would be constructed by toml::value. It can be used to generate // user-defined error messages. // // - std::uint_least32_t line() const noexcept // - returns the line number where the region is on. // - std::uint_least32_t column() const noexcept // - returns the column number where the region starts. // - std::uint_least32_t region() const noexcept // - returns the size of the region. // // +-- line() +-- region of interest (region() == 9) // v .---+---. // 12 | value = "foo bar" // ^ // +-- column() // // - std::string const& file_name() const noexcept; // - name of the file. // - std::string const& line_str() const noexcept; // - the whole line that contains the region of interest. // struct source_location { public: source_location() : line_num_(1), column_num_(1), region_size_(1), file_name_("unknown file"), line_str_("") {} explicit source_location(const detail::region_base* reg) : line_num_(1), column_num_(1), region_size_(1), file_name_("unknown file"), line_str_("") { if(reg) { if(reg->line_num() != detail::region_base().line_num()) { line_num_ = static_cast( std::stoul(reg->line_num())); } column_num_ = static_cast(reg->before() + 1); region_size_ = static_cast(reg->size()); file_name_ = reg->name(); line_str_ = reg->line(); } } explicit source_location(const detail::region& reg) : line_num_(static_cast(std::stoul(reg.line_num()))), column_num_(static_cast(reg.before() + 1)), region_size_(static_cast(reg.size())), file_name_(reg.name()), line_str_ (reg.line()) {} explicit source_location(const detail::location& loc) : line_num_(static_cast(std::stoul(loc.line_num()))), column_num_(static_cast(loc.before() + 1)), region_size_(static_cast(loc.size())), file_name_(loc.name()), line_str_ (loc.line()) {} ~source_location() = default; source_location(source_location const&) = default; source_location(source_location &&) = default; source_location& operator=(source_location const&) = default; source_location& operator=(source_location &&) = default; std::uint_least32_t line() const noexcept {return line_num_;} std::uint_least32_t column() const noexcept {return column_num_;} std::uint_least32_t region() const noexcept {return region_size_;} std::string const& file_name() const noexcept {return file_name_;} std::string const& line_str() const noexcept {return line_str_;} private: std::uint_least32_t line_num_; std::uint_least32_t column_num_; std::uint_least32_t region_size_; std::string file_name_; std::string line_str_; }; namespace detail { // internal error message generation. inline std::string format_underline(const std::string& message, const std::vector>& loc_com, const std::vector& helps = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { std::size_t line_num_width = 0; for(const auto& lc : loc_com) { std::uint_least32_t line = lc.first.line(); std::size_t digit = 0; while(line != 0) { line /= 10; digit += 1; } line_num_width = (std::max)(line_num_width, digit); } // 1 is the minimum width line_num_width = std::max(line_num_width, 1); std::ostringstream retval; if(color::should_color() || colorize) { retval << color::colorize; // turn on ANSI color } // XXX // Here, before `colorize` support, it does not output `[error]` prefix // automatically. So some user may output it manually and this change may // duplicate the prefix. To avoid it, check the first 7 characters and // if it is "[error]", it removes that part from the message shown. if(message.size() > 7 && message.substr(0, 7) == "[error]") { retval #ifndef TOML11_NO_ERROR_PREFIX << color::bold << color::red << "[error]" << color::reset #endif << color::bold << message.substr(7) << color::reset << '\n'; } else { retval #ifndef TOML11_NO_ERROR_PREFIX << color::bold << color::red << "[error] " << color::reset #endif << color::bold << message << color::reset << '\n'; } const auto format_one_location = [line_num_width] (std::ostringstream& oss, const source_location& loc, const std::string& comment) -> void { oss << ' ' << color::bold << color::blue << std::setw(static_cast(line_num_width)) << std::right << loc.line() << " | " << color::reset << loc.line_str() << '\n'; oss << make_string(line_num_width + 1, ' ') << color::bold << color::blue << " | " << color::reset << make_string(loc.column()-1 /*1-origin*/, ' '); if(loc.region() == 1) { // invalid // ^------ oss << color::bold << color::red << "^---" << color::reset; } else { // invalid // ~~~~~~~ const auto underline_len = (std::min)( static_cast(loc.region()), loc.line_str().size()); oss << color::bold << color::red << make_string(underline_len, '~') << color::reset; } oss << ' '; oss << comment; return; }; assert(!loc_com.empty()); // --> example.toml // | retval << color::bold << color::blue << " --> " << color::reset << loc_com.front().first.file_name() << '\n'; retval << make_string(line_num_width + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; // 1 | key value // | ^--- missing = format_one_location(retval, loc_com.front().first, loc_com.front().second); // process the rest of the locations for(std::size_t i=1; i filename.toml" again { retval << color::bold << color::blue << " --> " << color::reset << curr.first.file_name() << '\n'; retval << make_string(line_num_width + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; } format_one_location(retval, curr.first, curr.second); } if(!helps.empty()) { retval << '\n'; retval << make_string(line_num_width + 1, ' '); retval << color::bold << color::blue << " |" << color::reset; for(const auto& help : helps) { retval << color::bold << "\nHint: " << color::reset; retval << help; } } return retval.str(); } } // detail } // toml #endif// TOML11_SOURCE_LOCATION_HPP toml11-3.8.1/toml/storage.hpp000066400000000000000000000022301454646465200160050ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_STORAGE_HPP #define TOML11_STORAGE_HPP #include "utility.hpp" namespace toml { namespace detail { // this contains pointer and deep-copy the content if copied. // to avoid recursive pointer. template struct storage { using value_type = T; explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} ~storage() = default; storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} storage& operator=(const storage& rhs) { this->ptr = toml::make_unique(*rhs.ptr); return *this; } storage(storage&&) = default; storage& operator=(storage&&) = default; bool is_ok() const noexcept {return static_cast(ptr);} value_type& value() & noexcept {return *ptr;} value_type const& value() const& noexcept {return *ptr;} value_type&& value() && noexcept {return std::move(*ptr);} private: std::unique_ptr ptr; }; } // detail } // toml #endif// TOML11_STORAGE_HPP toml11-3.8.1/toml/string.hpp000066400000000000000000000171451454646465200156620ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_STRING_HPP #define TOML11_STRING_HPP #include "version.hpp" #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #if __has_include() #define TOML11_USING_STRING_VIEW 1 #include #endif #endif namespace toml { enum class string_t : std::uint8_t { basic = 0, literal = 1, }; struct string { string() = default; ~string() = default; string(const string& s) = default; string(string&& s) = default; string& operator=(const string& s) = default; string& operator=(string&& s) = default; string(const std::string& s): kind(string_t::basic), str(s){} string(const std::string& s, string_t k): kind(k), str(s){} string(const char* s): kind(string_t::basic), str(s){} string(const char* s, string_t k): kind(k), str(s){} string(std::string&& s): kind(string_t::basic), str(std::move(s)){} string(std::string&& s, string_t k): kind(k), str(std::move(s)){} string& operator=(const std::string& s) {kind = string_t::basic; str = s; return *this;} string& operator=(std::string&& s) {kind = string_t::basic; str = std::move(s); return *this;} operator std::string& () & noexcept {return str;} operator std::string const& () const& noexcept {return str;} operator std::string&& () && noexcept {return std::move(str);} string& operator+=(const char* rhs) {str += rhs; return *this;} string& operator+=(const char rhs) {str += rhs; return *this;} string& operator+=(const std::string& rhs) {str += rhs; return *this;} string& operator+=(const string& rhs) {str += rhs.str; return *this;} #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 explicit string(std::string_view s): kind(string_t::basic), str(s){} string(std::string_view s, string_t k): kind(k), str(s){} string& operator=(std::string_view s) {kind = string_t::basic; str = s; return *this;} explicit operator std::string_view() const noexcept {return std::string_view(str);} string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} #endif string_t kind; std::string str; }; inline bool operator==(const string& lhs, const string& rhs) { return lhs.kind == rhs.kind && lhs.str == rhs.str; } inline bool operator!=(const string& lhs, const string& rhs) { return !(lhs == rhs); } inline bool operator<(const string& lhs, const string& rhs) { return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); } inline bool operator>(const string& lhs, const string& rhs) { return rhs < lhs; } inline bool operator<=(const string& lhs, const string& rhs) { return !(rhs < lhs); } inline bool operator>=(const string& lhs, const string& rhs) { return !(lhs < rhs); } inline bool operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} inline bool operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} inline bool operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} inline bool operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} inline bool operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} inline bool operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} inline bool operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} inline bool operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} inline bool operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} inline bool operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} inline bool operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} inline bool operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} inline bool operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} inline bool operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} inline bool operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} inline bool operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} inline bool operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} inline bool operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} inline bool operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} inline bool operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} inline bool operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} inline bool operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} inline bool operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} inline bool operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} template std::basic_ostream& operator<<(std::basic_ostream& os, const string& s) { if(s.kind == string_t::basic) { if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) { // it contains newline. make it multiline string. os << "\"\"\"\n"; for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) { switch(*i) { case '\\': {os << "\\\\"; break;} case '\"': {os << "\\\""; break;} case '\b': {os << "\\b"; break;} case '\t': {os << "\\t"; break;} case '\f': {os << "\\f"; break;} case '\n': {os << '\n'; break;} case '\r': { // since it is a multiline string, // CRLF is not needed to be escaped. if(std::next(i) != e && *std::next(i) == '\n') { os << "\r\n"; ++i; } else { os << "\\r"; } break; } default: {os << *i; break;} } } os << "\\\n\"\"\""; return os; } // no newline. make it inline. os << "\""; for(const auto c : s.str) { switch(c) { case '\\': {os << "\\\\"; break;} case '\"': {os << "\\\""; break;} case '\b': {os << "\\b"; break;} case '\t': {os << "\\t"; break;} case '\f': {os << "\\f"; break;} case '\n': {os << "\\n"; break;} case '\r': {os << "\\r"; break;} default : {os << c; break;} } } os << "\""; return os; } // the string `s` is literal-string. if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) { // contains newline or single quote. make it multiline. os << "'''\n" << s.str << "'''"; return os; } // normal literal string os << '\'' << s.str << '\''; return os; } } // toml #endif// TOML11_STRING_H toml11-3.8.1/toml/traits.hpp000066400000000000000000000250571454646465200156630ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP #include "from.hpp" #include "into.hpp" #include "version.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #if __has_include() #include #endif // has_include() #endif // cplusplus >= C++17 namespace toml { template class T, template class A> class basic_value; namespace detail { // --------------------------------------------------------------------------- // check whether type T is a kind of container/map class struct has_iterator_impl { template static std::true_type check(typename T::iterator*); template static std::false_type check(...); }; struct has_value_type_impl { template static std::true_type check(typename T::value_type*); template static std::false_type check(...); }; struct has_key_type_impl { template static std::true_type check(typename T::key_type*); template static std::false_type check(...); }; struct has_mapped_type_impl { template static std::true_type check(typename T::mapped_type*); template static std::false_type check(...); }; struct has_reserve_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().reserve(std::declval()))*); }; struct has_push_back_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().push_back(std::declval()))*); }; struct is_comparable_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval() < std::declval())*); }; struct has_from_toml_method_impl { template class Tb, template class A> static std::true_type check( decltype(std::declval().from_toml( std::declval<::toml::basic_value>()))*); template class Tb, template class A> static std::false_type check(...); }; struct has_into_toml_method_impl { template static std::true_type check(decltype(std::declval().into_toml())*); template static std::false_type check(...); }; struct has_specialized_from_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::from*); }; struct has_specialized_into_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::from*); }; /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER #define decltype(...) std::enable_if::type #endif template struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; template struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; template struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; template struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; template struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; template struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; template struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; template class Tb, template class A> struct has_from_toml_method : decltype(has_from_toml_method_impl::check(nullptr)){}; template struct has_into_toml_method : decltype(has_into_toml_method_impl::check(nullptr)){}; template struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; template struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; #ifdef __INTEL_COMPILER #undef decltype #endif // --------------------------------------------------------------------------- // C++17 and/or/not #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L using std::conjunction; using std::disjunction; using std::negation; #else template struct conjunction : std::true_type{}; template struct conjunction : T{}; template struct conjunction : std::conditional(T::value), conjunction, T>::type {}; template struct disjunction : std::false_type{}; template struct disjunction : T {}; template struct disjunction : std::conditional(T::value), T, disjunction>::type {}; template struct negation : std::integral_constant(T::value)>{}; #endif // --------------------------------------------------------------------------- // type checkers template struct is_std_pair : std::false_type{}; template struct is_std_pair> : std::true_type{}; template struct is_std_tuple : std::false_type{}; template struct is_std_tuple> : std::true_type{}; template struct is_std_forward_list : std::false_type{}; template struct is_std_forward_list> : std::true_type{}; template struct is_chrono_duration: std::false_type{}; template struct is_chrono_duration>: std::true_type{}; template struct is_map : conjunction< // map satisfies all the following conditions has_iterator, // has T::iterator has_value_type, // has T::value_type has_key_type, // has T::key_type has_mapped_type // has T::mapped_type >{}; template struct is_map : is_map{}; template struct is_map : is_map{}; template struct is_map : is_map{}; template struct is_map : is_map{}; template struct is_container : conjunction< negation>, // not a map negation>, // not a std::string #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #if __has_include() negation>, // not a std::string_view #endif // has_include() #endif has_iterator, // has T::iterator has_value_type // has T::value_type >{}; template struct is_container : is_container{}; template struct is_container : is_container{}; template struct is_container : is_container{}; template struct is_container : is_container{}; template struct is_basic_value: std::false_type{}; template struct is_basic_value : is_basic_value{}; template struct is_basic_value : is_basic_value{}; template struct is_basic_value : is_basic_value{}; template struct is_basic_value : is_basic_value{}; template class M, template class V> struct is_basic_value<::toml::basic_value>: std::true_type{}; // --------------------------------------------------------------------------- // C++14 index_sequence #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::index_sequence; using std::make_index_sequence; #else template struct index_sequence{}; template struct push_back_index_sequence{}; template struct push_back_index_sequence, N> { typedef index_sequence type; }; template struct index_sequence_maker { typedef typename push_back_index_sequence< typename index_sequence_maker::type, N>::type type; }; template<> struct index_sequence_maker<0> { typedef index_sequence<0> type; }; template using make_index_sequence = typename index_sequence_maker::type; #endif // cplusplus >= 2014 // --------------------------------------------------------------------------- // C++14 enable_if_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::enable_if_t; #else template using enable_if_t = typename std::enable_if::type; #endif // cplusplus >= 2014 // --------------------------------------------------------------------------- // return_type_of_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703 template using return_type_of_t = std::invoke_result_t; #else // result_of is deprecated after C++17 template using return_type_of_t = typename std::result_of::type; #endif // --------------------------------------------------------------------------- // is_string_literal // // to use this, pass `typename remove_reference::type` to T. template struct is_string_literal: disjunction< std::is_same, conjunction< std::is_array, std::is_same::type> > >{}; // --------------------------------------------------------------------------- // C++20 remove_cvref_t template struct remove_cvref { using type = typename std::remove_cv< typename std::remove_reference::type>::type; }; template using remove_cvref_t = typename remove_cvref::type; }// detail }//toml #endif // TOML_TRAITS toml11-3.8.1/toml/types.hpp000066400000000000000000000173711454646465200155210ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_TYPES_HPP #define TOML11_TYPES_HPP #include #include #include "comments.hpp" #include "datetime.hpp" #include "string.hpp" #include "traits.hpp" namespace toml { template class Table, // map-like class template class Array> // vector-like class class basic_value; using character = char; using key = std::string; #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wshadow" #endif using boolean = bool; using integer = std::int64_t; using floating = double; // "float" is a keyword, cannot use it here. // the following stuffs are structs defined here, so aliases are not needed. // - string // - offset_datetime // - offset_datetime // - local_datetime // - local_date // - local_time #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic pop #endif // default toml::value and default array/table. these are defined after defining // basic_value itself. // using value = basic_value; // using array = typename value::array_type; // using table = typename value::table_type; // to avoid warnings about `value_t::integer` is "shadowing" toml::integer in // GCC -Wshadow=global. #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic push # if 7 <= __GNUC__ # pragma GCC diagnostic ignored "-Wshadow=global" # else // gcc-6 or older # pragma GCC diagnostic ignored "-Wshadow" # endif #endif enum class value_t : std::uint8_t { empty = 0, boolean = 1, integer = 2, floating = 3, string = 4, offset_datetime = 5, local_datetime = 6, local_date = 7, local_time = 8, array = 9, table = 10, }; #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic pop #endif template inline std::basic_ostream& operator<<(std::basic_ostream& os, value_t t) { switch(t) { case value_t::boolean : os << "boolean"; return os; case value_t::integer : os << "integer"; return os; case value_t::floating : os << "floating"; return os; case value_t::string : os << "string"; return os; case value_t::offset_datetime : os << "offset_datetime"; return os; case value_t::local_datetime : os << "local_datetime"; return os; case value_t::local_date : os << "local_date"; return os; case value_t::local_time : os << "local_time"; return os; case value_t::array : os << "array"; return os; case value_t::table : os << "table"; return os; case value_t::empty : os << "empty"; return os; default : os << "unknown"; return os; } } template, typename alloc = std::allocator> inline std::basic_string stringize(value_t t) { std::basic_ostringstream oss; oss << t; return oss.str(); } namespace detail { // helper to define a type that represents a value_t value. template using value_t_constant = std::integral_constant; // meta-function that convertes from value_t to the exact toml type that corresponds to. // It takes toml::basic_value type because array and table types depend on it. template struct enum_to_type {using type = void ;}; template struct enum_to_type{using type = void ;}; template struct enum_to_type{using type = boolean ;}; template struct enum_to_type{using type = integer ;}; template struct enum_to_type{using type = floating ;}; template struct enum_to_type{using type = string ;}; template struct enum_to_type{using type = offset_datetime ;}; template struct enum_to_type{using type = local_datetime ;}; template struct enum_to_type{using type = local_date ;}; template struct enum_to_type{using type = local_time ;}; template struct enum_to_type{using type = typename Value::array_type;}; template struct enum_to_type{using type = typename Value::table_type;}; // meta-function that converts from an exact toml type to the enum that corresponds to. template struct type_to_enum : std::conditional< std::is_same::value, // if T == array_type, value_t_constant, // then value_t::array typename std::conditional< // else... std::is_same::value, // if T == table_type value_t_constant, // then value_t::table value_t_constant // else value_t::empty >::type >::type {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; template struct type_to_enum: value_t_constant {}; // meta-function that checks the type T is the same as one of the toml::* types. template struct is_exact_toml_type : disjunction< std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same >{}; template struct is_exact_toml_type : is_exact_toml_type{}; template struct is_exact_toml_type : is_exact_toml_type{}; template struct is_exact_toml_type : is_exact_toml_type{}; template struct is_exact_toml_type: is_exact_toml_type{}; } // detail } // toml #endif// TOML11_TYPES_H toml11-3.8.1/toml/utility.hpp000066400000000000000000000070171454646465200160540ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_UTILITY_HPP #define TOML11_UTILITY_HPP #include #include #include #include "traits.hpp" #include "version.hpp" #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] #elif defined(__GNUC__) # define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif defined(_MSC_VER) # define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg)) #else # define TOML11_MARK_AS_DEPRECATED #endif namespace toml { #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::make_unique; #else template inline std::unique_ptr make_unique(Ts&& ... args) { return std::unique_ptr(new T(std::forward(args)...)); } #endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014 namespace detail { template void try_reserve_impl(Container& container, std::size_t N, std::true_type) { container.reserve(N); return; } template void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept { return; } } // detail template void try_reserve(Container& container, std::size_t N) { if(N <= container.size()) {return;} detail::try_reserve_impl(container, N, detail::has_reserve_method{}); return; } namespace detail { inline std::string concat_to_string_impl(std::ostringstream& oss) { return oss.str(); } template std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) { oss << std::forward(head); return concat_to_string_impl(oss, std::forward(tail) ... ); } } // detail template std::string concat_to_string(Ts&& ... args) { std::ostringstream oss; oss << std::boolalpha << std::fixed; return detail::concat_to_string_impl(oss, std::forward(args) ...); } template T from_string(const std::string& str, T opt) { T v(opt); std::istringstream iss(str); iss >> v; return v; } namespace detail { #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L template decltype(auto) last_one(T&& tail) noexcept { return std::forward(tail); } template decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept { return last_one(std::forward(tail)...); } #else // C++11 // The following code // ```cpp // 1 | template // 2 | auto last_one(T&& /*head*/, Ts&& ... tail) // 3 | -> decltype(last_one(std::forward(tail)...)) // 4 | { // 5 | return last_one(std::forward(tail)...); // 6 | } // ``` // does not work because the function `last_one(...)` is not yet defined at // line #3, so `decltype()` cannot deduce the type returned from `last_one`. // So we need to determine return type in a different way, like a meta func. template struct last_one_in_pack { using type = typename last_one_in_pack::type; }; template struct last_one_in_pack { using type = T; }; template using last_one_in_pack_t = typename last_one_in_pack::type; template T&& last_one(T&& tail) noexcept { return std::forward(tail); } template enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> last_one(T&& /*head*/, Ts&& ... tail) { return last_one(std::forward(tail)...); } #endif } // detail }// toml #endif // TOML11_UTILITY toml11-3.8.1/toml/value.hpp000066400000000000000000002375171454646465200154770ustar00rootroot00000000000000// Copyright Toru Niina 2017. // Distributed under the MIT License. #ifndef TOML11_VALUE_HPP #define TOML11_VALUE_HPP #include #include "comments.hpp" #include "exception.hpp" #include "into.hpp" #include "region.hpp" #include "source_location.hpp" #include "storage.hpp" #include "traits.hpp" #include "types.hpp" #include "utility.hpp" namespace toml { namespace detail { // to show error messages. not recommended for users. template inline region_base const* get_region(const Value& v) { return v.region_info_.get(); } template void change_region(Value& v, region reg) { v.region_info_ = std::make_shared(std::move(reg)); return; } template [[noreturn]] inline void throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) { throw type_error(detail::format_underline( concat_to_string(funcname, "bad_cast to ", Expected), { {v.location(), concat_to_string("the actual type is ", actual)} }), v.location()); } // Throw `out_of_range` from `toml::value::at()` and `toml::find()` // after generating an error message. // // The implementation is a bit complicated and there are many edge-cases. // If you are not interested in the error message generation, just skip this. template [[noreturn]] void throw_key_not_found_error(const Value& v, const key& ky) { // The top-level table has its region at the first character of the file. // That means that, in the case when a key is not found in the top-level // table, the error message points to the first character. If the file has // its first table at the first line, the error message would be like this. // ```console // [error] key "a" not found // --> example.toml // | // 1 | [table] // | ^------ in this table // ``` // It actually points to the top-level table at the first character, // not `[table]`. But it is too confusing. To avoid the confusion, the error // message should explicitly say "key not found in the top-level table", // or "the parsed file is empty" if there is no content at all (0 bytes in file). const auto loc = v.location(); if(loc.line() == 1 && loc.region() == 0) { // First line with a zero-length region means "empty file". // The region will be generated at `parse_toml_file` function // if the file contains no bytes. throw std::out_of_range(format_underline(concat_to_string( "key \"", ky, "\" not found in the top-level table"), { {loc, "the parsed file is empty"} })); } else if(loc.line() == 1 && loc.region() == 1) { // Here it assumes that top-level table starts at the first character. // The region corresponds to the top-level table will be generated at // `parse_toml_file` function. // It also assumes that the top-level table size is just one and // the line number is `1`. It is always satisfied. And those conditions // are satisfied only if the table is the top-level table. // // 1. one-character dot-key at the first line // ```toml // a.b = "c" // ``` // toml11 counts whole key as the table key. Here, `a.b` is the region // of the table "a". It could be counter intuitive, but it works. // The size of the region is 3, not 1. The above example is the shortest // dot-key example. The size cannot be 1. // // 2. one-character inline-table at the first line // ```toml // a = {b = "c"} // ``` // toml11 considers the inline table body as the table region. Here, // `{b = "c"}` is the region of the table "a". The size of the region // is 9, not 1. The shotest inline table still has two characters, `{` // and `}`. The size cannot be 1. // // 3. one-character table declaration at the first line // ```toml // [a] // ``` // toml11 considers the whole table key as the table region. Here, // `[a]` is the table region. The size is 3, not 1. // throw std::out_of_range(format_underline(concat_to_string( "key \"", ky, "\" not found in the top-level table"), { {loc, "the top-level table starts here"} })); } else { // normal table. throw std::out_of_range(format_underline(concat_to_string( "key \"", ky, "\" not found"), { {loc, "in this table"} })); } } // switch by `value_t` at the compile time. template struct switch_cast {}; #define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ template<> \ struct switch_cast \ { \ template \ static typename Value::TYPE##_type& invoke(Value& v) \ { \ return v.as_##TYPE(); \ } \ template \ static typename Value::TYPE##_type const& invoke(const Value& v) \ { \ return v.as_##TYPE(); \ } \ template \ static typename Value::TYPE##_type&& invoke(Value&& v) \ { \ return std::move(v).as_##TYPE(); \ } \ }; \ /**/ TOML11_GENERATE_SWITCH_CASTER(boolean) TOML11_GENERATE_SWITCH_CASTER(integer) TOML11_GENERATE_SWITCH_CASTER(floating) TOML11_GENERATE_SWITCH_CASTER(string) TOML11_GENERATE_SWITCH_CASTER(offset_datetime) TOML11_GENERATE_SWITCH_CASTER(local_datetime) TOML11_GENERATE_SWITCH_CASTER(local_date) TOML11_GENERATE_SWITCH_CASTER(local_time) TOML11_GENERATE_SWITCH_CASTER(array) TOML11_GENERATE_SWITCH_CASTER(table) #undef TOML11_GENERATE_SWITCH_CASTER }// detail template class Table = std::unordered_map, template class Array = std::vector> class basic_value { template static void assigner(T& dst, U&& v) { const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); assert(tmp == std::addressof(dst)); (void)tmp; } using region_base = detail::region_base; template class T, template class A> friend class basic_value; public: using comment_type = Comment; using key_type = ::toml::key; using value_type = basic_value; using boolean_type = ::toml::boolean; using integer_type = ::toml::integer; using floating_type = ::toml::floating; using string_type = ::toml::string; using local_time_type = ::toml::local_time; using local_date_type = ::toml::local_date; using local_datetime_type = ::toml::local_datetime; using offset_datetime_type = ::toml::offset_datetime; using array_type = Array; using table_type = Table; public: basic_value() noexcept : type_(value_t::empty), region_info_(std::make_shared(region_base{})) {} ~basic_value() noexcept {this->cleanup();} basic_value(const basic_value& v) : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) { switch(v.type()) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default: break; } } basic_value(basic_value&& v) : type_(v.type()), region_info_(std::move(v.region_info_)), comments_(std::move(v.comments_)) { switch(this->type_) // here this->type_ is already initialized { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default: break; } } basic_value& operator=(const basic_value& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->region_info_ = v.region_info_; this->comments_ = v.comments_; this->type_ = v.type(); switch(this->type_) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default: break; } return *this; } basic_value& operator=(basic_value&& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->region_info_ = std::move(v.region_info_); this->comments_ = std::move(v.comments_); this->type_ = v.type(); switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default: break; } return *this; } // overwrite comments ---------------------------------------------------- basic_value(const basic_value& v, std::vector com) : type_(v.type()), region_info_(v.region_info_), comments_(std::move(com)) { switch(v.type()) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default: break; } } basic_value(basic_value&& v, std::vector com) : type_(v.type()), region_info_(std::move(v.region_info_)), comments_(std::move(com)) { switch(this->type_) // here this->type_ is already initialized { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default: break; } } // ----------------------------------------------------------------------- // conversion between different basic_values. template class T, template class A> basic_value(const basic_value& v) : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) { switch(v.type()) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : { array_type tmp(v.as_array(std::nothrow).begin(), v.as_array(std::nothrow).end()); assigner(array_, std::move(tmp)); break; } case value_t::table : { table_type tmp(v.as_table(std::nothrow).begin(), v.as_table(std::nothrow).end()); assigner(table_, std::move(tmp)); break; } default: break; } } template class T, template class A> basic_value(const basic_value& v, std::vector com) : type_(v.type()), region_info_(v.region_info_), comments_(std::move(com)) { switch(v.type()) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : { array_type tmp(v.as_array(std::nothrow).begin(), v.as_array(std::nothrow).end()); assigner(array_, std::move(tmp)); break; } case value_t::table : { table_type tmp(v.as_table(std::nothrow).begin(), v.as_table(std::nothrow).end()); assigner(table_, std::move(tmp)); break; } default: break; } } template class T, template class A> basic_value& operator=(const basic_value& v) { this->region_info_ = v.region_info_; this->comments_ = comment_type(v.comments()); this->type_ = v.type(); switch(v.type()) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : { array_type tmp(v.as_array(std::nothrow).begin(), v.as_array(std::nothrow).end()); assigner(array_, std::move(tmp)); break; } case value_t::table : { table_type tmp(v.as_table(std::nothrow).begin(), v.as_table(std::nothrow).end()); assigner(table_, std::move(tmp)); break; } default: break; } return *this; } // boolean ============================================================== basic_value(boolean b) : type_(value_t::boolean), region_info_(std::make_shared(region_base{})) { assigner(this->boolean_, b); } basic_value& operator=(boolean b) { this->cleanup(); this->type_ = value_t::boolean; this->region_info_ = std::make_shared(region_base{}); assigner(this->boolean_, b); return *this; } basic_value(boolean b, std::vector com) : type_(value_t::boolean), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->boolean_, b); } // integer ============================================================== template, detail::negation>>::value, std::nullptr_t>::type = nullptr> basic_value(T i) : type_(value_t::integer), region_info_(std::make_shared(region_base{})) { assigner(this->integer_, static_cast(i)); } template, detail::negation>>::value, std::nullptr_t>::type = nullptr> basic_value& operator=(T i) { this->cleanup(); this->type_ = value_t::integer; this->region_info_ = std::make_shared(region_base{}); assigner(this->integer_, static_cast(i)); return *this; } template, detail::negation>>::value, std::nullptr_t>::type = nullptr> basic_value(T i, std::vector com) : type_(value_t::integer), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->integer_, static_cast(i)); } // floating ============================================================= template::value, std::nullptr_t>::type = nullptr> basic_value(T f) : type_(value_t::floating), region_info_(std::make_shared(region_base{})) { assigner(this->floating_, static_cast(f)); } template::value, std::nullptr_t>::type = nullptr> basic_value& operator=(T f) { this->cleanup(); this->type_ = value_t::floating; this->region_info_ = std::make_shared(region_base{}); assigner(this->floating_, static_cast(f)); return *this; } template::value, std::nullptr_t>::type = nullptr> basic_value(T f, std::vector com) : type_(value_t::floating), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->floating_, f); } // string =============================================================== basic_value(toml::string s) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, std::move(s)); } basic_value& operator=(toml::string s) { this->cleanup(); this->type_ = value_t::string ; this->region_info_ = std::make_shared(region_base{}); assigner(this->string_, s); return *this; } basic_value(toml::string s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, std::move(s)); } basic_value(std::string s) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(std::move(s))); } basic_value& operator=(std::string s) { this->cleanup(); this->type_ = value_t::string ; this->region_info_ = std::make_shared(region_base{}); assigner(this->string_, toml::string(std::move(s))); return *this; } basic_value(std::string s, string_t kind) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(std::move(s), kind)); } basic_value(std::string s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(std::move(s))); } basic_value(std::string s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(std::move(s), kind)); } basic_value(const char* s) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(std::string(s))); } basic_value& operator=(const char* s) { this->cleanup(); this->type_ = value_t::string ; this->region_info_ = std::make_shared(region_base{}); assigner(this->string_, toml::string(std::string(s))); return *this; } basic_value(const char* s, string_t kind) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(std::string(s), kind)); } basic_value(const char* s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(std::string(s))); } basic_value(const char* s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(std::string(s), kind)); } #if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 basic_value(std::string_view s) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(s)); } basic_value& operator=(std::string_view s) { this->cleanup(); this->type_ = value_t::string ; this->region_info_ = std::make_shared(region_base{}); assigner(this->string_, toml::string(s)); return *this; } basic_value(std::string_view s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(s)); } basic_value(std::string_view s, string_t kind) : type_(value_t::string), region_info_(std::make_shared(region_base{})) { assigner(this->string_, toml::string(s, kind)); } basic_value(std::string_view s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->string_, toml::string(s, kind)); } #endif // local date =========================================================== basic_value(const local_date& ld) : type_(value_t::local_date), region_info_(std::make_shared(region_base{})) { assigner(this->local_date_, ld); } basic_value& operator=(const local_date& ld) { this->cleanup(); this->type_ = value_t::local_date; this->region_info_ = std::make_shared(region_base{}); assigner(this->local_date_, ld); return *this; } basic_value(const local_date& ld, std::vector com) : type_(value_t::local_date), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->local_date_, ld); } // local time =========================================================== basic_value(const local_time& lt) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})) { assigner(this->local_time_, lt); } basic_value(const local_time& lt, std::vector com) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->local_time_, lt); } basic_value& operator=(const local_time& lt) { this->cleanup(); this->type_ = value_t::local_time; this->region_info_ = std::make_shared(region_base{}); assigner(this->local_time_, lt); return *this; } template basic_value(const std::chrono::duration& dur) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})) { assigner(this->local_time_, local_time(dur)); } template basic_value(const std::chrono::duration& dur, std::vector com) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->local_time_, local_time(dur)); } template basic_value& operator=(const std::chrono::duration& dur) { this->cleanup(); this->type_ = value_t::local_time; this->region_info_ = std::make_shared(region_base{}); assigner(this->local_time_, local_time(dur)); return *this; } // local datetime ======================================================= basic_value(const local_datetime& ldt) : type_(value_t::local_datetime), region_info_(std::make_shared(region_base{})) { assigner(this->local_datetime_, ldt); } basic_value(const local_datetime& ldt, std::vector com) : type_(value_t::local_datetime), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->local_datetime_, ldt); } basic_value& operator=(const local_datetime& ldt) { this->cleanup(); this->type_ = value_t::local_datetime; this->region_info_ = std::make_shared(region_base{}); assigner(this->local_datetime_, ldt); return *this; } // offset datetime ====================================================== basic_value(const offset_datetime& odt) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})) { assigner(this->offset_datetime_, odt); } basic_value(const offset_datetime& odt, std::vector com) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->offset_datetime_, odt); } basic_value& operator=(const offset_datetime& odt) { this->cleanup(); this->type_ = value_t::offset_datetime; this->region_info_ = std::make_shared(region_base{}); assigner(this->offset_datetime_, odt); return *this; } basic_value(const std::chrono::system_clock::time_point& tp) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})) { assigner(this->offset_datetime_, offset_datetime(tp)); } basic_value(const std::chrono::system_clock::time_point& tp, std::vector com) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->offset_datetime_, offset_datetime(tp)); } basic_value& operator=(const std::chrono::system_clock::time_point& tp) { this->cleanup(); this->type_ = value_t::offset_datetime; this->region_info_ = std::make_shared(region_base{}); assigner(this->offset_datetime_, offset_datetime(tp)); return *this; } // array ================================================================ basic_value(const array_type& ary) : type_(value_t::array), region_info_(std::make_shared(region_base{})) { assigner(this->array_, ary); } basic_value(const array_type& ary, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->array_, ary); } basic_value& operator=(const array_type& ary) { this->cleanup(); this->type_ = value_t::array ; this->region_info_ = std::make_shared(region_base{}); assigner(this->array_, ary); return *this; } // array (initializer_list) ---------------------------------------------- template::value, std::nullptr_t>::type = nullptr> basic_value(std::initializer_list list) : type_(value_t::array), region_info_(std::make_shared(region_base{})) { array_type ary(list.begin(), list.end()); assigner(this->array_, std::move(ary)); } template::value, std::nullptr_t>::type = nullptr> basic_value(std::initializer_list list, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { array_type ary(list.begin(), list.end()); assigner(this->array_, std::move(ary)); } template::value, std::nullptr_t>::type = nullptr> basic_value& operator=(std::initializer_list list) { this->cleanup(); this->type_ = value_t::array; this->region_info_ = std::make_shared(region_base{}); array_type ary(list.begin(), list.end()); assigner(this->array_, std::move(ary)); return *this; } // array (STL Containers) ------------------------------------------------ template>, detail::is_container >::value, std::nullptr_t>::type = nullptr> basic_value(const T& list) : type_(value_t::array), region_info_(std::make_shared(region_base{})) { static_assert(std::is_convertible::value, "elements of a container should be convertible to toml::value"); array_type ary(list.size()); std::copy(list.begin(), list.end(), ary.begin()); assigner(this->array_, std::move(ary)); } template>, detail::is_container >::value, std::nullptr_t>::type = nullptr> basic_value(const T& list, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { static_assert(std::is_convertible::value, "elements of a container should be convertible to toml::value"); array_type ary(list.size()); std::copy(list.begin(), list.end(), ary.begin()); assigner(this->array_, std::move(ary)); } template>, detail::is_container >::value, std::nullptr_t>::type = nullptr> basic_value& operator=(const T& list) { static_assert(std::is_convertible::value, "elements of a container should be convertible to toml::value"); this->cleanup(); this->type_ = value_t::array; this->region_info_ = std::make_shared(region_base{}); array_type ary(list.size()); std::copy(list.begin(), list.end(), ary.begin()); assigner(this->array_, std::move(ary)); return *this; } // table ================================================================ basic_value(const table_type& tab) : type_(value_t::table), region_info_(std::make_shared(region_base{})) { assigner(this->table_, tab); } basic_value(const table_type& tab, std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { assigner(this->table_, tab); } basic_value& operator=(const table_type& tab) { this->cleanup(); this->type_ = value_t::table; this->region_info_ = std::make_shared(region_base{}); assigner(this->table_, tab); return *this; } // initializer-list ------------------------------------------------------ basic_value(std::initializer_list> list) : type_(value_t::table), region_info_(std::make_shared(region_base{})) { table_type tab; for(const auto& elem : list) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); } basic_value(std::initializer_list> list, std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { table_type tab; for(const auto& elem : list) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); } basic_value& operator=(std::initializer_list> list) { this->cleanup(); this->type_ = value_t::table; this->region_info_ = std::make_shared(region_base{}); table_type tab; for(const auto& elem : list) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); return *this; } // other table-like ----------------------------------------------------- template>, detail::is_map >::value, std::nullptr_t>::type = nullptr> basic_value(const Map& mp) : type_(value_t::table), region_info_(std::make_shared(region_base{})) { table_type tab; for(const auto& elem : mp) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); } template>, detail::is_map >::value, std::nullptr_t>::type = nullptr> basic_value(const Map& mp, std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), comments_(std::move(com)) { table_type tab; for(const auto& elem : mp) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); } template>, detail::is_map >::value, std::nullptr_t>::type = nullptr> basic_value& operator=(const Map& mp) { this->cleanup(); this->type_ = value_t::table; this->region_info_ = std::make_shared(region_base{}); table_type tab; for(const auto& elem : mp) {tab[elem.first] = elem.second;} assigner(this->table_, std::move(tab)); return *this; } // user-defined ========================================================= // convert using into_toml() method ------------------------------------- template::value, std::nullptr_t>::type = nullptr> basic_value(const T& ud): basic_value(ud.into_toml()) {} template::value, std::nullptr_t>::type = nullptr> basic_value(const T& ud, std::vector com) : basic_value(ud.into_toml(), std::move(com)) {} template::value, std::nullptr_t>::type = nullptr> basic_value& operator=(const T& ud) { *this = ud.into_toml(); return *this; } // convert using into struct ----------------------------------------- template)> basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} template)> basic_value(const T& ud, std::vector com) : basic_value(::toml::into::into_toml(ud), std::move(com)) {} template)> basic_value& operator=(const T& ud) { *this = ::toml::into::into_toml(ud); return *this; } // for internal use ------------------------------------------------------ // // Those constructors take detail::region that contains parse result. basic_value(boolean b, detail::region reg, std::vector cm) : type_(value_t::boolean), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->boolean_, b); } template, detail::negation> >::value, std::nullptr_t>::type = nullptr> basic_value(T i, detail::region reg, std::vector cm) : type_(value_t::integer), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->integer_, static_cast(i)); } template::value, std::nullptr_t>::type = nullptr> basic_value(T f, detail::region reg, std::vector cm) : type_(value_t::floating), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->floating_, static_cast(f)); } basic_value(toml::string s, detail::region reg, std::vector cm) : type_(value_t::string), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->string_, std::move(s)); } basic_value(const local_date& ld, detail::region reg, std::vector cm) : type_(value_t::local_date), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->local_date_, ld); } basic_value(const local_time& lt, detail::region reg, std::vector cm) : type_(value_t::local_time), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->local_time_, lt); } basic_value(const local_datetime& ldt, detail::region reg, std::vector cm) : type_(value_t::local_datetime), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->local_datetime_, ldt); } basic_value(const offset_datetime& odt, detail::region reg, std::vector cm) : type_(value_t::offset_datetime), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->offset_datetime_, odt); } basic_value(const array_type& ary, detail::region reg, std::vector cm) : type_(value_t::array), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->array_, ary); } basic_value(const table_type& tab, detail::region reg, std::vector cm) : type_(value_t::table), region_info_(std::make_shared(std::move(reg))), comments_(std::move(cm)) { assigner(this->table_, tab); } template::value, std::nullptr_t>::type = nullptr> basic_value(std::pair parse_result, std::vector com) : basic_value(std::move(parse_result.first), std::move(parse_result.second), std::move(com)) {} // type checking and casting ============================================ template::value, std::nullptr_t>::type = nullptr> bool is() const noexcept { return detail::type_to_enum::value == this->type_; } bool is(value_t t) const noexcept {return t == this->type_;} bool is_uninitialized() const noexcept {return this->is(value_t::empty );} bool is_boolean() const noexcept {return this->is(value_t::boolean );} bool is_integer() const noexcept {return this->is(value_t::integer );} bool is_floating() const noexcept {return this->is(value_t::floating );} bool is_string() const noexcept {return this->is(value_t::string );} bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} bool is_local_date() const noexcept {return this->is(value_t::local_date );} bool is_local_time() const noexcept {return this->is(value_t::local_time );} bool is_array() const noexcept {return this->is(value_t::array );} bool is_table() const noexcept {return this->is(value_t::table );} value_t type() const noexcept {return type_;} template typename detail::enum_to_type::type& cast() & { if(this->type_ != T) { detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); } return detail::switch_cast::invoke(*this); } template typename detail::enum_to_type::type const& cast() const& { if(this->type_ != T) { detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); } return detail::switch_cast::invoke(*this); } template typename detail::enum_to_type::type&& cast() && { if(this->type_ != T) { detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); } return detail::switch_cast::invoke(std::move(*this)); } // ------------------------------------------------------------------------ // nothrow version boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} // ======================================================================== // throw version // ------------------------------------------------------------------------ // const reference {{{ boolean const& as_boolean() const& { if(this->type_ != value_t::boolean) { detail::throw_bad_cast( "toml::value::as_boolean(): ", this->type_, *this); } return this->boolean_; } integer const& as_integer() const& { if(this->type_ != value_t::integer) { detail::throw_bad_cast( "toml::value::as_integer(): ", this->type_, *this); } return this->integer_; } floating const& as_floating() const& { if(this->type_ != value_t::floating) { detail::throw_bad_cast( "toml::value::as_floating(): ", this->type_, *this); } return this->floating_; } string const& as_string() const& { if(this->type_ != value_t::string) { detail::throw_bad_cast( "toml::value::as_string(): ", this->type_, *this); } return this->string_; } offset_datetime const& as_offset_datetime() const& { if(this->type_ != value_t::offset_datetime) { detail::throw_bad_cast( "toml::value::as_offset_datetime(): ", this->type_, *this); } return this->offset_datetime_; } local_datetime const& as_local_datetime() const& { if(this->type_ != value_t::local_datetime) { detail::throw_bad_cast( "toml::value::as_local_datetime(): ", this->type_, *this); } return this->local_datetime_; } local_date const& as_local_date() const& { if(this->type_ != value_t::local_date) { detail::throw_bad_cast( "toml::value::as_local_date(): ", this->type_, *this); } return this->local_date_; } local_time const& as_local_time() const& { if(this->type_ != value_t::local_time) { detail::throw_bad_cast( "toml::value::as_local_time(): ", this->type_, *this); } return this->local_time_; } array_type const& as_array() const& { if(this->type_ != value_t::array) { detail::throw_bad_cast( "toml::value::as_array(): ", this->type_, *this); } return this->array_.value(); } table_type const& as_table() const& { if(this->type_ != value_t::table) { detail::throw_bad_cast( "toml::value::as_table(): ", this->type_, *this); } return this->table_.value(); } // }}} // ------------------------------------------------------------------------ // nonconst reference {{{ boolean & as_boolean() & { if(this->type_ != value_t::boolean) { detail::throw_bad_cast( "toml::value::as_boolean(): ", this->type_, *this); } return this->boolean_; } integer & as_integer() & { if(this->type_ != value_t::integer) { detail::throw_bad_cast( "toml::value::as_integer(): ", this->type_, *this); } return this->integer_; } floating & as_floating() & { if(this->type_ != value_t::floating) { detail::throw_bad_cast( "toml::value::as_floating(): ", this->type_, *this); } return this->floating_; } string & as_string() & { if(this->type_ != value_t::string) { detail::throw_bad_cast( "toml::value::as_string(): ", this->type_, *this); } return this->string_; } offset_datetime & as_offset_datetime() & { if(this->type_ != value_t::offset_datetime) { detail::throw_bad_cast( "toml::value::as_offset_datetime(): ", this->type_, *this); } return this->offset_datetime_; } local_datetime & as_local_datetime() & { if(this->type_ != value_t::local_datetime) { detail::throw_bad_cast( "toml::value::as_local_datetime(): ", this->type_, *this); } return this->local_datetime_; } local_date & as_local_date() & { if(this->type_ != value_t::local_date) { detail::throw_bad_cast( "toml::value::as_local_date(): ", this->type_, *this); } return this->local_date_; } local_time & as_local_time() & { if(this->type_ != value_t::local_time) { detail::throw_bad_cast( "toml::value::as_local_time(): ", this->type_, *this); } return this->local_time_; } array_type & as_array() & { if(this->type_ != value_t::array) { detail::throw_bad_cast( "toml::value::as_array(): ", this->type_, *this); } return this->array_.value(); } table_type & as_table() & { if(this->type_ != value_t::table) { detail::throw_bad_cast( "toml::value::as_table(): ", this->type_, *this); } return this->table_.value(); } // }}} // ------------------------------------------------------------------------ // rvalue reference {{{ boolean && as_boolean() && { if(this->type_ != value_t::boolean) { detail::throw_bad_cast( "toml::value::as_boolean(): ", this->type_, *this); } return std::move(this->boolean_); } integer && as_integer() && { if(this->type_ != value_t::integer) { detail::throw_bad_cast( "toml::value::as_integer(): ", this->type_, *this); } return std::move(this->integer_); } floating && as_floating() && { if(this->type_ != value_t::floating) { detail::throw_bad_cast( "toml::value::as_floating(): ", this->type_, *this); } return std::move(this->floating_); } string && as_string() && { if(this->type_ != value_t::string) { detail::throw_bad_cast( "toml::value::as_string(): ", this->type_, *this); } return std::move(this->string_); } offset_datetime && as_offset_datetime() && { if(this->type_ != value_t::offset_datetime) { detail::throw_bad_cast( "toml::value::as_offset_datetime(): ", this->type_, *this); } return std::move(this->offset_datetime_); } local_datetime && as_local_datetime() && { if(this->type_ != value_t::local_datetime) { detail::throw_bad_cast( "toml::value::as_local_datetime(): ", this->type_, *this); } return std::move(this->local_datetime_); } local_date && as_local_date() && { if(this->type_ != value_t::local_date) { detail::throw_bad_cast( "toml::value::as_local_date(): ", this->type_, *this); } return std::move(this->local_date_); } local_time && as_local_time() && { if(this->type_ != value_t::local_time) { detail::throw_bad_cast( "toml::value::as_local_time(): ", this->type_, *this); } return std::move(this->local_time_); } array_type && as_array() && { if(this->type_ != value_t::array) { detail::throw_bad_cast( "toml::value::as_array(): ", this->type_, *this); } return std::move(this->array_.value()); } table_type && as_table() && { if(this->type_ != value_t::table) { detail::throw_bad_cast( "toml::value::as_table(): ", this->type_, *this); } return std::move(this->table_.value()); } // }}} // accessors ============================================================= // // may throw type_error or out_of_range // value_type& at(const key& k) { if(!this->is_table()) { detail::throw_bad_cast( "toml::value::at(key): ", this->type_, *this); } if(this->as_table(std::nothrow).count(k) == 0) { detail::throw_key_not_found_error(*this, k); } return this->as_table(std::nothrow).at(k); } value_type const& at(const key& k) const { if(!this->is_table()) { detail::throw_bad_cast( "toml::value::at(key): ", this->type_, *this); } if(this->as_table(std::nothrow).count(k) == 0) { detail::throw_key_not_found_error(*this, k); } return this->as_table(std::nothrow).at(k); } value_type& operator[](const key& k) { if(this->is_uninitialized()) { *this = table_type{}; } else if(!this->is_table()) // initialized, but not a table { detail::throw_bad_cast( "toml::value::operator[](key): ", this->type_, *this); } return this->as_table(std::nothrow)[k]; } value_type& at(const std::size_t idx) { if(!this->is_array()) { detail::throw_bad_cast( "toml::value::at(idx): ", this->type_, *this); } if(this->as_array(std::nothrow).size() <= idx) { throw std::out_of_range(detail::format_underline( "toml::value::at(idx): no element corresponding to the index", { {this->location(), concat_to_string("the length is ", this->as_array(std::nothrow).size(), ", and the specified index is ", idx)} })); } return this->as_array().at(idx); } value_type const& at(const std::size_t idx) const { if(!this->is_array()) { detail::throw_bad_cast( "toml::value::at(idx): ", this->type_, *this); } if(this->as_array(std::nothrow).size() <= idx) { throw std::out_of_range(detail::format_underline( "toml::value::at(idx): no element corresponding to the index", { {this->location(), concat_to_string("the length is ", this->as_array(std::nothrow).size(), ", and the specified index is ", idx)} })); } return this->as_array(std::nothrow).at(idx); } value_type& operator[](const std::size_t idx) noexcept { // no check... return this->as_array(std::nothrow)[idx]; } value_type const& operator[](const std::size_t idx) const noexcept { // no check... return this->as_array(std::nothrow)[idx]; } void push_back(const value_type& x) { if(!this->is_array()) { detail::throw_bad_cast( "toml::value::push_back(value): ", this->type_, *this); } this->as_array(std::nothrow).push_back(x); return; } void push_back(value_type&& x) { if(!this->is_array()) { detail::throw_bad_cast( "toml::value::push_back(value): ", this->type_, *this); } this->as_array(std::nothrow).push_back(std::move(x)); return; } template value_type& emplace_back(Ts&& ... args) { if(!this->is_array()) { detail::throw_bad_cast( "toml::value::emplace_back(...): ", this->type_, *this); } this->as_array(std::nothrow).emplace_back(std::forward(args) ...); return this->as_array(std::nothrow).back(); } std::size_t size() const { switch(this->type_) { case value_t::array: { return this->as_array(std::nothrow).size(); } case value_t::table: { return this->as_table(std::nothrow).size(); } case value_t::string: { return this->as_string(std::nothrow).str.size(); } default: { throw type_error(detail::format_underline( "toml::value::size(): bad_cast to container types", { {this->location(), concat_to_string("the actual type is ", this->type_)} }), this->location()); } } } std::size_t count(const key_type& k) const { if(!this->is_table()) { detail::throw_bad_cast( "toml::value::count(key): ", this->type_, *this); } return this->as_table(std::nothrow).count(k); } bool contains(const key_type& k) const { if(!this->is_table()) { detail::throw_bad_cast( "toml::value::contains(key): ", this->type_, *this); } return (this->as_table(std::nothrow).count(k) != 0); } source_location location() const { return source_location(this->region_info_.get()); } comment_type const& comments() const noexcept {return this->comments_;} comment_type& comments() noexcept {return this->comments_;} private: void cleanup() noexcept { switch(this->type_) { case value_t::string : {string_.~string(); return;} case value_t::array : {array_.~array_storage(); return;} case value_t::table : {table_.~table_storage(); return;} default : return; } } // for error messages template friend region_base const* detail::get_region(const Value& v); template friend void detail::change_region(Value& v, detail::region reg); private: using array_storage = detail::storage; using table_storage = detail::storage; value_t type_; union { boolean boolean_; integer integer_; floating floating_; string string_; offset_datetime offset_datetime_; local_datetime local_datetime_; local_date local_date_; local_time local_time_; array_storage array_; table_storage table_; }; std::shared_ptr region_info_; comment_type comments_; }; // default toml::value and default array/table. // TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp using value = basic_value; using array = typename value::array_type; using table = typename value::table_type; template class T, template class A> inline bool operator==(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()) {return false;} if(lhs.comments() != rhs.comments()) {return false;} switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() == rhs.as_boolean(); } case value_t::integer : { return lhs.as_integer() == rhs.as_integer(); } case value_t::floating : { return lhs.as_floating() == rhs.as_floating(); } case value_t::string : { return lhs.as_string() == rhs.as_string(); } case value_t::offset_datetime: { return lhs.as_offset_datetime() == rhs.as_offset_datetime(); } case value_t::local_datetime: { return lhs.as_local_datetime() == rhs.as_local_datetime(); } case value_t::local_date: { return lhs.as_local_date() == rhs.as_local_date(); } case value_t::local_time: { return lhs.as_local_time() == rhs.as_local_time(); } case value_t::array : { return lhs.as_array() == rhs.as_array(); } case value_t::table : { return lhs.as_table() == rhs.as_table(); } case value_t::empty : {return true; } default: {return false;} } } template class T, template class A> inline bool operator!=(const basic_value& lhs, const basic_value& rhs) { return !(lhs == rhs); } template class T, template class A> typename std::enable_if::array_type>, detail::is_comparable::table_type> >::value, bool>::type operator<(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() < rhs.as_boolean() || (lhs.as_boolean() == rhs.as_boolean() && lhs.comments() < rhs.comments()); } case value_t::integer : { return lhs.as_integer() < rhs.as_integer() || (lhs.as_integer() == rhs.as_integer() && lhs.comments() < rhs.comments()); } case value_t::floating : { return lhs.as_floating() < rhs.as_floating() || (lhs.as_floating() == rhs.as_floating() && lhs.comments() < rhs.comments()); } case value_t::string : { return lhs.as_string() < rhs.as_string() || (lhs.as_string() == rhs.as_string() && lhs.comments() < rhs.comments()); } case value_t::offset_datetime: { return lhs.as_offset_datetime() < rhs.as_offset_datetime() || (lhs.as_offset_datetime() == rhs.as_offset_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_datetime: { return lhs.as_local_datetime() < rhs.as_local_datetime() || (lhs.as_local_datetime() == rhs.as_local_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_date: { return lhs.as_local_date() < rhs.as_local_date() || (lhs.as_local_date() == rhs.as_local_date() && lhs.comments() < rhs.comments()); } case value_t::local_time: { return lhs.as_local_time() < rhs.as_local_time() || (lhs.as_local_time() == rhs.as_local_time() && lhs.comments() < rhs.comments()); } case value_t::array : { return lhs.as_array() < rhs.as_array() || (lhs.as_array() == rhs.as_array() && lhs.comments() < rhs.comments()); } case value_t::table : { return lhs.as_table() < rhs.as_table() || (lhs.as_table() == rhs.as_table() && lhs.comments() < rhs.comments()); } case value_t::empty : { return lhs.comments() < rhs.comments(); } default: { return lhs.comments() < rhs.comments(); } } } template class T, template class A> typename std::enable_if::array_type>, detail::is_comparable::table_type> >::value, bool>::type operator<=(const basic_value& lhs, const basic_value& rhs) { return (lhs < rhs) || (lhs == rhs); } template class T, template class A> typename std::enable_if::array_type>, detail::is_comparable::table_type> >::value, bool>::type operator>(const basic_value& lhs, const basic_value& rhs) { return !(lhs <= rhs); } template class T, template class A> typename std::enable_if::array_type>, detail::is_comparable::table_type> >::value, bool>::type operator>=(const basic_value& lhs, const basic_value& rhs) { return !(lhs < rhs); } template class T, template class A> inline std::string format_error(const std::string& err_msg, const basic_value& v, const std::string& comment, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, {{v.location(), comment}}, std::move(hints), colorize); } template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, { {v1.location(), comment1}, {v2.location(), comment2} }, std::move(hints), colorize); } template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, const toml::basic_value& v3, const std::string& comment3, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, {{v1.location(), comment1}, {v2.location(), comment2}, {v3.location(), comment3} }, std::move(hints), colorize); } template class T, template class A> detail::return_type_of_t visit(Visitor&& visitor, const toml::basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid basic_value.", v, "here")); } template class T, template class A> detail::return_type_of_t visit(Visitor&& visitor, toml::basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid basic_value.", v, "here")); } template class T, template class A> detail::return_type_of_t visit(Visitor&& visitor, toml::basic_value&& v) { switch(v.type()) { case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} case value_t::integer : {return visitor(std::move(v.as_integer ()));} case value_t::floating : {return visitor(std::move(v.as_floating ()));} case value_t::string : {return visitor(std::move(v.as_string ()));} case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} case value_t::array : {return visitor(std::move(v.as_array ()));} case value_t::table : {return visitor(std::move(v.as_table ()));} case value_t::empty : break; default: break; } throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid basic_value.", v, "here")); } }// toml #endif// TOML11_VALUE toml11-3.8.1/toml/version.hpp000066400000000000000000000031771454646465200160410ustar00rootroot00000000000000#ifndef TOML11_VERSION_HPP #define TOML11_VERSION_HPP // This file checks C++ version. #ifndef __cplusplus # error "__cplusplus is not defined" #endif // Since MSVC does not define `__cplusplus` correctly unless you pass // `/Zc:__cplusplus` when compiling, the workaround macros are added. // Those enables you to define version manually or to use MSVC specific // version macro automatically. // // The value of `__cplusplus` macro is defined in the C++ standard spec, but // MSVC ignores the value, maybe because of backward compatibility. Instead, // MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in // the C++ standard. First we check the manual version definition, and then // we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`. // // FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 // #if defined(TOML11_ENFORCE_CXX11) # define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L #elif defined(TOML11_ENFORCE_CXX14) # define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L #elif defined(TOML11_ENFORCE_CXX17) # define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L #elif defined(TOML11_ENFORCE_CXX20) # define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L #elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER # define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG #else # define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900 # error "toml11 requires C++11 or later." #endif #endif// TOML11_VERSION_HPP