Compare commits

..

9 Commits

Author SHA1 Message Date
ToruNiina
3bf4ac0965 doc: update Fuzzy search section 2019-09-01 14:50:01 +09:00
ToruNiina
d53026837a test: add test for find<T> and check throw 2019-09-01 14:46:46 +09:00
ToruNiina
6d31cccc5b feat: throw if multiple keys match to the key 2019-09-01 14:45:56 +09:00
ToruNiina
99e46813f4 doc: add Fuzzy search section to README 2019-08-31 20:11:31 +09:00
ToruNiina
c0df39ca49 test: add test for find_fuzzy 2019-08-31 19:30:19 +09:00
ToruNiina
3e6747cfeb feat: add find_fuzzy and suggeston
Add the following
- find_fuzzy<T>(val, "key")
- find<T>(val, "key", FuzzyMatcher) to suggest typo
- levenstein_matcher
2019-08-31 19:28:50 +09:00
ToruNiina
4cebd660fd refactor: use as_xxx to cast toml value
and store rvalue reference as a value instead of lvalue
2019-08-31 18:22:36 +09:00
ToruNiina
43907de365 refactor: check key types in find(v, k1, k2, ...)
ks should be convertible to toml::key
2019-08-31 17:28:07 +09:00
ToruNiina
9b43171b65 refactor: split get.hpp to get/find.hpp 2019-08-31 14:49:00 +09:00
54 changed files with 2999 additions and 5496 deletions

View File

@@ -12,7 +12,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_toml_test.cpp -o check_toml_test g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test $GOPATH/bin/toml-test ./check_toml_test
test_serialization: test_serialization:
@@ -24,7 +24,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_serialization.cpp -o check_serialization 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 git clone https://github.com/BurntSushi/toml-test.git
cp check_serialization toml-test/tests/valid cp check_serialization toml-test/tests/valid
cd toml-test/tests/valid cd toml-test/tests/valid
@@ -47,7 +47,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check.cpp -o check g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check
git clone https://github.com/BurntSushi/toml-test.git git clone https://github.com/BurntSushi/toml-test.git
cp check toml-test/tests/invalid cp check toml-test/tests/invalid
cp check toml-test/tests/valid cp check toml-test/tests/valid

View File

@@ -1,151 +0,0 @@
name: build
on: [push, pull_request]
jobs:
build-linux-gcc:
runs-on: Ubuntu-18.04
strategy:
matrix:
# g++-4.8 and 4.9 are tested on Travis.CI.
compiler: ['g++-9', 'g++-8', 'g++-7', 'g++-6', 'g++-5']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: 'g++-5', standard: '17'}
- {compiler: 'g++-6', standard: '17'}
- {compiler: 'g++-5', standard: '20'}
- {compiler: 'g++-6', standard: '20'}
- {compiler: 'g++-7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install boost1.70
if [[ "${{ matrix.compiler }}" == "g++-6" || "${{ matrix.compiler }}" == "g++-5" ]] ; then
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install ${{ matrix.compiler }}
fi
- 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
else
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
fi
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-linux-clang:
runs-on: Ubuntu-18.04
strategy:
matrix:
compiler: ['10', '9', '8', '7', '6.0', '5.0', '4.0', '3.9']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: '3.9', standard: '17'}
- {compiler: '4.0', standard: '17'}
- {compiler: '5.0', standard: '17'}
- {compiler: '3.9', standard: '20'}
- {compiler: '4.0', standard: '20'}
- {compiler: '5.0', standard: '20'}
- {compiler: '6.0', standard: '20'}
- {compiler: '7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install boost1.70
if [[ "${{ matrix.compiler }}" != "6" && "${{ matrix.compiler }}" != "8" && "${{ matrix.compiler }}" != "9" ]] ; then
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install clang-${{ matrix.compiler }}
fi
- 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 }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-osx:
runs-on: macos-10.15
strategy:
matrix:
standard: ['11', '14', '17', '20']
steps:
- name: Checkout
uses: actions/checkout@v2
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 }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-windows-msvc:
runs-on: windows-2019
strategy:
matrix:
standard: ['11', '14', '17', '20']
config: ['Release', 'Debug']
steps:
- name: Checkout
uses: actions/checkout@v2
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"
- name: Build
working-directory: ./build
run: |
cmake --build . --config "${{ matrix.config }}"
- name: Test
working-directory: ./build
run: |
./tests/test_literals --log_level=all
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

View File

@@ -2,30 +2,6 @@ dist: trusty
matrix: matrix:
include: include:
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-4.8" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-4.9" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.9
- boost1.70
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
@@ -33,7 +9,7 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-5 - g++-5
@@ -45,7 +21,7 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-6 - g++-6
@@ -57,7 +33,7 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-7 - g++-7
@@ -69,7 +45,7 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
@@ -77,47 +53,11 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=ON env: COMPILER="g++-8" CXX_STANDARD=17
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=14
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
@@ -125,16 +65,16 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-3.9" CXX_STANDARD=11 env: COMPILER="clang++-3.7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-3.9
packages: packages:
- g++-8 - g++-8
- clang-3.9 - clang-3.7
- boost1.70 - boost1.70
- os: linux - os: linux
language: cpp language: cpp
@@ -143,9 +83,9 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-4.0 - llvm-toolchain-trusty-4.0
- sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
- clang-4.0 - clang-4.0
@@ -157,9 +97,9 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-5.0 - llvm-toolchain-trusty-5.0
- sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
- clang-5.0 - clang-5.0
@@ -171,9 +111,9 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-6.0 - llvm-toolchain-trusty-6.0
- sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
- clang-6.0 - clang-6.0
@@ -185,9 +125,9 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-7 - llvm-toolchain-trusty-7
- sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
- clang-7 - clang-7
@@ -199,9 +139,9 @@ matrix:
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8 - llvm-toolchain-trusty-8
- sourceline: 'ppa:mhier/libboost-latest'
packages: packages:
- g++-8 - g++-8
- clang-8 - clang-8
@@ -209,83 +149,13 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=ON env: COMPILER="clang++-8" CXX_STANDARD=17
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test' - ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8 - llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=14
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest' - sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_ASAN=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_UBSAN=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages: packages:
- clang-8 - clang-8
- g++-8 - g++-8
@@ -293,6 +163,7 @@ matrix:
- os: osx - os: osx
language: cpp language: cpp
compiler: clang compiler: clang
env: CXX_STANDARD=11
script: script:
- | - |
@@ -301,37 +172,13 @@ script:
travis_retry wget "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz" travis_retry wget "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz"
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1 tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
else
brew upgrade cmake boost
fi fi
- |
if [[ "${CXX_STANDARD}" == "" ]]; then
export CXX_STANDARD="11"
fi
- |
if [[ "${TOML_HEAD}" != "ON" ]]; then
export TOML_HEAD="OFF"
fi
- echo "TOML_HEAD = ${TOML_HEAD}"
- |
if [[ "${WITH_ASAN}" != "ON" ]]; then
export WITH_ASAN="OFF"
fi
- echo "WITH_ASAN = ${WITH_ASAN}"
- |
if [[ "${WITH_UBSAN}" != "ON" ]]; then
export WITH_UBSAN="OFF"
fi
- echo "WITH_UBSAN = ${WITH_UBSAN}"
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- |
if [[ "${REQUIRE_FILESYSTEM_LIBRARY}" != "ON" ]]; then
export REQUIRE_FILESYSTEM_LIBRARY="OFF"
fi
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- cmake --version - cmake --version
- mkdir build - mkdir build
- cd build - cd build
- echo "COMPILER = ${COMPILER}" - git clone https://github.com/toml-lang/toml.git
- echo "CXX_STANDARD = ${CXX_STANDARD}" - cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD ..
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=${REQUIRE_FILESYSTEM_LIBRARY} -Dtoml11_BUILD_TEST=ON -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- make - make
- ctest --output-on-failure - ctest --output-on-failure

View File

@@ -1,17 +1,24 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 2.8)
enable_testing() enable_testing()
project(toml11 VERSION 3.6.1) project(toml11)
option(toml11_BUILD_TEST "Build toml tests" OFF) set(toml11_VERSION_MAYOR 3)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF) set(toml11_VERSION_MINOR 0)
option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF) set(toml11_VERSION_PATCH 1)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
option(toml11_BUILD_TEST "Build toml tests" ON)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1) if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard whose features are requested to build all targets.") set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.") if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.") set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON)
else() else()
# Manually check for C++11 compiler flag. # Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
@@ -37,11 +44,6 @@ else()
endif() endif()
endif() endif()
if(MSVC)
add_definitions("/Zc:__cplusplus") # define __cplusplus value correctly
add_definitions("/utf-8") # enable to use u8"" literal
endif()
# Set some common directories # Set some common directories
include(GNUInstallDirs) include(GNUInstallDirs)
set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11) set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11)

644
README.md
View File

@@ -1,7 +1,6 @@
toml11 toml11
====== ======
[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions)
[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11) [![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![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 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) [![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master)
@@ -9,16 +8,16 @@ toml11
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![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) [![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. toml11 is a C++11 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). compatible to the latest version of
- 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). [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). after version 2.0.0.
- 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 passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings. Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It supports user-defined type conversion from/into toml values. You can see the error messages about invalid files and serialization results of valid files at
- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows. [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
## Example ## Example
@@ -28,29 +27,15 @@ toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C
int main() int main()
{ {
// ```toml const auto data = toml::parse("example.toml");
// title = "an example toml file" // 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<std::string>(data, "title"); std::string title = toml::find<std::string>(data, "title");
std::cout << "the title is " << title << std::endl;
// convert the whole array into any container automatically // nums = [1, 2, 3, 4, 5]
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums"); std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
std::cout << "the length of `nums` is" << nums.size() << std::endl;
// access with STL-like manner
if(not data.contains("foo"))
{
data["foo"] = "bar";
}
// pass a fallback
std::string name = toml::find_or<std::string>(data, "name", "not found");
// width-dependent formatting
std::cout << std::setw(80) << data << std::endl;
return 0; return 0;
} }
@@ -83,12 +68,8 @@ int main()
- [TOML literal](#toml-literal) - [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [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) - [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types) - [Underlying types](#underlying-types)
- [Unreleased TOML features](#unreleased-toml-features)
- [Breaking Changes from v2](#breaking-changes-from-v2) - [Breaking Changes from v2](#breaking-changes-from-v2)
- [Running Tests](#running-tests) - [Running Tests](#running-tests)
- [Contributors](#contributors) - [Contributors](#contributors)
@@ -99,7 +80,7 @@ int main()
Just include the file after adding it to the include path. Just include the file after adding it to the include path.
```cpp ```cpp
#include <toml.hpp> // that's all! now you can use it. #include <toml11/toml.hpp> // that's all! now you can use it.
#include <iostream> #include <iostream>
int main() int main()
@@ -114,8 +95,6 @@ int main()
The convenient way is to add this repository as a git-submodule or to install The convenient way is to add this repository as a git-submodule or to install
it in your system by CMake. it in your system by CMake.
Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly.
## Decoding a toml file ## Decoding a toml file
To parse a toml file, the only thing you have to do is To parse a toml file, the only thing you have to do is
@@ -126,9 +105,6 @@ const std::string fname("sample.toml");
const toml::value data = toml::parse(fname); 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`. 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. You can also pass a `std::istream` to the `toml::parse` function.
@@ -186,7 +162,7 @@ what(): [error] bad time: should be HH:MM:SS.subsec
--> ./datetime-malformed-no-secs.toml --> ./datetime-malformed-no-secs.toml
1 | no-secs = 1987-07-05T17:45Z 1 | no-secs = 1987-07-05T17:45Z
| ^------- HH:MM:SS.subsec | ^------- HH:MM:SS.subsec
| |
Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 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 Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32
``` ```
@@ -276,11 +252,11 @@ shape = "round"
``` cpp ``` cpp
const auto data = toml::parse("fruit.toml"); const auto data = toml::parse("fruit.toml");
const auto& fruit = toml::find(data, "fruit"); const auto& fruit = toml::find(data, "fruit");
const auto name = toml::find<std::string>(fruit, "name"); const auto name = toml::find<std::string>(fruit, "apple");
const auto& physical = toml::find(fruit, "physical"); const auto& physical = toml::find(fruit, "physical");
const auto color = toml::find<std::string>(physical, "color"); const auto color = toml::find<std::string>(fruit, "color");
const auto shape = toml::find<std::string>(physical, "shape"); const auto shape = toml::find<std::string>(fruit, "shape");
``` ```
Here, variable `fruit` is a `toml::value` and can be used as the first argument Here, variable `fruit` is a `toml::value` and can be used as the first argument
@@ -294,75 +270,6 @@ const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape"); const auto shape = toml::find<std::string>(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<std::string>(values, 1);
```
`toml::find` can also search array recursively.
```cpp
const auto data = toml::parse("fruit.toml");
const auto bar = toml::find<std::string>(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 ### Dotted keys
TOML v0.5.0 has a new feature named "dotted keys". TOML v0.5.0 has a new feature named "dotted keys".
@@ -403,6 +310,116 @@ The above code works with the following toml file.
# NOT {"physical": {"color": "orange"}}. # NOT {"physical": {"color": "orange"}}.
``` ```
### In case of error
If the value does not exist, `toml::find` throws an error 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
```
**Note**: It is recommended to find a table as `toml::value` because it has much information
compared to `toml::table`, which is an alias of
`std::unordered_map<std::string, toml::value>`. Since `toml::table` does not have
any information about toml file, such as where the table was defined in the file.
----
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.
### Fuzzy Search
To find a value, you can use `find_fuzzy` instead of `find`.
```toml
[foobar]
# typo!
anseer = 42
```
```cpp
const auto data = toml::parse("sample.toml");
const auto foobar = toml::find(data, "foobar");
const auto answer = toml::find_fuzzy<int>(data, "answer"); // it finds "anseer".
```
When the specified key is not found, `toml::find_fuzzy` calculates
[levenstein distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
between the specified key and other keys.
If it finds a key that is 1 away from the specified key by the Levenstein
distance, it returns the corresponding value.
To allow a more distant string, you can explicitly pass `toml::levenstein_matcher`
to `find_fuzzy`.
```cpp
toml::levenstein_matcher lev(2); // allow distance <= 2
const auto answer = toml::find_fuzzy<int>(data, "answer", lev);
```
You can also use your own distance metric. Implement your `fuzzy_matcher` that
has `operator()` that takes two strings and returns true if two strings resemble
each other.
```cpp
struct fuzzy_matcher
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
// return true if lhs matches with rhs.
}
};
```
If there are multiple keys that meets the condition, it throws `runtime_error`.
However, in many cases, rather than just allowing typographical errors,
you will want to suggest it and encouledge users to correct it.
If you pass a `fuzzy_matcher` to `toml::find`, a suggestion will be displayed
in the error message.
```cpp
toml::levenstein_matcher lev(1); // finds keys within distance <= 1
const auto answer = toml::find<int>(data, "answer", lev); // it throws!
```
```console
terminate called after throwing an instance of 'std::out_of_range'
what(): [error] key "answer" not found.
--> hoge.toml
1 | [foobar]
| ~~~~~~~~ in this table
...
2 | anseer = 42
| ~~ did you mean this here?
```
Note: Currently, `find_fuzzy` and `find(value, key, matcher)` take only one key.
The codes like `find_fuzzy(value, key1, key2, key3)` do not work.
## Casting a toml value ## Casting a toml value
@@ -472,24 +489,6 @@ if(answer.is_integer() && answer.as_integer(std::nothrow) == 42)
If `std::nothrow` is passed, the functions are marked as noexcept. 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& [k, v] : data.as_table())
{
std::cout << k << '\n';
}
const auto& fruits = toml::find(data, "fruits");
for(const auto& v : fruits.as_array())
{
std::cout << toml::find<std::string>(v, "name") << '\n';
}
```
The full list of the functions is below. The full list of the functions is below.
```cpp ```cpp
@@ -525,60 +524,6 @@ class value {
} // toml } // 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 ## Checking value type
You can check the type of a value by `is_xxx` function. You can check the type of a value by `is_xxx` function.
@@ -801,7 +746,6 @@ 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`. represents a duration from the beginning of the day, `00:00:00.000`.
```toml ```toml
# sample.toml
date = 2018-12-23 date = 2018-12-23
time = 12:30:00 time = 12:30:00
l_dt = 2018-12-23T12:30:00 l_dt = 2018-12-23T12:30:00
@@ -818,12 +762,8 @@ const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt
const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
``` ```
`local_date` and `local_datetime` are assumed to be in the local timezone when toml11 defines its own datetime classes.
they are converted into `time_point`. On the other hand, `offset_datetime` only You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
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 ## Getting with a fallback
@@ -936,25 +876,14 @@ toml::value v(toml::local_time(std::chrono::hours(10)));
``` ```
You can construct an array object not only from `initializer_list`, but also 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 from STL containers.
`toml::value`.
```cpp ```cpp
std::vector<int> vec{1,2,3,4,5}; std::vector<int> vec{1,2,3,4,5};
toml::value v(vec); toml::value v = vec;
``` ```
When you construct an array value, all the elements of `initializer_list` All the elements of `initializer_list` should be convertible into `toml::value`.
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 ## Preserving comments
@@ -1171,7 +1100,7 @@ const auto data = toml::parse("example.toml");
const foo f = toml::find<ext::foo>(data, "foo"); const foo f = toml::find<ext::foo>(data, "foo");
``` ```
There are 3 ways to use `toml::get` with the types that you defined. There are 2 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function. The first one is to implement `from_toml(const toml::value&)` member function.
@@ -1198,31 +1127,7 @@ struct foo
In this way, because `toml::get` first constructs `foo` without arguments, In this way, because `toml::get` first constructs `foo` without arguments,
the type should be default-constructible. the type should be default-constructible.
The second is to implement `constructor(const toml::value&)`. The second is to implement specialization of `toml::from` for your type.
```cpp
namespace ext
{
struct foo
{
explicit foo(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<double>(v, "b")),
c(toml::find<std::string>(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<foo>`), you may need to define default
constructor explicitly.
The third is to implement specialization of `toml::from` for your type.
```cpp ```cpp
namespace ext namespace ext
@@ -1240,7 +1145,7 @@ namespace toml
template<> template<>
struct from<ext::foo> struct from<ext::foo>
{ {
static ext::foo from_toml(const value& v) ext::foo from_toml(const value& v)
{ {
ext::foo f; ext::foo f;
f.a = find<int >(v, "a"); f.a = find<int >(v, "a");
@@ -1280,7 +1185,7 @@ template<>
struct from<ext::foo> struct from<ext::foo>
{ {
template<typename C, template<typename ...> class M, template<typename ...> class A> template<typename C, template<typename ...> class M, template<typename ...> class A>
static ext::foo from_toml(const basic_value<C, M, A>& v) ext::foo from_toml(const basic_value<C, M, A>& v)
{ {
ext::foo f; ext::foo f;
f.a = find<int >(v, "a"); f.a = find<int >(v, "a");
@@ -1307,9 +1212,9 @@ struct foo
double b; double b;
std::string c; std::string c;
toml::value into_toml() const // you need to mark it const. toml::table into_toml() const // you need to mark it const.
{ {
return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}}; return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
} }
}; };
} // ext } // ext
@@ -1336,9 +1241,9 @@ namespace toml
template<> template<>
struct into<ext::foo> struct into<ext::foo>
{ {
static toml::value into_toml(const ext::foo& f) toml::table into_toml(const ext::foo& f)
{ {
return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}}; return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
} }
}; };
} // toml } // toml
@@ -1350,27 +1255,6 @@ toml::value v(f);
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
`toml::array` are okay to return from `into_toml`. `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<ext::foo>
{
static toml::basic_value<toml::preserve_comments> into_toml(const ext::foo& f)
{
toml::basic_value<toml::preserve_comments> 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.
## Formatting user-defined error messages ## Formatting user-defined error messages
When you encounter an error after you read the toml value, you may want to When you encounter an error after you read the toml value, you may want to
@@ -1424,28 +1308,7 @@ you will get an error message like this.
| ~~ maximum number here | ~~ maximum number here
``` ```
You can print hints at the end of the message. ### Obtaining location information
```cpp
std::vector<std::string> 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`. You can also format error messages in your own way by using `source_location`.
@@ -1470,102 +1333,6 @@ const toml::value v = /*...*/;
const toml::source_location loc = v.location(); 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 defalut 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 colorize `[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.
## Serializing TOML data ## Serializing TOML data
toml11 enables you to serialize data into toml format. toml11 enables you to serialize data into toml format.
@@ -1698,90 +1465,7 @@ This feature is introduced to make it easy to write a custom serializer.
Because `std::chrono::system_clock::time_point` is a __time point__, Because `std::chrono::system_clock::time_point` is a __time point__,
not capable of representing a Local Time independent from a specific day. not capable of representing a Local Time independent from a specific day.
## Unreleased TOML features It is recommended to get `datetime`s as `std::chrono` classes through `toml::get`.
Since TOML v1.0.0-rc.1 has been released, those features are now activated by
default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`.
- Leading zeroes in exponent parts of floats are permitted.
- e.g. `1.0e+01`, `5e+05`
- [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656)
- Allow raw tab characters in basic strings and multi-line basic strings.
- [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627)
- Allow heterogeneous arrays
- [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676)
## 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 ## Breaking Changes from v2
@@ -1819,13 +1503,14 @@ Such a big change will not happen in the coming years.
## Running Tests ## Running Tests
After cloning this repository, run the following command (thank you @jwillikers To run test codes, you need to clone toml-lang/toml repository under `build/` directory
for automating test set fetching!). because some of the test codes read a file in the repository.
```sh ```sh
$ mkdir build $ mkdir build
$ cd build $ cd build
$ cmake .. -Dtoml11_BUILD_TEST=ON $ git clone https://github.com/toml-lang/toml.git
$ cmake ..
$ make $ make
$ make test $ make test
``` ```
@@ -1851,40 +1536,11 @@ I appreciate the help of the contributors who introduced the great feature to th
- Fixed warnings while type conversion - Fixed warnings while type conversion
- @KerstinKeller - @KerstinKeller
- Added installation script to CMake - 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
- 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
- OGAWA KenIchi (@kenichiice)
- Fix include path in README
- Mohammed Alyousef (@MoAlyousef)
- Made testing optional in CMake
- Ivan Shynkarenka (@chronoxor)
- Fix compilation error in `<filesystem>` with MinGW
- Alex Merry (@amerry)
- Add missing include files
- sneakypete81 (@sneakypete81)
- Fix typo in error message
## Licensing terms ## Licensing terms
This product is licensed under the terms of the [MIT License](LICENSE). This product is licensed under the terms of the [MIT License](LICENSE).
- Copyright (c) 2017-2020 Toru Niina - Copyright (c) 2017-2019 Toru Niina
All rights reserved. All rights reserved.

View File

@@ -17,9 +17,10 @@ build_script:
- cd C:\toml11 - cd C:\toml11
- mkdir build - mkdir build
- cd build - cd build
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 -Dtoml11_BUILD_TEST=ON .. - git clone https://github.com/toml-lang/toml.git
- file --mime-encoding toml/tests/hard_example_unicode.toml
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 ..
- cmake --build . --config "%configuration%" - cmake --build . --config "%configuration%"
- file --mime-encoding tests/toml/tests/hard_example_unicode.toml
test_script: test_script:
- ctest --build-config "%configuration%" --timeout 300 --output-on-failure - ctest --build-config "%configuration%" --timeout 300 --output-on-failure

View File

@@ -1,12 +1,3 @@
include(ExternalProject)
ExternalProject_Add(toml
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/toml
GIT_REPOSITORY https://github.com/toml-lang/toml
GIT_TAG v0.5.0
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
set(TEST_NAMES set(TEST_NAMES
test_datetime test_datetime
test_string test_string
@@ -36,6 +27,7 @@ set(TEST_NAMES
test_get_or test_get_or
test_find test_find
test_find_or test_find_or
test_find_fuzzy
test_expect test_expect
test_parse_file test_parse_file
test_serialize_file test_serialize_file
@@ -48,17 +40,6 @@ set(TEST_NAMES
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA) CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA)
CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC) 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)
if(COMPILER_SUPPORTS_WALL) if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -69,44 +50,6 @@ endif()
if(COMPILER_SUPPORTS_WPEDANTIC) if(COMPILER_SUPPORTS_WPEDANTIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
endif() endif()
if(COMPILER_SUPPORTS_WERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
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()
option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF)
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 # Disable some MSVC warnings
if(MSVC) if(MSVC)
@@ -150,61 +93,11 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED)
add_definitions(-DBOOST_TEST_DYN_LINK) add_definitions(-DBOOST_TEST_DYN_LINK)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST) add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
# check which standard library implementation is used. If libstdc++ is used,
# it will fail to compile. It compiles if libc++ is used.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <cstddef>
#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 <filesystem>.
# 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)
foreach(TEST_NAME ${TEST_NAMES}) foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME} ${TEST_NAME}.cpp) add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11) target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
# to compile tests with <filesystem>...
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}) target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
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 the PATH to be able to find Boost DLL # Set the PATH to be able to find Boost DLL
if(WIN32) if(WIN32)
@@ -215,15 +108,8 @@ foreach(TEST_NAME ${TEST_NAMES})
endif() endif()
endforeach(TEST_NAME) endforeach(TEST_NAME)
# this test is to check it compiles. it will not run # this test is to check it compiles. it will not run
add_executable(test_multiple_translation_unit add_executable(test_multiple_translation_unit
test_multiple_translation_unit_1.cpp test_multiple_translation_unit_1.cpp
test_multiple_translation_unit_2.cpp) test_multiple_translation_unit_2.cpp)
target_link_libraries(test_multiple_translation_unit toml11::toml11) 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()

View File

@@ -84,7 +84,9 @@ struct json_serializer
{ {
if(!is_first) {std::cout << ", ";} if(!is_first) {std::cout << ", ";}
is_first = false; is_first = false;
std::cout << this->format_key(elem.first) << ':'; std::cout << toml::format(toml::value(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second); toml::visit(*this, elem.second);
} }
std::cout << '}'; std::cout << '}';
@@ -110,12 +112,6 @@ struct json_serializer
} }
return retval; return retval;
} }
std::string format_key(const std::string& s) const
{
const auto quote("\"");
return quote + escape_string(s) + quote;
}
}; };
int main() int main()

View File

@@ -11,7 +11,7 @@
BOOST_AUTO_TEST_CASE(test_comment_before) BOOST_AUTO_TEST_CASE(test_comment_before)
{ {
{ {
const std::string file = R"( const std::string file = u8R"(
# comment for a. # comment for a.
a = 42 a = 42
# comment for b. # comment for b.
@@ -24,12 +24,12 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().front() == u8" comment for b.");
} }
{ {
const std::string file = R"( const std::string file = u8R"(
# comment for a. # comment for a.
# another comment for a. # another comment for a.
a = 42 a = 42
@@ -45,18 +45,18 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == " another comment for a."); BOOST_TEST(a.comments().back() == u8" another comment for a.");
BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == " also comment for b."); BOOST_TEST(b.comments().back() == u8" also comment for b.");
} }
} }
BOOST_AUTO_TEST_CASE(test_comment_inline) BOOST_AUTO_TEST_CASE(test_comment_inline)
{ {
{ {
const std::string file = R"( const std::string file = u8R"(
a = 42 # comment for a. a = 42 # comment for a.
b = "baz" # comment for b. b = "baz" # comment for b.
)"; )";
@@ -68,12 +68,12 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().front() == u8" comment for b.");
} }
{ {
const std::string file = R"( const std::string file = u8R"(
a = [ a = [
42, 42,
] # comment for a. ] # comment for a.
@@ -90,18 +90,18 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b0 = b.as_array().at(0); const auto& b0 = b.as_array().at(0);
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == " this is a comment for b."); BOOST_TEST(b.comments().front() == u8" this is a comment for b.");
BOOST_TEST(b0.comments().size() == 1u); BOOST_TEST(b0.comments().size() == 1u);
BOOST_TEST(b0.comments().front() == " this is not a comment for b, but \"bar\""); BOOST_TEST(b0.comments().front() == u8" this is not a comment for b, but \"bar\"");
} }
} }
BOOST_AUTO_TEST_CASE(test_comment_both) BOOST_AUTO_TEST_CASE(test_comment_both)
{ {
{ {
const std::string file = R"( const std::string file = u8R"(
# comment for a. # comment for a.
a = 42 # inline comment for a. a = 42 # inline comment for a.
# comment for b. # comment for b.
@@ -122,62 +122,25 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
const auto& c0 = c.as_array().at(0); const auto& c0 = c.as_array().at(0);
BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == " comment for a."); BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == " inline comment for a."); BOOST_TEST(a.comments().back() == u8" inline comment for a.");
BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == " comment for b."); BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == " inline comment for b."); BOOST_TEST(b.comments().back() == u8" inline comment for b.");
BOOST_TEST(c.comments().size() == 2u); BOOST_TEST(c.comments().size() == 2u);
BOOST_TEST(c.comments().front() == " comment for c."); BOOST_TEST(c.comments().front() == u8" comment for c.");
BOOST_TEST(c.comments().back() == " another comment for c."); BOOST_TEST(c.comments().back() == u8" another comment for c.");
BOOST_TEST(c0.comments().size() == 2u); BOOST_TEST(c0.comments().size() == 2u);
BOOST_TEST(c0.comments().front() == " comment for the first element."); BOOST_TEST(c0.comments().front() == u8" comment for the first element.");
BOOST_TEST(c0.comments().back() == " this also."); BOOST_TEST(c0.comments().back() == u8" 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<toml::preserve_comments>(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<toml::preserve_comments>(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) BOOST_AUTO_TEST_CASE(test_discard_comment)
{ {
const std::string file = R"( const std::string file = u8R"(
# comment for a. # comment for a.
a = 42 # inline comment for a. a = 42 # inline comment for a.
# comment for b. # comment for b.

View File

@@ -80,14 +80,10 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value)
BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array)
{ {
#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS
std::istringstream stream(std::string( std::istringstream stream(std::string(
"a = [1, 1.0]\n" "a = [1, 1.0]\n"
)); ));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); 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) BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table)

View File

@@ -33,27 +33,6 @@ struct bar
return toml::table{{"a", this->a}, {"b", this->b}}; 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<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib } // extlib
namespace toml namespace toml
@@ -70,25 +49,7 @@ struct from<extlib::foo>
template<> template<>
struct into<extlib::foo> struct into<extlib::foo>
{ {
static toml::value into_toml(const extlib::foo& f) static toml::table into_toml(const extlib::foo& f)
{
return toml::value{{"a", f.a}, {"b", f.b}};
}
};
template<>
struct from<extlib::baz>
{
static extlib::baz from_toml(const toml::value& v)
{
return extlib::baz{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib::qux>
{
static toml::table into_toml(const extlib::qux& f)
{ {
return toml::table{{"a", f.a}, {"b", f.b}}; return toml::table{{"a", f.a}, {"b", f.b}};
} }
@@ -122,27 +83,6 @@ struct bar
return toml::table{{"a", this->a}, {"b", this->b}}; 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<typename C, template<typename ...> class M, template<typename ...> class A>
explicit foobar(const toml::basic_value<C, M, A>& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib2 } // extlib2
namespace toml namespace toml
@@ -165,28 +105,6 @@ struct into<extlib2::foo>
return toml::table{{"a", f.a}, {"b", f.b}}; return toml::table{{"a", f.a}, {"b", f.b}};
} }
}; };
template<>
struct from<extlib2::baz>
{
template<typename C, template<typename ...> class M, template<typename ...> class A>
static extlib2::baz from_toml(const toml::basic_value<C, M, A>& v)
{
return extlib2::baz{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib2::qux>
{
static toml::basic_value<toml::preserve_comments, std::map>
into_toml(const extlib2::qux& f)
{
return toml::basic_value<toml::preserve_comments, std::map>{
{"a", f.a}, {"b", f.b}
};
}
};
} // toml } // toml
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -270,62 +188,6 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
} }
} }
BOOST_AUTO_TEST_CASE(test_conversion_one_way)
{
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto baz = toml::get<extlib::baz>(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<int>(v, "a") == 42);
BOOST_TEST(toml::find<std::string>(v, "b") == "qux");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "baz"}
};
const auto baz = toml::get<extlib2::baz>(v);
BOOST_TEST(baz.a == 42);
BOOST_TEST(baz.b == "baz");
}
{
const extlib::qux q{42, "qux"};
const toml::basic_value<toml::preserve_comments, std::map> v(q);
BOOST_TEST(toml::find<int>(v, "a") == 42);
BOOST_TEST(toml::find<std::string>(v, "b") == "qux");
}
}
BOOST_AUTO_TEST_CASE(test_conversion_via_constructor)
{
{
const toml::value v{{"a", 42}, {"b", "foobar"}};
const auto foobar = toml::get<extlib::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "foobar"}
};
const auto foobar = toml::get<extlib2::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion) BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{ {
{ {
@@ -426,122 +288,5 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion)
BOOST_TEST(bars.at(2).b == "quux"); BOOST_TEST(bars.at(2).b == "quux");
BOOST_TEST(bars.at(3).b == "foobar"); 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<std::vector<extlib::foobar>>(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<std::vector<extlib2::foobar>>(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<toml::discard_comments, std::map, std::deque>
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<std::vector<extlib2::foobar>>(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<std::map<std::string, extlib::foobar>>(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<std::map<std::string, extlib2::foobar>>(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<toml::discard_comments, std::map, std::deque>
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<std::map<std::string, extlib::foobar>>(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");
}
} }

View File

@@ -27,32 +27,6 @@ using test_value_types = std::tuple<
BOOST_AUTO_TEST_CASE(test_find_throws) 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<toml::boolean>(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<toml::boolean>(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<toml::integer>(v, "different_key"),
std::out_of_range);
}
{
// the positive control.
const toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(v, "key"));
}
// -----------------------------------------------------------------------
// reference version
{ {
// value is not a table // value is not a table
toml::value v(true); toml::value v(true);
@@ -74,103 +48,6 @@ BOOST_AUTO_TEST_CASE(test_find_throws)
toml::value v{{"key", 42}}; toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(v, "key")); BOOST_TEST(42 == toml::find<int>(v, "key"));
} }
// -----------------------------------------------------------------------
// move version
{
// value is not a table
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(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<toml::boolean>(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<toml::integer>(std::move(v), "different_key"),
std::out_of_range);
}
{
// the positive control.
toml::value v{{"key", 42}};
BOOST_TEST(42 == toml::find<int>(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<toml::boolean>(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<toml::boolean>(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<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
const toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(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<toml::boolean>(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<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(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<toml::boolean>(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<toml::integer>(std::move(v), 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(std::move(v), 2));
}
} }
BOOST_AUTO_TEST_CASE(test_find_recursive) BOOST_AUTO_TEST_CASE(test_find_recursive)
@@ -197,77 +74,6 @@ BOOST_AUTO_TEST_CASE(test_find_recursive)
auto& num2 = toml::find<toml::integer>(v, a, b, c, d); auto& num2 = toml::find<toml::integer>(v, a, b, c, d);
num2 = 42; num2 = 42;
BOOST_TEST(42 == toml::find<int>(v, a, b, c, d)); BOOST_TEST(42 == toml::find<int>(v, a, b, c, d));
auto num3 = toml::find<toml::integer>(v, a, "b", c, "d");
BOOST_TEST(42 == num3);
auto num4 = toml::find<toml::integer>(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<std::string>(v, 0, 0));
BOOST_TEST("of" == toml::find<std::string>(v, 0, 1));
BOOST_TEST("string" == toml::find<std::string>(v, 0, 2));
BOOST_TEST(1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST(2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(3 == toml::find<int>(v, 1, 0, 2));
BOOST_TEST(3.14 == toml::find<double>(v, 1, 1, 0));
BOOST_TEST(2.71 == toml::find<double>(v, 1, 1, 1));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, 1, 0, 2);
num = 42;
BOOST_TEST( 1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST( 2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(42 == toml::find<int>(v, 1, 0, 2));
// move value
auto num2 = toml::find<toml::integer>(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<int>(v, "array", 0, 0));
BOOST_TEST(2 == toml::find<int>(v, "array", 0, 1));
BOOST_TEST(3 == toml::find<int>(v, "array", 0, 2));
BOOST_TEST("bar" == toml::find<std::string>(v, "array", 1, 0, "foo"));
BOOST_TEST("qux" == toml::find<std::string>(v, "array", 1, 0, "baz"));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, "pi"));
BOOST_TEST(2.71 == toml::find<double>(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<double>(v, ar, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi_c));
} }
} }
@@ -279,9 +85,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::boolean>(v, "key") = false; toml::find<toml::boolean>(v, "key") = false;
BOOST_TEST(false == toml::find<toml::boolean>(v, "key")); BOOST_TEST(false == toml::find<toml::boolean>(v, "key"));
const auto moved = toml::find<toml::boolean>(std::move(v), "key");
BOOST_TEST(false == moved);
} }
{ {
value_type v{{"key", 42}}; value_type v{{"key", 42}};
@@ -289,9 +92,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::integer>(v, "key") = 54; toml::find<toml::integer>(v, "key") = 54;
BOOST_TEST(toml::integer(54) == toml::find<toml::integer>(v, "key")); BOOST_TEST(toml::integer(54) == toml::find<toml::integer>(v, "key"));
const auto moved = toml::find<toml::integer>(std::move(v), "key");
BOOST_TEST(toml::integer(54) == moved);
} }
{ {
value_type v{{"key", 3.14}}; value_type v{{"key", 3.14}};
@@ -299,9 +99,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::floating>(v, "key") = 2.71; toml::find<toml::floating>(v, "key") = 2.71;
BOOST_TEST(toml::floating(2.71) == toml::find<toml::floating>(v, "key")); BOOST_TEST(toml::floating(2.71) == toml::find<toml::floating>(v, "key"));
const auto moved = toml::find<toml::floating>(std::move(v), "key");
BOOST_TEST(toml::floating(2.71) == moved);
} }
{ {
value_type v{{"key", "foo"}}; value_type v{{"key", "foo"}};
@@ -311,9 +108,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::string>(v, "key").str += "bar"; toml::find<toml::string>(v, "key").str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::basic) == BOOST_TEST(toml::string("foobar", toml::string_t::basic) ==
toml::find<toml::string>(v, "key")); toml::find<toml::string>(v, "key"));
const auto moved = toml::find<toml::string>(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)}}; value_type v{{"key", value_type("foo", toml::string_t::literal)}};
@@ -323,9 +117,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::string>(v, "key").str += "bar"; toml::find<toml::string>(v, "key").str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == BOOST_TEST(toml::string("foobar", toml::string_t::literal) ==
toml::find<toml::string>(v, "key")); toml::find<toml::string>(v, "key"));
const auto moved = toml::find<toml::string>(std::move(v), "key");
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == moved);
} }
{ {
toml::local_date d(2018, toml::month_t::Apr, 22); toml::local_date d(2018, toml::month_t::Apr, 22);
@@ -335,9 +126,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_date>(v, "key").year = 2017; toml::find<toml::local_date>(v, "key").year = 2017;
d.year = 2017; d.year = 2017;
BOOST_CHECK(d == toml::find<toml::local_date>(v, "key")); BOOST_CHECK(d == toml::find<toml::local_date>(v, "key"));
const auto moved = toml::find<toml::local_date>(std::move(v), "key");
BOOST_CHECK(d == moved);
} }
{ {
toml::local_time t(12, 30, 45); toml::local_time t(12, 30, 45);
@@ -347,9 +135,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_time>(v, "key").hour = 9; toml::find<toml::local_time>(v, "key").hour = 9;
t.hour = 9; t.hour = 9;
BOOST_CHECK(t == toml::find<toml::local_time>(v, "key")); BOOST_CHECK(t == toml::find<toml::local_time>(v, "key"));
const auto moved = toml::find<toml::local_time>(std::move(v), "key");
BOOST_CHECK(t == moved);
} }
{ {
toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22),
@@ -360,9 +145,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::local_datetime>(v, "key").date.year = 2017; toml::find<toml::local_datetime>(v, "key").date.year = 2017;
dt.date.year = 2017; dt.date.year = 2017;
BOOST_CHECK(dt == toml::find<toml::local_datetime>(v, "key")); BOOST_CHECK(dt == toml::find<toml::local_datetime>(v, "key"));
const auto moved = toml::find<toml::local_datetime>(std::move(v), "key");
BOOST_CHECK(dt == moved);
} }
{ {
toml::offset_datetime dt(toml::local_datetime( toml::offset_datetime dt(toml::local_datetime(
@@ -374,9 +156,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
toml::find<toml::offset_datetime>(v, "key").date.year = 2017; toml::find<toml::offset_datetime>(v, "key").date.year = 2017;
dt.date.year = 2017; dt.date.year = 2017;
BOOST_CHECK(dt == toml::find<toml::offset_datetime>(v, "key")); BOOST_CHECK(dt == toml::find<toml::offset_datetime>(v, "key"));
const auto moved = toml::find<toml::offset_datetime>(std::move(v), "key");
BOOST_CHECK(dt == moved);
} }
{ {
typename value_type::array_type vec; typename value_type::array_type vec;
@@ -392,10 +171,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
const bool result2 = (vec == toml::find<typename value_type::array_type>(v, "key")); const bool result2 = (vec == toml::find<typename value_type::array_type>(v, "key"));
BOOST_CHECK(result2); BOOST_CHECK(result2);
const auto moved = toml::find<typename value_type::array_type>(std::move(v), "key");
const bool result3 = (vec == moved);
BOOST_CHECK(result3);
} }
{ {
typename value_type::table_type tab; typename value_type::table_type tab;
@@ -409,10 +184,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
tab["key3"] = value_type(123); tab["key3"] = value_type(123);
const bool result2 = (tab == toml::find<typename value_type::table_type>(v, "key")); const bool result2 = (tab == toml::find<typename value_type::table_type>(v, "key"));
BOOST_CHECK(result2); BOOST_CHECK(result2);
const auto moved = toml::find<typename value_type::table_type>(std::move(v), "key");
const bool result3 = (tab == moved);
BOOST_CHECK(result3);
} }
{ {
value_type v1(42); value_type v1(42);
@@ -422,9 +193,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_exact, value_type, test_value_types)
value_type v2(54); value_type v2(54);
toml::find(v, "key") = v2; toml::find(v, "key") = v2;
BOOST_CHECK(v2 == toml::find(v, "key")); BOOST_CHECK(v2 == toml::find(v, "key"));
const auto moved = toml::find(std::move(v), "key");
BOOST_CHECK(v2 == moved);
} }
} }
@@ -432,16 +200,15 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_integer_type, value_type, test_value_typ
{ {
{ {
value_type v{{"key", 42}}; value_type v{{"key", 42}};
BOOST_TEST(int(42) == toml::find<int >(v, "key")); BOOST_TEST(int(42) == toml::find<int >(v, "key"));
BOOST_TEST(short(42) == toml::find<short >(v, "key")); BOOST_TEST(short(42) == toml::find<short >(v, "key"));
BOOST_TEST(char(42) == toml::find<char >(v, "key")); BOOST_TEST(char(42) == toml::find<char >(v, "key"));
BOOST_TEST(unsigned(42) == toml::find<unsigned >(v, "key")); BOOST_TEST(unsigned(42) == toml::find<unsigned >(v, "key"));
BOOST_TEST(long(42) == toml::find<long >(v, "key")); BOOST_TEST(long(42) == toml::find<long >(v, "key"));
BOOST_TEST(std::int64_t(42) == toml::find<std::int64_t >(v, "key")); BOOST_TEST(std::int64_t(42) == toml::find<std::int64_t >(v, "key"));
BOOST_TEST(std::uint64_t(42) == toml::find<std::uint64_t>(v, "key")); BOOST_TEST(std::uint64_t(42) == toml::find<std::uint64_t>(v, "key"));
BOOST_TEST(std::int16_t(42) == toml::find<std::int16_t >(v, "key")); BOOST_TEST(std::int16_t(42) == toml::find<std::int16_t >(v, "key"));
BOOST_TEST(std::uint16_t(42) == toml::find<std::uint16_t>(v, "key")); BOOST_TEST(std::uint16_t(42) == toml::find<std::uint16_t>(v, "key"));
BOOST_TEST(std::uint16_t(42) == toml::find<std::uint16_t>(std::move(v), "key"));
} }
} }
@@ -449,11 +216,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_floating_type, value_type, test_value_ty
{ {
{ {
value_type v{{"key", 3.14}}; value_type v{{"key", 3.14}};
const double ref(3.14); BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(v, "key"));
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(v, "key")); BOOST_TEST(static_cast<double >(3.14) == toml::find<double >(v, "key"));
BOOST_TEST( ref == toml::find<double >(v, "key")); BOOST_TEST(static_cast<long double>(3.14) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<long double>(ref) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(std::move(v), "key"));
} }
} }
@@ -471,11 +236,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_string_type, value_type, test_value_type
toml::find<std::string>(v, "key") += "bar"; toml::find<std::string>(v, "key") += "bar";
BOOST_TEST("foobar" == toml::find<std::string>(v, "key")); BOOST_TEST("foobar" == toml::find<std::string>(v, "key"));
} }
{
value_type v{{"key", toml::string("foo", toml::string_t::literal)}};
const auto moved = toml::find<std::string>(std::move(v), "key");
BOOST_TEST("foo" == moved);
}
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
{ {
@@ -514,14 +274,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3)); BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key"); std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key");
BOOST_TEST(42 == ary.at(0)); BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(54 == ary.at(1)); BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(69 == ary.at(2)); BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(72 == ary.at(3)); BOOST_TEST(static_cast<int>(72) == ary.at(3));
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(v, "key"); toml::find<std::tuple<int, short, unsigned, long>>(v, "key");
BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -532,53 +292,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(2.71 == pr.second); 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<int> vec = toml::find<std::vector<int>>(std::move(v1), "key");
const std::list<short> lst = toml::find<std::list<short>>(std::move(v2), "key");
const std::deque<std::int64_t> deq = toml::find<std::deque<std::int64_t>>(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<short>::const_iterator iter = lst.begin();
BOOST_TEST(static_cast<short>(42) == *(iter++));
BOOST_TEST(static_cast<short>(54) == *(iter++));
BOOST_TEST(static_cast<short>(69) == *(iter++));
BOOST_TEST(static_cast<short>(72) == *(iter++));
BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0));
BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1));
BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2));
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(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<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key");
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
value_type p{{"key", {3.14, 2.71}}};
std::pair<double, double> pr = toml::find<std::pair<double, double> >(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) 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 v1{42, 54, 69, 72};
@@ -610,153 +323,62 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array_of_array, value_type, test_va
BOOST_TEST(std::get<1>(t).at(2) == "baz"); 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<int>, std::vector<std::string>> p =
toml::find<std::pair<std::vector<int>, std::vector<std::string>>>(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<int>, std::vector<std::string>> t =
toml::find<std::tuple<std::vector<int>, std::vector<std::string>>>(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) BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_table, value_type, test_value_types)
{ {
{ value_type v1{{"key", {
value_type v1{{"key", { {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }}};
}}}; const auto v = toml::find<std::map<std::string, int>>(v1, "key");
const auto v = toml::find<std::map<std::string, int>>(v1, "key"); BOOST_TEST(v.at("key1") == 1);
BOOST_TEST(v.at("key1") == 1); BOOST_TEST(v.at("key2") == 2);
BOOST_TEST(v.at("key2") == 2); BOOST_TEST(v.at("key3") == 3);
BOOST_TEST(v.at("key3") == 3); BOOST_TEST(v.at("key4") == 4);
BOOST_TEST(v.at("key4") == 4);
}
{
value_type v1{{"key", {
{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}
}}};
const auto v = toml::find<std::map<std::string, int>>(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) 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}}};
value_type v1{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}}; const auto date = std::chrono::system_clock::to_time_t(
const auto date = std::chrono::system_clock::to_time_t( toml::find<std::chrono::system_clock::time_point>(v1, "key"));
toml::find<std::chrono::system_clock::time_point>(v1, "key"));
std::tm t; std::tm t;
t.tm_year = 2018 - 1900; t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1; t.tm_mon = 4 - 1;
t.tm_mday = 1; t.tm_mday = 1;
t.tm_hour = 0; t.tm_hour = 0;
t.tm_min = 0; t.tm_min = 0;
t.tm_sec = 0; t.tm_sec = 0;
t.tm_isdst = -1; t.tm_isdst = -1;
const auto c = std::mktime(&t); const auto c = std::mktime(&t);
BOOST_TEST(c == date); 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::chrono::system_clock::time_point>(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) 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}}};
value_type v1{{"key", toml::local_time{12, 30, 45}}}; const auto time = toml::find<std::chrono::seconds>(v1, "key");
const auto time = toml::find<std::chrono::seconds>(v1, "key"); BOOST_CHECK(time == std::chrono::hours(12) +
BOOST_CHECK(time == std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45));
std::chrono::minutes(30) + std::chrono::seconds(45));
}
{
value_type v1{{"key", toml::local_time{12, 30, 45}}};
const auto time = toml::find<std::chrono::seconds>(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) BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_local_datetime, value_type, test_value_types)
{ {
{ value_type v1{{"key", toml::local_datetime(
value_type v1{{"key", toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1},
toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})}};
toml::local_time{12, 30, 45})}};
const auto date = std::chrono::system_clock::to_time_t( const auto date = std::chrono::system_clock::to_time_t(
toml::find<std::chrono::system_clock::time_point>(v1, "key")); toml::find<std::chrono::system_clock::time_point>(v1, "key"));
std::tm t; std::tm t;
t.tm_year = 2018 - 1900; t.tm_year = 2018 - 1900;
t.tm_mon = 4 - 1; t.tm_mon = 4 - 1;
t.tm_mday = 1; t.tm_mday = 1;
t.tm_hour = 12; t.tm_hour = 12;
t.tm_min = 30; t.tm_min = 30;
t.tm_sec = 45; t.tm_sec = 45;
t.tm_isdst = -1; t.tm_isdst = -1;
const auto c = std::mktime(&t); const auto c = std::mktime(&t);
BOOST_TEST(c == date); 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::chrono::system_clock::time_point>(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) BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_value_types)
@@ -806,28 +428,5 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_va
BOOST_TEST(tm.tm_min == 30); BOOST_TEST(tm.tm_min == 30);
BOOST_TEST(tm.tm_sec == 0); 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::chrono::system_clock::time_point>(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);
}
} }

350
tests/test_find_fuzzy.cpp Normal file
View File

@@ -0,0 +1,350 @@
#define BOOST_TEST_MODULE "test_find_fuzzy"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
BOOST_AUTO_TEST_CASE(test_levenstein_distance)
{
const toml::levenstein_matcher lev(1);
// distance == 0
{
const std::string s1("foobar");
const std::string s2 = s1;
BOOST_TEST(lev.distance(s1, s2) == 0);
}
{
const std::string s1("foobar");
const std::string s2("foobaz");
BOOST_TEST(lev.distance(s1, s2) == 1);
}
{
const std::string s1("foobar"); // insertion (+x)
const std::string s2("fooxbar");
BOOST_TEST(lev.distance(s1, s2) == 1);
}
{
const std::string s1("foobar");
const std::string s2("fooar"); // insertion(+b)
BOOST_TEST(lev.distance(s1, s2) == 1);
}
// distance > 1
{
const std::string s1("foobar");
const std::string s2("fooquux");
BOOST_TEST(lev.distance(s1, s2) == 4);
}
{
const std::string s1("foobar");
const std::string s2("fooqu");
BOOST_TEST(s1 != s2);
BOOST_TEST(lev.distance(s1, s2) == 3);
}
}
BOOST_AUTO_TEST_CASE(test_find_fuzzy)
{
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
toml::find_fuzzy(v, "key") = "foobar";
BOOST_TEST(toml::find(v, "keu") == toml::value("foobar"));
}
{
const toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(v, "key") == toml::value("value"));
BOOST_CHECK_THROW(toml::find_fuzzy(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value const&, decltype(toml::find_fuzzy(v, "key"))>::value, "");
}
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy(std::move(v), "key") == toml::value("value"));
static_assert(std::is_same<
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
}
{
toml::value v{
{"keu", "value"} // typo! key -> keu
};
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "kiwi"), std::out_of_range);
static_assert(std::is_same<
toml::value&&, decltype(toml::find_fuzzy(std::move(v), "key"))>::value, "");
}
// find with conversion
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
}
{
const toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(v, "key") == 42);
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "kiwi"), std::out_of_range);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(v, "key"))>::value, "");
}
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_TEST(toml::find_fuzzy<int>(std::move(v), "key") == 42);
static_assert(std::is_same<int,
decltype(toml::find_fuzzy<int>(std::move(v), "key"))>::value, "");
}
{
toml::value v{
{"keu", 42} // typo! key -> keu
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "kiwi"), std::out_of_range);
}
}
BOOST_AUTO_TEST_CASE(test_find_fuzzy_throw)
{
{
toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
}
{
const toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(v, "key"), std::out_of_range);
}
{
toml::value v{
{"keu", "value"}, // typo! key -> keu
{"ky", "value"} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy(std::move(v), "key"), std::out_of_range);
}
{
toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
}
{
const toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(v, "key"), std::out_of_range);
}
{
toml::value v{
{"keu", 42}, // typo! key -> keu
{"ky", 42} // typo! key -> ky
};
BOOST_CHECK_THROW(toml::find_fuzzy<int>(std::move(v), "key"), std::out_of_range);
}
}
BOOST_AUTO_TEST_CASE(test_find_throw_typo_aware_exception)
{
using namespace toml::literals::toml_literals;
const toml::levenstein_matcher lev(1);
{
toml::value v = u8R"(
keu = "value"
)"_toml;
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find(v, "key", lev);
(void)ret; // suppress unused variable
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<
toml::value&, decltype(toml::find(v, "key"))>::value, "");
}
{
const toml::value v = u8R"(
keu = "value"
)"_toml;
BOOST_CHECK_THROW(toml::find(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find(v, "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<
toml::value const&, decltype(toml::find(v, "key"))>::value, "");
}
{
toml::value v = u8R"(
keu = "value"
)"_toml;
bool thrown = false; // since it moves, we need to check both once
try
{
const auto& ret = toml::find(std::move(v), "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
thrown = true;
// std::cout << what << std::endl;
}
BOOST_TEST(thrown);
static_assert(std::is_same<
toml::value&, decltype(toml::find(v, "key"))>::value, "");
}
}
BOOST_AUTO_TEST_CASE(test_find_throw_conversion_typo_aware_exception)
{
using namespace toml::literals::toml_literals;
const toml::levenstein_matcher lev(1);
{
toml::value v = u8R"(
keu = 42
)"_toml;
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find<int>(v, "key", lev);
(void)ret; // suppress unused variable
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
{
const toml::value v = u8R"(
keu = 42
)"_toml;
BOOST_CHECK_THROW(toml::find<int>(v, "key", lev), std::out_of_range);
try
{
const auto& ret = toml::find<int>(v, "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
// std::cout << what << std::endl;
}
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
{
toml::value v = u8R"(
keu = 42
)"_toml;
bool thrown = false; // since it moves, we need to check both once
try
{
const auto& ret = toml::find<int>(std::move(v), "key", lev);
(void)ret;
}
catch(const std::out_of_range& oor)
{
// exception.what() should include the typo-ed key name
const std::string what(oor.what());
BOOST_TEST(what.find("keu") != std::string::npos);
thrown = true;
// std::cout << what << std::endl;
}
BOOST_TEST(thrown);
static_assert(std::is_same<int,
decltype(toml::find<int>(v, "key"))>::value, "");
}
}

View File

@@ -121,55 +121,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_exact, value_type, test_value_types)
} }
#undef TOML11_TEST_FIND_OR_EXACT #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)\ #define TOML11_TEST_FIND_OR_MODIFY(toml_type, init_expr, opt_expr)\
{ \ { \
using namespace test; \ using namespace test; \
@@ -353,20 +304,10 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_fallback, value_type, test_value_type
BOOST_AUTO_TEST_CASE(test_find_or_integer) BOOST_AUTO_TEST_CASE(test_find_or_integer)
{ {
{ {
toml::value v{{"num", 42}}; toml::value v = toml::table{{"num", 42}};
BOOST_TEST(42u == toml::find_or(v, "num", 0u)); BOOST_TEST(42u == toml::find_or(v, "num", 0u));
BOOST_TEST(0u == toml::find_or(v, "foo", 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) BOOST_AUTO_TEST_CASE(test_find_or_floating)
@@ -375,17 +316,7 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
toml::value v1{{"key", 42}}; toml::value v1{{"key", 42}};
toml::value v2{{"key", 3.14}}; toml::value v2{{"key", 3.14}};
BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f)); BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f));
const double ref(3.14); BOOST_TEST(static_cast<float>(double(3.14)) == toml::find_or(v2, "key", 2.71f));
BOOST_TEST(static_cast<float>(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<float>(ref) == moved2);
} }
} }
@@ -414,32 +345,6 @@ BOOST_AUTO_TEST_CASE(test_find_or_string)
s1 = "bazqux"; // restoring moved value s1 = "bazqux"; // restoring moved value
BOOST_TEST("bazqux" == toml::find_or(std::move(v2), "key", std::move(s1))); 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 // string literal
{ {
toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v1 = toml::table{{"key", "foobar"}};
@@ -452,28 +357,6 @@ BOOST_AUTO_TEST_CASE(test_find_or_string)
BOOST_TEST("foobar" == toml::find_or(v1, "key", lit)); BOOST_TEST("foobar" == toml::find_or(v1, "key", lit));
BOOST_TEST("bazqux" == toml::find_or(v2, "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) BOOST_AUTO_TEST_CASE(test_find_or_map)
@@ -494,7 +377,7 @@ BOOST_AUTO_TEST_CASE(test_find_or_map)
BOOST_TEST(key.at("key") == "value"); BOOST_TEST(key.at("key") == "value");
} }
{ {
toml::value v1{ const toml::value v1{
{"key", {{"key", "value"}}} {"key", {{"key", "value"}}}
}; };
@@ -507,35 +390,4 @@ BOOST_AUTO_TEST_CASE(test_find_or_map)
BOOST_TEST(key.size() == 1u); BOOST_TEST(key.size() == 1u);
BOOST_TEST(key.at("key") == "value"); 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<map_type>(std::move(v1), "key", map_type{});
const auto key2 = toml::find_or<map_type>(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");
}
} }

View File

@@ -31,9 +31,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::boolean>(v) = false; toml::get<toml::boolean>(v) = false;
BOOST_TEST(false == toml::get<toml::boolean>(v)); BOOST_TEST(false == toml::get<toml::boolean>(v));
toml::boolean x = toml::get<toml::boolean>(std::move(v));
BOOST_TEST(false == x);
} }
{ {
value_type v(42); value_type v(42);
@@ -41,9 +38,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::integer>(v) = 54; toml::get<toml::integer>(v) = 54;
BOOST_TEST(toml::integer(54) == toml::get<toml::integer>(v)); BOOST_TEST(toml::integer(54) == toml::get<toml::integer>(v));
toml::integer x = toml::get<toml::integer>(std::move(v));
BOOST_TEST(toml::integer(54) == x);
} }
{ {
value_type v(3.14); value_type v(3.14);
@@ -51,9 +45,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::floating>(v) = 2.71; toml::get<toml::floating>(v) = 2.71;
BOOST_TEST(toml::floating(2.71) == toml::get<toml::floating>(v)); BOOST_TEST(toml::floating(2.71) == toml::get<toml::floating>(v));
toml::floating x = toml::get<toml::floating>(std::move(v));
BOOST_TEST(toml::floating(2.71) == x);
} }
{ {
value_type v("foo"); value_type v("foo");
@@ -63,9 +54,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::string>(v).str += "bar"; toml::get<toml::string>(v).str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::basic) == BOOST_TEST(toml::string("foobar", toml::string_t::basic) ==
toml::get<toml::string>(v)); toml::get<toml::string>(v));
toml::string x = toml::get<toml::string>(std::move(v));
BOOST_TEST(toml::string("foobar") == x);
} }
{ {
value_type v("foo", toml::string_t::literal); value_type v("foo", toml::string_t::literal);
@@ -75,9 +63,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::string>(v).str += "bar"; toml::get<toml::string>(v).str += "bar";
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == BOOST_TEST(toml::string("foobar", toml::string_t::literal) ==
toml::get<toml::string>(v)); toml::get<toml::string>(v));
toml::string x = toml::get<toml::string>(std::move(v));
BOOST_TEST(toml::string("foobar", toml::string_t::literal) == x);
} }
{ {
toml::local_date d(2018, toml::month_t::Apr, 22); toml::local_date d(2018, toml::month_t::Apr, 22);
@@ -87,9 +72,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_date>(v).year = 2017; toml::get<toml::local_date>(v).year = 2017;
d.year = 2017; d.year = 2017;
BOOST_TEST(d == toml::get<toml::local_date>(v)); BOOST_TEST(d == toml::get<toml::local_date>(v));
toml::local_date x = toml::get<toml::local_date>(std::move(v));
BOOST_TEST(d == x);
} }
{ {
toml::local_time t(12, 30, 45); toml::local_time t(12, 30, 45);
@@ -99,9 +81,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_time>(v).hour = 9; toml::get<toml::local_time>(v).hour = 9;
t.hour = 9; t.hour = 9;
BOOST_TEST(t == toml::get<toml::local_time>(v)); BOOST_TEST(t == toml::get<toml::local_time>(v));
toml::local_time x = toml::get<toml::local_time>(std::move(v));
BOOST_TEST(t == x);
} }
{ {
toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22),
@@ -112,9 +91,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::local_datetime>(v).date.year = 2017; toml::get<toml::local_datetime>(v).date.year = 2017;
dt.date.year = 2017; dt.date.year = 2017;
BOOST_TEST(dt == toml::get<toml::local_datetime>(v)); BOOST_TEST(dt == toml::get<toml::local_datetime>(v));
toml::local_datetime x = toml::get<toml::local_datetime>(std::move(v));
BOOST_TEST(dt == x);
} }
{ {
toml::offset_datetime dt(toml::local_datetime( toml::offset_datetime dt(toml::local_datetime(
@@ -126,9 +102,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<toml::offset_datetime>(v).date.year = 2017; toml::get<toml::offset_datetime>(v).date.year = 2017;
dt.date.year = 2017; dt.date.year = 2017;
BOOST_TEST(dt == toml::get<toml::offset_datetime>(v)); BOOST_TEST(dt == toml::get<toml::offset_datetime>(v));
toml::offset_datetime x = toml::get<toml::offset_datetime>(std::move(v));
BOOST_TEST(dt == x);
} }
{ {
using array_type = typename value_type::array_type; using array_type = typename value_type::array_type;
@@ -141,9 +114,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<array_type>(v).push_back(value_type(123)); toml::get<array_type>(v).push_back(value_type(123));
vec.push_back(value_type(123)); vec.push_back(value_type(123));
BOOST_TEST(vec == toml::get<array_type>(v)); BOOST_TEST(vec == toml::get<array_type>(v));
array_type x = toml::get<array_type>(std::move(v));
BOOST_TEST(vec == x);
} }
{ {
using table_type = typename value_type::table_type; using table_type = typename value_type::table_type;
@@ -156,9 +126,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
toml::get<table_type>(v)["key3"] = value_type(123); toml::get<table_type>(v)["key3"] = value_type(123);
tab["key3"] = value_type(123); tab["key3"] = value_type(123);
BOOST_TEST(tab == toml::get<table_type>(v)); BOOST_TEST(tab == toml::get<table_type>(v));
table_type x = toml::get<table_type>(std::move(v));
BOOST_TEST(tab == x);
} }
{ {
value_type v1(42); value_type v1(42);
@@ -167,9 +134,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_exact, value_type, test_value_types)
value_type v2(54); value_type v2(54);
toml::get<value_type>(v1) = v2; toml::get<value_type>(v1) = v2;
BOOST_TEST(v2 == toml::get<value_type>(v1)); BOOST_TEST(v2 == toml::get<value_type>(v1));
value_type x = toml::get<value_type>(std::move(v1));
BOOST_TEST(v2 == x);
} }
} }
@@ -186,8 +150,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_integer_type, value_type, test_value_type
BOOST_TEST(std::uint64_t(42) == toml::get<std::uint64_t>(v)); BOOST_TEST(std::uint64_t(42) == toml::get<std::uint64_t>(v));
BOOST_TEST(std::int16_t(42) == toml::get<std::int16_t >(v)); BOOST_TEST(std::int16_t(42) == toml::get<std::int16_t >(v));
BOOST_TEST(std::uint16_t(42) == toml::get<std::uint16_t>(v)); BOOST_TEST(std::uint16_t(42) == toml::get<std::uint16_t>(v));
BOOST_TEST(std::uint16_t(42) == toml::get<std::uint16_t>(std::move(v)));
} }
} }
@@ -195,11 +157,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_floating_type, value_type, test_value_typ
{ {
{ {
value_type v(3.14); value_type v(3.14);
const double ref(3.14); BOOST_TEST(static_cast<float >(3.14) == toml::get<float >(v));
BOOST_TEST(static_cast<float >(ref) == toml::get<float >(v)); BOOST_TEST(static_cast<double >(3.14) == toml::get<double >(v));
BOOST_TEST( ref == toml::get<double >(v)); BOOST_TEST(static_cast<long double>(3.14) == toml::get<long double>(v));
BOOST_TEST(static_cast<long double>(ref) == toml::get<long double>(v));
BOOST_TEST(static_cast<float >(ref) == toml::get<float>(std::move(v)));
} }
} }
@@ -210,18 +170,12 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types
BOOST_TEST("foo" == toml::get<std::string>(v)); BOOST_TEST("foo" == toml::get<std::string>(v));
toml::get<std::string>(v) += "bar"; toml::get<std::string>(v) += "bar";
BOOST_TEST("foobar" == toml::get<std::string>(v)); BOOST_TEST("foobar" == toml::get<std::string>(v));
const auto x = toml::get<std::string>(std::move(v));
BOOST_TEST("foobar" == x);
} }
{ {
value_type v("foo", toml::string_t::literal); value_type v("foo", toml::string_t::literal);
BOOST_TEST("foo" == toml::get<std::string>(v)); BOOST_TEST("foo" == toml::get<std::string>(v));
toml::get<std::string>(v) += "bar"; toml::get<std::string>(v) += "bar";
BOOST_TEST("foobar" == toml::get<std::string>(v)); BOOST_TEST("foobar" == toml::get<std::string>(v));
const auto x = toml::get<std::string>(std::move(v));
BOOST_TEST("foobar" == x);
} }
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
@@ -238,176 +192,92 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types) BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
{ {
{ const value_type v{42, 54, 69, 72};
const value_type v{42, 54, 69, 72};
const std::vector<int> vec = toml::get<std::vector<int>>(v); const std::vector<int> vec = toml::get<std::vector<int>>(v);
const std::list<short> lst = toml::get<std::list<short>>(v); const std::list<short> lst = toml::get<std::list<short>>(v);
const std::deque<std::int64_t> deq = toml::get<std::deque<std::int64_t>>(v); const std::deque<std::int64_t> deq = toml::get<std::deque<std::int64_t>>(v);
BOOST_TEST(42 == vec.at(0)); BOOST_TEST(42 == vec.at(0));
BOOST_TEST(54 == vec.at(1)); BOOST_TEST(54 == vec.at(1));
BOOST_TEST(69 == vec.at(2)); BOOST_TEST(69 == vec.at(2));
BOOST_TEST(72 == vec.at(3)); BOOST_TEST(72 == vec.at(3));
std::list<short>::const_iterator iter = lst.begin(); std::list<short>::const_iterator iter = lst.begin();
BOOST_TEST(static_cast<short>(42) == *(iter++)); BOOST_TEST(static_cast<short>(42) == *(iter++));
BOOST_TEST(static_cast<short>(54) == *(iter++)); BOOST_TEST(static_cast<short>(54) == *(iter++));
BOOST_TEST(static_cast<short>(69) == *(iter++)); BOOST_TEST(static_cast<short>(69) == *(iter++));
BOOST_TEST(static_cast<short>(72) == *(iter++)); BOOST_TEST(static_cast<short>(72) == *(iter++));
BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0)); BOOST_TEST(static_cast<std::int64_t>(42) == deq.at(0));
BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1)); BOOST_TEST(static_cast<std::int64_t>(54) == deq.at(1));
BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2)); BOOST_TEST(static_cast<std::int64_t>(69) == deq.at(2));
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3)); BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::get<std::array<int, 4>>(v); std::array<int, 4> ary = toml::get<std::array<int, 4>>(v);
BOOST_TEST(42 == ary.at(0)); BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(54 == ary.at(1)); BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(69 == ary.at(2)); BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(72 == ary.at(3)); BOOST_TEST(static_cast<int>(72) == ary.at(3));
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(v); toml::get<std::tuple<int, short, unsigned, long>>(v);
BOOST_TEST( 42 == std::get<0>(tpl)); BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
const value_type p{3.14, 2.71}; const value_type p{3.14, 2.71};
std::pair<double, double> pr = toml::get<std::pair<double, double> >(p); std::pair<double, double> pr = toml::get<std::pair<double, double> >(p);
BOOST_TEST(3.14 == pr.first); BOOST_TEST(3.14 == pr.first);
BOOST_TEST(2.71 == pr.second); BOOST_TEST(2.71 == pr.second);
}
{
value_type v{42, 54, 69, 72};
const std::vector<int> vec = toml::get<std::vector<int>>(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<int> deq = toml::get<std::deque<int>>(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<int> lst = toml::get<std::list<int>>(std::move(v));
std::list<int>::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<int, 4> ary = toml::get<std::array<int, 4>>(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<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(std::move(v));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
}
} }
BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array_of_array, value_type, test_value_types) 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 v1{42, 54, 69, 72}; const value_type v2{"foo", "bar", "baz"};
const value_type v2{"foo", "bar", "baz"}; const value_type v{v1, v2};
const value_type v{v1, v2};
std::pair<std::vector<int>, std::vector<std::string>> p = std::pair<std::vector<int>, std::vector<std::string>> p =
toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(v); toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(v);
BOOST_TEST(p.first.size() == 4u); BOOST_TEST(p.first.at(0) == 42);
BOOST_TEST(p.first.at(0) == 42); BOOST_TEST(p.first.at(1) == 54);
BOOST_TEST(p.first.at(1) == 54); BOOST_TEST(p.first.at(2) == 69);
BOOST_TEST(p.first.at(2) == 69); BOOST_TEST(p.first.at(3) == 72);
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(0) == "foo"); BOOST_TEST(p.second.at(1) == "bar");
BOOST_TEST(p.second.at(1) == "bar"); BOOST_TEST(p.second.at(2) == "baz");
BOOST_TEST(p.second.at(2) == "baz");
std::tuple<std::vector<int>, std::vector<std::string>> t = std::tuple<std::vector<int>, std::vector<std::string>> t =
toml::get<std::tuple<std::vector<int>, std::vector<std::string>>>(v); toml::get<std::tuple<std::vector<int>, std::vector<std::string>>>(v);
BOOST_TEST(std::get<0>(t).at(0) == 42); 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(1) == 54);
BOOST_TEST(std::get<0>(t).at(2) == 69); BOOST_TEST(std::get<0>(t).at(2) == 69);
BOOST_TEST(std::get<0>(t).at(3) == 72); 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(0) == "foo");
BOOST_TEST(std::get<1>(t).at(1) == "bar"); BOOST_TEST(std::get<1>(t).at(1) == "bar");
BOOST_TEST(std::get<1>(t).at(2) == "baz"); 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<int>, std::vector<std::string>> p =
toml::get<std::pair<std::vector<int>, std::vector<std::string>>>(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) BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_table, value_type, test_value_types)
{ {
{ const value_type v1{
const value_type v1{ {"key1", 1},
{"key1", 1}, {"key2", 2},
{"key2", 2}, {"key3", 3},
{"key3", 3}, {"key4", 4}
{"key4", 4} };
};
const auto v = toml::get<std::map<std::string, int>>(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::map<std::string, int>>(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);
}
const auto v = toml::get<std::map<std::string, int>>(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) BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_local_date, value_type, test_value_types)

View File

@@ -25,11 +25,6 @@ using test_value_types = std::tuple<
namespace test 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<<s for
// containers to compare.
template<typename charT, typename traits, typename T, typename Alloc> template<typename charT, typename traits, typename T, typename Alloc>
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const std::vector<T, Alloc>& v) operator<<(std::basic_ostream<charT, traits>& os, const std::vector<T, Alloc>& v)
@@ -126,55 +121,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_or_exact, value_type, test_value_types)
} }
#undef TOML11_TEST_GET_OR_EXACT #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)\ #define TOML11_TEST_GET_OR_MODIFY(toml_type, init_expr, opt_expr)\
{ \ { \
using namespace test; \ using namespace test; \
@@ -363,13 +309,6 @@ BOOST_AUTO_TEST_CASE(test_get_or_integer)
BOOST_TEST(42u == toml::get_or(v1, 0u)); BOOST_TEST(42u == toml::get_or(v1, 0u));
BOOST_TEST(0u == toml::get_or(v2, 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) BOOST_AUTO_TEST_CASE(test_get_or_floating)
@@ -380,12 +319,6 @@ BOOST_AUTO_TEST_CASE(test_get_or_floating)
BOOST_TEST(2.71f == toml::get_or(v1, 2.71f)); BOOST_TEST(2.71f == toml::get_or(v1, 2.71f));
BOOST_TEST(static_cast<float>(v2.as_floating()) == toml::get_or(v2, 2.71f)); BOOST_TEST(static_cast<float>(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<float>(3.14) == toml::get_or(std::move(v2), 2.71f));
}
} }
BOOST_AUTO_TEST_CASE(test_get_or_string) BOOST_AUTO_TEST_CASE(test_get_or_string)
@@ -412,16 +345,7 @@ BOOST_AUTO_TEST_CASE(test_get_or_string)
BOOST_TEST("foobar" == toml::get_or(v1, std::move(s1))); BOOST_TEST("foobar" == toml::get_or(v1, std::move(s1)));
BOOST_TEST("bazqux" == toml::get_or(v2, 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 v1("foobar");
toml::value v2(42); toml::value v2(42);
@@ -433,20 +357,4 @@ BOOST_AUTO_TEST_CASE(test_get_or_string)
BOOST_TEST("foobar" == toml::get_or(v1, lit)); BOOST_TEST("foobar" == toml::get_or(v1, lit));
BOOST_TEST("bazqux" == toml::get_or(v2, 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));
}
} }

View File

@@ -8,7 +8,7 @@
do { \ do { \
const std::string token (tkn); \ const std::string token (tkn); \
const std::string expected(expct); \ const std::string expected(expct); \
toml::detail::location loc("test", token); \ toml::detail::location<std::string> loc("test", token); \
const auto result = lxr::invoke(loc); \ const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \
@@ -28,7 +28,7 @@ do { \
#define TOML11_TEST_LEX_REJECT(lxr, tkn) \ #define TOML11_TEST_LEX_REJECT(lxr, tkn) \
do { \ do { \
const std::string token (tkn); \ const std::string token (tkn); \
toml::detail::location loc("test", token); \ toml::detail::location<std::string> loc("test", token); \
const auto result = lxr::invoke(loc); \ const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_err()); \ BOOST_TEST(result.is_err()); \
const bool loc_same = (loc.begin() == loc.iter()); \ const bool loc_same = (loc.begin() == loc.iter()); \

View File

@@ -35,35 +35,27 @@ BOOST_AUTO_TEST_CASE(test_fractional_invalid)
BOOST_AUTO_TEST_CASE(test_exponential_valid) BOOST_AUTO_TEST_CASE(test_exponential_valid)
{ {
TOML11_TEST_LEX_ACCEPT(lex_float, "1e10", "1e10"); 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, "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, "+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, "+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, "-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, "-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, "123e-10", "123e-10");
TOML11_TEST_LEX_ACCEPT(lex_float, "1E10", "1E10"); 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, "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, "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-10", "1_2_3E-10");
TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-1_0", "1_2_3E-1_0"); 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) 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");
TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1"); TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1");
} }
@@ -72,26 +64,12 @@ BOOST_AUTO_TEST_CASE(test_both_valid)
TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e23", "6.02e23"); 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, "6.02e+23", "6.02e+23");
TOML11_TEST_LEX_ACCEPT(lex_float, "1.112_650_06e-17", "1.112_650_06e-17"); 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) 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"); TOML11_TEST_LEX_ACCEPT(lex_float, "1e1.0", "1e1");
TOML11_TEST_LEX_REJECT(lex_float, "01e1.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.0e_01", "1.0");
TOML11_TEST_LEX_ACCEPT(lex_float, "1.0e0__1", "1.0e0");
#endif
} }
BOOST_AUTO_TEST_CASE(test_special_floating_point) BOOST_AUTO_TEST_CASE(test_special_floating_point)

View File

@@ -18,11 +18,12 @@ 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, "\"127.0.0.1\"", "\"127.0.0.1\"");
TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
// UTF-8 codepoint of characters that looks like "key" written upside down
TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"",
"\"\xCA\x8E\xC7\x9D\xCA\x9E\""); "\"\xCA\x8E\xC7\x9D\xCA\x9E\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'");
TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'");
} }

View File

@@ -31,9 +31,15 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
"\"192.168.1.1\"", "\"192.168.1.1\"",
"\"192.168.1.1\""); "\"192.168.1.1\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in "\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
"\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters) "\"\xE4\xB8\xAD\xE5\x9B\xBD\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_string,
u8"\"中国\"",
u8"\"中国\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"\"You'll hate me after this - #\"", "\"You'll hate me after this - #\"",
@@ -51,22 +57,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string, 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 \"\"\"",
"\"\"\"\\\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) BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -93,16 +83,4 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string, 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'''",
"'''\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.''''");
} }

View File

@@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
{ {
const toml::value r{{"a", 42}, {"b", "baz"}}; const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = R"( const toml::value v = u8R"(
a = 42 a = 42
b = "baz" b = "baz"
)"_toml; )"_toml;
@@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
{"c", 3.14}, {"c", 3.14},
{"table", toml::table{{"a", 42}, {"b", "baz"}}} {"table", toml::table{{"a", 42}, {"b", "baz"}}}
}; };
const toml::value v = R"( const toml::value v = u8R"(
c = 3.14 c = 3.14
[table] [table]
a = 42 a = 42
@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
const toml::value r{ const toml::value r{
{"table", toml::table{{"a", 42}, {"b", "baz"}}} {"table", toml::table{{"a", 42}, {"b", "baz"}}}
}; };
const toml::value v = R"( const toml::value v = u8R"(
[table] [table]
a = 42 a = 42
b = "baz" b = "baz"
@@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
const toml::value r{ const toml::value r{
{"array_of_tables", toml::array{toml::table{}}} {"array_of_tables", toml::array{toml::table{}}}
}; };
const toml::value v = R"( const toml::value v = u8R"(
[[array_of_tables]] [[array_of_tables]]
)"_toml; )"_toml;
@@ -63,170 +63,6 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
{ {
using namespace toml::literals::toml_literals; 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<bool>(v1));
BOOST_TEST(!toml::get<bool>(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<toml::integer>(v1) == 123456);
BOOST_TEST(toml::get<toml::integer>(v2) == 2);
BOOST_TEST(toml::get<toml::integer>(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<double>(v1) == 3.1415, boost::test_tools::tolerance(0.00001));
BOOST_TEST(toml::get<double>(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<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo");
}
{
{
const toml::value v1 = R"([1,2,3])"_toml;
BOOST_TEST(v1.is_array());
const bool result = (toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3});
BOOST_TEST(result);
}
{
const toml::value v2 = R"([1,])"_toml;
BOOST_TEST(v2.is_array());
const bool result = (toml::get<std::vector<int>>(v2) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v3 = R"([[1,]])"_toml;
BOOST_TEST(v3.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v3).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v4 = R"([[1],])"_toml;
BOOST_TEST(v4.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v4).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
}
{
const toml::value v1 = R"({a = 42})"_toml;
BOOST_TEST(v1.is_table());
const bool result = toml::get<std::map<std::string,int>>(v1) ==
std::map<std::string,int>{{"a", 42}};
BOOST_TEST(result);
}
{
const toml::value v1 = "1979-05-27"_toml;
BOOST_TEST(v1.is_local_date());
BOOST_TEST(toml::get<toml::local_date>(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<std::chrono::hours>(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<toml::local_datetime>(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<toml::offset_datetime>(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 v1 = u8"true"_toml;
const toml::value v2 = u8"false"_toml; const toml::value v2 = u8"false"_toml;
@@ -262,18 +98,15 @@ BOOST_AUTO_TEST_CASE(test_value_as_u8_literal)
const toml::value v2 = u8R"('foo')"_toml; const toml::value v2 = u8R"('foo')"_toml;
const toml::value v3 = u8R"("""foo""")"_toml; const toml::value v3 = u8R"("""foo""")"_toml;
const toml::value v4 = u8R"('''foo''')"_toml; const toml::value v4 = u8R"('''foo''')"_toml;
const toml::value v5 = u8R"("")"_toml;
BOOST_TEST(v1.is_string()); BOOST_TEST(v1.is_string());
BOOST_TEST(v2.is_string()); BOOST_TEST(v2.is_string());
BOOST_TEST(v3.is_string()); BOOST_TEST(v3.is_string());
BOOST_TEST(v4.is_string()); BOOST_TEST(v4.is_string());
BOOST_TEST(v5.is_string());
BOOST_TEST(toml::get<std::string>(v1) == "foo"); BOOST_TEST(toml::get<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo"); BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo"); BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo"); BOOST_TEST(toml::get<std::string>(v4) == "foo");
BOOST_TEST(toml::get<std::string>(v5) == "\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA");
} }
{ {
{ {
@@ -331,7 +164,7 @@ BOOST_AUTO_TEST_CASE(test_value_as_u8_literal)
toml::local_time(7, 32, 0))); toml::local_time(7, 32, 0)));
} }
{ {
const toml::value v1 = u8"1979-05-27T07:32:00Z"_toml; const toml::value v1 = "1979-05-27T07:32:00Z"_toml;
BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(v1.is_offset_datetime());
BOOST_TEST(toml::get<toml::offset_datetime>(v1) == BOOST_TEST(toml::get<toml::offset_datetime>(v1) ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),

View File

@@ -128,62 +128,3 @@ BOOST_AUTO_TEST_CASE(test_multiline_array_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::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<toml::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<toml::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<toml::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<toml::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)
{
{
array a;
a.push_back("foo");
a.push_back("bar");
a.push_back("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"[ \"foo\" # comment\n"
", \"bar\" # comment\n"
", \"baz\" # comment\n"
"]", toml::value(a));
}
}

View File

@@ -7,7 +7,7 @@
#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \ #define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \
do { \ do { \
const std::string token(tkn); \ const std::string token(tkn); \
toml::detail::location loc("test", token); \ toml::detail::location<std::string> loc("test", token); \
const auto result = psr(loc); \ const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \
@@ -23,7 +23,7 @@ do { \
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ #define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
do { \ do { \
const std::string token(tkn); \ const std::string token(tkn); \
toml::detail::location loc("test", token); \ toml::detail::location<std::string> loc("test", token); \
const auto result = psr(loc); \ const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \

View File

@@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_AUTO_TEST_CASE(test_example_stream) BOOST_AUTO_TEST_CASE(test_example_stream)
{ {
std::ifstream ifs("toml/tests/example.toml", std::ios::binary); std::ifstream ifs("toml/tests/example.toml");
const auto data = toml::parse(ifs); const auto data = toml::parse(ifs);
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example"); BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
@@ -779,22 +779,6 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value"); BOOST_TEST(toml::find<std::string>(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<std::string>(data, "key") == "value");
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// CRLF // CRLF
@@ -905,41 +889,3 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
} }
} }
BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
{
// toml::parse("");
const auto string_literal = toml::parse("toml/tests/example.toml");
BOOST_TEST_MESSAGE("string_literal");
const char* fname_cstring = "toml/tests/example.toml";
// toml::parse(const char*);
const auto cstring = toml::parse(fname_cstring);
BOOST_TEST_MESSAGE("const char*");
// toml::parse(char*);
std::array<char, 24> fname_char_ptr;
std::strncpy(fname_char_ptr.data(), fname_cstring, 24);
const auto char_ptr = toml::parse(fname_char_ptr.data());
BOOST_TEST_MESSAGE("char*");
// toml::parse(const std::string&);
const std::string fname_string("toml/tests/example.toml");
const auto string = toml::parse(fname_string);
std::string fname_string_mut("toml/tests/example.toml");
// toml::parse(std::string&);
const auto string_mutref = toml::parse(fname_string_mut);
// toml::parse(std::string&&);
const auto string_rref = toml::parse(std::move(fname_string_mut));
BOOST_TEST_MESSAGE("strings");
#ifdef TOML11_HAS_STD_FILESYSTEM
const std::filesystem::path fname_path(fname_string.begin(), fname_string.end());
const auto filesystem_path = toml::parse(fname_path);
BOOST_TEST_MESSAGE("path");
#endif
}

View File

@@ -68,13 +68,6 @@ BOOST_AUTO_TEST_CASE(test_exponential)
TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-1_0", 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);
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) BOOST_AUTO_TEST_CASE(test_exponential_value)
@@ -97,13 +90,6 @@ BOOST_AUTO_TEST_CASE(test_exponential_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-1_0", value(123e-10)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-1_0", value(123e-10));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "+0e0", value( 0.0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "+0e0", value( 0.0));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "-0e0", value(-0.0)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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<toml::value>, "1_2_3E-01", value(123e-1));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1_2_3E-0_1", value(123e-1));
#endif
} }
BOOST_AUTO_TEST_CASE(test_fe) BOOST_AUTO_TEST_CASE(test_fe)
{ {
@@ -116,19 +102,13 @@ BOOST_AUTO_TEST_CASE(test_fe_vaule)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e23", value(6.02e23)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e23", value(6.02e23));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e+23", value(6.02e23)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "6.02e+23", value(6.02e23));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1.112_650_06e-17", value(1.11265006e-17)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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<toml::value>, "3.141_5e-01", value(3.1415e-1));
#endif
} }
BOOST_AUTO_TEST_CASE(test_inf) BOOST_AUTO_TEST_CASE(test_inf)
{ {
{ {
const std::string token("inf"); const std::string token("inf");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -136,7 +116,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
} }
{ {
const std::string token("+inf"); const std::string token("+inf");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -144,7 +124,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
} }
{ {
const std::string token("-inf"); const std::string token("-inf");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -156,21 +136,21 @@ BOOST_AUTO_TEST_CASE(test_nan)
{ {
{ {
const std::string token("nan"); const std::string token("nan");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));
} }
{ {
const std::string token("+nan"); const std::string token("+nan");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));
} }
{ {
const std::string token("-nan"); const std::string token("-nan");
toml::detail::location loc("test", token); toml::detail::location<std::string> loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));

View File

@@ -46,19 +46,3 @@ BOOST_AUTO_TEST_CASE(test_inline_table_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "{type.name = \"pug\"}", value(t)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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);
}
}

View File

@@ -29,16 +29,16 @@ BOOST_AUTO_TEST_CASE(test_string)
BOOST_AUTO_TEST_CASE(test_string_value) BOOST_AUTO_TEST_CASE(test_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"The quick brown fox jumps over the lazy dog\"", "\"The quick brown fox jumps over the lazy dog\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\'The quick brown fox jumps over the lazy dog\'", "\'The quick brown fox jumps over the lazy dog\'",
toml::value("The quick brown fox jumps over the lazy dog", string_t::literal)); toml::value("The quick brown fox jumps over the lazy dog", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''The quick brown fox \njumps over the lazy dog'''", "'''The quick brown fox \njumps over the lazy dog'''",
toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal)); toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal));
} }
@@ -69,17 +69,14 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"", "\" And when \\\"'s are in the along with # \\\"\"",
string(" And when \"'s are in the along with # \"", string_t::basic)); 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) BOOST_AUTO_TEST_CASE(test_basic_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"",
value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"192.168.1.1\"", "\"192.168.1.1\"",
value("192.168.1.1", string_t::basic)); value("192.168.1.1", string_t::basic));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
@@ -87,19 +84,16 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", "\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic));
#else #else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"中国\"", "\"中国\"",
value("中国", string_t::basic)); value("中国", string_t::basic));
#endif #endif
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"You'll hate me after this - #\"", "\"You'll hate me after this - #\"",
value("You'll hate me after this - #", string_t::basic)); value("You'll hate me after this - #", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\" And when \\\"'s are in the along with # \\\"\"", "\" And when \\\"'s are in the along with # \\\"\"",
value(" And when \"'s are in the along with # \"", string_t::basic)); value(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
} }
BOOST_AUTO_TEST_CASE(test_ml_basic_string) BOOST_AUTO_TEST_CASE(test_ml_basic_string)
@@ -110,41 +104,16 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_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 \"\"\"",
string("The quick brown fox jumps over the lazy dog.", string_t::basic)); 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) BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "\"\"\"\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)); value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\\\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 \"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic)); value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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<toml::value>,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
value("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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) BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -165,16 +134,16 @@ BOOST_AUTO_TEST_CASE(test_literal_string)
BOOST_AUTO_TEST_CASE(test_literal_string_value) BOOST_AUTO_TEST_CASE(test_literal_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'C:\\Users\\nodejs\\templates'", "'C:\\Users\\nodejs\\templates'",
value("C:\\Users\\nodejs\\templates", string_t::literal)); value("C:\\Users\\nodejs\\templates", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'\\\\ServerX\\admin$\\system32\\'", "'\\\\ServerX\\admin$\\system32\\'",
value("\\\\ServerX\\admin$\\system32\\", string_t::literal)); value("\\\\ServerX\\admin$\\system32\\", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'Tom \"Dubs\" Preston-Werner'", "'Tom \"Dubs\" Preston-Werner'",
value("Tom \"Dubs\" Preston-Werner", string_t::literal)); value("Tom \"Dubs\" Preston-Werner", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'<\\i\\c*\\s*>'", "'<\\i\\c*\\s*>'",
value("<\\i\\c*\\s*>", string_t::literal)); value("<\\i\\c*\\s*>", string_t::literal));
} }
@@ -187,34 +156,16 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_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'''",
string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); 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) BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''I [dw]on't need \\d{2} apples'''", "'''I [dw]on't need \\d{2} apples'''",
value("I [dw]on't need \\d{2} apples", string_t::literal)); value("I [dw]on't need \\d{2} apples", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''\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'''",
value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); 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<toml::value>,
"''''That's still pointless', she said.'''",
value("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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_unicode_escape_sequence) BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence)

View File

@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_normal_table)
"key2 = 42\n" "key2 = 42\n"
"key3 = 3.14\n" "key3 = 3.14\n"
); );
location loc("test", table); location<std::string> loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc); const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok()); BOOST_TEST(result.is_ok());
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(test_nested_table)
"a.b = \"value\"\n" "a.b = \"value\"\n"
"a.c.d = 42\n" "a.c.d = 42\n"
); );
location loc("test", table); location<std::string> loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc); const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok()); BOOST_TEST(result.is_ok());

View File

@@ -9,6 +9,7 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
BOOST_AUTO_TEST_CASE(test_hard_example_unicode) BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{ {
const auto data = toml::parse("toml/tests/hard_example_unicode.toml"); const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
@@ -39,3 +40,35 @@ BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) == BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array); expected_multi_line_array);
} }
#else
BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{
const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
const auto the = toml::find<toml::table>(data, "the");
BOOST_TEST(toml::get<std::string>(the.at("test_string")) ==
std::string(u8"Ýôú' λáƭè ₥è áƒƭèř ƭλïƨ - #"));
const auto hard = toml::get<toml::table>(the.at("hard"));
const std::vector<std::string> expected_the_hard_test_array{"] ", " # "};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array")) ==
expected_the_hard_test_array);
const std::vector<std::string> expected_the_hard_test_array2{
std::string(u8"Tèƨƭ #11 ]ƥřôƲèδ ƭλáƭ"),
std::string(u8"Éжƥèřï₥èñƭ #9 ωáƨ á ƨúççèƨƨ")};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array2")) ==
expected_the_hard_test_array2);
BOOST_TEST(toml::get<std::string>(hard.at("another_test_string")) ==
std::string(u8"§á₥è ƭλïñϱ, βúƭ ωïƭλ á ƨƭřïñϱ #"));
BOOST_TEST(toml::get<std::string>(hard.at("harder_test_string")) ==
std::string(u8" Âñδ ωλèñ \"'ƨ ářè ïñ ƭλè ƨƭřïñϱ, áℓôñϱ ωïƭλ # \""));
const auto bit = toml::get<toml::table>(hard.at(std::string(u8"βïƭ#")));
BOOST_TEST(toml::get<std::string>(bit.at(std::string(u8"ωλáƭ?"))) ==
std::string(u8"Ýôú δôñ'ƭ ƭλïñƙ ƨô₥è úƨèř ωôñ'ƭ δô ƭλáƭ?"));
const std::vector<std::string> expected_multi_line_array{"]"};
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
#endif

View File

@@ -10,7 +10,6 @@
#include <map> #include <map>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <sstream>
template<typename Comment, template<typename Comment,
template<typename ...> class Table, template<typename ...> class Table,
@@ -304,60 +303,3 @@ BOOST_AUTO_TEST_CASE(test_format_key)
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key)); 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<toml::preserve_comments>(iss);
const auto serialized = toml::format(data, /*width = */ 0);
std::istringstream reparse(serialized);
const auto parsed = toml::parse<toml::preserve_comments>(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)");
}

View File

@@ -111,44 +111,3 @@ BOOST_AUTO_TEST_CASE(test_literal_ml_string)
} }
} }
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 __cplusplus >= 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");
}
}

View File

@@ -9,24 +9,27 @@
#include <vector> #include <vector>
#include <array> #include <array>
BOOST_AUTO_TEST_CASE(test_try_reserve) BOOST_AUTO_TEST_CASE(test_resize)
{ {
{ {
// since BOOST_TEST is a macro, it cannot handle commas correctly. typedef std::vector<int> resizable_type;
// When toml::detail::has_reserve_method<std::array<int, 1>>::value typedef std::array<int,1> non_resizable_type;
// is passed to a macro, C preprocessor considers BOOST_TEST(toml::detail::has_resize_method<resizable_type>::value);
// toml::detail::has_reserve_method<std::array<int as the first argument BOOST_TEST(!toml::detail::has_resize_method<non_resizable_type>::value);
// and 1>>::value as the second argument. We need an alias to avoid
// this problem.
using reservable_type = std::vector<int> ;
using nonreservable_type = std::array<int, 1>;
BOOST_TEST( toml::detail::has_reserve_method<reservable_type >::value);
BOOST_TEST(!toml::detail::has_reserve_method<nonreservable_type>::value);
} }
{ {
std::vector<int> v; std::vector<int> v;
toml::try_reserve(v, 100); toml::resize(v, 10);
BOOST_TEST(v.capacity() == 100u); BOOST_TEST(v.size() == 10u);
}
{
std::array<int, 15> a;
toml::resize(a, 10);
BOOST_TEST(a.size() == 15u);
}
{
std::array<int, 15> a;
BOOST_CHECK_THROW(toml::resize(a, 20), std::invalid_argument);
} }
} }

View File

@@ -905,115 +905,3 @@ BOOST_AUTO_TEST_CASE(test_value_empty)
BOOST_CHECK_THROW(v1.as_array(), toml::type_error); BOOST_CHECK_THROW(v1.as_array(), toml::type_error);
BOOST_CHECK_THROW(v1.as_table(), 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);
}
}

View File

@@ -1,12 +0,0 @@
#include <windows.h>
#include <toml.hpp>
#include <iostream>
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<std::string>(data, "windows") << std::endl;
return 0;
}

View File

@@ -33,13 +33,10 @@
# error "toml11 requires C++11 or later." # error "toml11 requires C++11 or later."
#endif #endif
#define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 6
#define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp" #include "toml/literal.hpp"
#include "toml/serializer.hpp" #include "toml/serializer.hpp"
#include "toml/get.hpp" #include "toml/get.hpp"
#include "toml/find.hpp"
#endif// TOML_FOR_MODERN_CPP #endif// TOML_FOR_MODERN_CPP

View File

@@ -1,64 +0,0 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <cstdint>
#include <ostream>
#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;
}
} // 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;}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently
namespace color = color_ansi;
} // toml
#endif// TOML11_COLOR_HPP

View File

@@ -2,20 +2,18 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_COMBINATOR_HPP #ifndef TOML11_COMBINATOR_HPP
#define TOML11_COMBINATOR_HPP #define TOML11_COMBINATOR_HPP
#include <cassert> #include "traits.hpp"
#include <cctype> #include "result.hpp"
#include <cstdio> #include "utility.hpp"
#include "region.hpp"
#include <array> #include <type_traits>
#include <iomanip>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <type_traits> #include <array>
#include <iomanip>
#include "region.hpp" #include <cstdio>
#include "result.hpp" #include <cassert>
#include "traits.hpp" #include <cctype>
#include "utility.hpp"
// they scans characters and returns region if it matches to the condition. // they scans characters and returns region if it matches to the condition.
// when they fail, it does not change the location. // when they fail, it does not change the location.
@@ -47,7 +45,6 @@ inline std::string show_char(const char c)
buf.fill('\0'); buf.fill('\0');
const auto r = std::snprintf( const auto r = std::snprintf(
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF); buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1); assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data()); return std::string(buf.data());
} }
@@ -58,9 +55,13 @@ struct character
{ {
static constexpr char target = C; static constexpr char target = C;
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
@@ -71,7 +72,7 @@ struct character
} }
loc.advance(); // update location loc.advance(); // update location
return ok(region(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
}; };
template<char C> template<char C>
@@ -87,9 +88,13 @@ struct in_range
static constexpr char upper = Up; static constexpr char upper = Up;
static constexpr char lower = Low; static constexpr char lower = Low;
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
@@ -100,7 +105,7 @@ struct in_range
} }
loc.advance(); loc.advance();
return ok(region(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
}; };
template<char L, char U> constexpr char in_range<L, U>::upper; template<char L, char U> constexpr char in_range<L, U>::upper;
@@ -111,9 +116,13 @@ template<char L, char U> constexpr char in_range<L, U>::lower;
template<typename Combinator> template<typename Combinator>
struct exclude struct exclude
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter(); auto first = loc.iter();
@@ -124,7 +133,7 @@ struct exclude
return none(); return none();
} }
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
}; };
@@ -132,15 +141,19 @@ struct exclude
template<typename Combinator> template<typename Combinator>
struct maybe struct maybe
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Combinator::invoke(loc); const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok()) if(rslt.is_ok())
{ {
return rslt; return rslt;
} }
return ok(region(loc)); return ok(region<Cont>(loc));
} }
}; };
@@ -150,9 +163,13 @@ struct sequence;
template<typename Head, typename ... Tail> template<typename Head, typename ... Tail>
struct sequence<Head, Tail...> struct sequence<Head, Tail...>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto first = loc.iter(); const auto first = loc.iter();
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
@@ -164,9 +181,9 @@ struct sequence<Head, Tail...>
} }
// called from the above function only, recursively. // called from the above function only, recursively.
template<typename Iterator> template<typename Cont, typename Iterator>
static result<region, none_t> static result<region<Cont>, none_t>
invoke(location& loc, region reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
@@ -183,9 +200,9 @@ template<typename Head>
struct sequence<Head> struct sequence<Head>
{ {
// would be called from sequence<T ...>::invoke only. // would be called from sequence<T ...>::invoke only.
template<typename Iterator> template<typename Cont, typename Iterator>
static result<region, none_t> static result<region<Cont>, none_t>
invoke(location& loc, region reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
@@ -204,9 +221,13 @@ struct either;
template<typename Head, typename ... Tail> template<typename Head, typename ... Tail>
struct either<Head, Tail...> struct either<Head, Tail...>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;} if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc); return either<Tail...>::invoke(loc);
@@ -215,9 +236,12 @@ struct either<Head, Tail...>
template<typename Head> template<typename Head>
struct either<Head> struct either<Head>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
return Head::invoke(loc); return Head::invoke(loc);
} }
}; };
@@ -232,10 +256,11 @@ struct unlimited{};
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct repeat<T, exactly<N>> struct repeat<T, exactly<N>>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region retval(loc); region<Cont> retval(loc);
const auto first = loc.iter(); const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i) for(std::size_t i=0; i<N; ++i)
{ {
@@ -254,10 +279,11 @@ struct repeat<T, exactly<N>>
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct repeat<T, at_least<N>> struct repeat<T, at_least<N>>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region retval(loc); region<Cont> retval(loc);
const auto first = loc.iter(); const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i) for(std::size_t i=0; i<N; ++i)
@@ -285,10 +311,11 @@ struct repeat<T, at_least<N>>
template<typename T> template<typename T>
struct repeat<T, unlimited> struct repeat<T, unlimited>
{ {
static result<region, none_t> template<typename Cont>
invoke(location& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region retval(loc); region<Cont> retval(loc);
while(true) while(true)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc);

View File

@@ -2,13 +2,12 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_COMMENTS_HPP #ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <iterator>
#include <initializer_list>
#include <vector> #include <vector>
#include <string>
// This file provides mainly two classes, `preserve_comments` and `discard_comments`. // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>` // Those two are a container that have the same interface as `std::vector<std::string>`
@@ -82,54 +81,6 @@ struct preserve_comments
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);} void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
void assign(size_type n, const std::string& val) {comments.assign(n, val);} 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<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
void insert(iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(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) iterator insert(const_iterator p, const std::string& x)
{ {
return comments.insert(p, x); return comments.insert(p, x);
@@ -163,7 +114,6 @@ struct preserve_comments
{ {
return comments.erase(first, last); return comments.erase(first, last);
} }
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);} void swap(preserve_comments& other) {comments.swap(other.comments);}

View File

@@ -2,16 +2,15 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_DATETIME_HPP #ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP #define TOML11_DATETIME_HPP
#include <chrono>
#include <tuple>
#include <array>
#include <ostream>
#include <iomanip>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <ctime> #include <ctime>
#include <array>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <tuple>
namespace toml namespace toml
{ {
@@ -21,7 +20,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -29,14 +28,7 @@ inline std::tm localtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("localtime_r failed."); } if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst; return dst;
} }
inline std::tm gmtime_s(const std::time_t* src) #elif _MSC_VER
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -44,26 +36,13 @@ inline std::tm localtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("localtime_s failed."); } if (result) { throw std::runtime_error("localtime_s failed."); }
return dst; return dst;
} }
inline std::tm gmtime_s(const std::time_t* src) #else
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
const auto result = std::localtime(src); const auto result = std::localtime(src);
if (!result) { throw std::runtime_error("localtime failed."); } if (!result) { throw std::runtime_error("localtime failed."); }
return *result; 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 #endif
} // detail } // detail
@@ -173,9 +152,9 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date) operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{ {
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-'; os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-'; os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month + 1) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ; os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day );
return os; return os;
} }
@@ -399,31 +378,10 @@ struct local_datetime
{ {
using internal_duration = using internal_duration =
typename std::chrono::system_clock::time_point::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<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(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 // std::mktime returns date as local time zone. no conversion needed
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); auto dt = std::chrono::system_clock::time_point(this->date);
dt += std::chrono::duration_cast<internal_duration>( dt += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) + std::chrono::nanoseconds(this->time));
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
return dt; return dt;
} }
@@ -489,71 +447,40 @@ struct offset_datetime
: date(dt.date), time(dt.time), offset(o) : date(dt.date), time(dt.time), offset(o)
{} {}
explicit offset_datetime(const local_datetime& ld) explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) : date(ld.date), time(ld.time), offset(get_local_offset())
// use the current local timezone offset
{} {}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp) explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset(0, 0) // use gmtime : offset_datetime(local_datetime(tp))
{ {}
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) explicit offset_datetime(const std::time_t& t)
: offset(0, 0) // use gmtime : offset_datetime(local_datetime(t))
{ {}
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::tm& t) explicit offset_datetime(const std::tm& t)
: offset(0, 0) // assume gmtime : offset_datetime(local_datetime(t))
{ {}
this->date = local_date(t);
this->time = local_time(t);
}
operator std::chrono::system_clock::time_point() const operator std::chrono::system_clock::time_point() const
{ {
// get date-time // get date-time
using internal_duration = using internal_duration =
typename std::chrono::system_clock::time_point::duration; typename std::chrono::system_clock::time_point::duration;
std::chrono::system_clock::time_point tp =
std::chrono::system_clock::time_point(this->date) +
std::chrono::duration_cast<internal_duration>(
std::chrono::nanoseconds(this->time));
// first, convert it to local date-time information in the same way as // get date-time in UTC. let's say we are in +09:00 (JPN).
// local_datetime does. later we will use time_t to adjust time offset. // writing 12:00:00 in +09:00 means 03:00:00Z. to represent
std::tm t; // 12:00:00Z, first we need to add +09:00.
t.tm_sec = static_cast<int>(this->time.second); const auto ofs = get_local_offset();
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(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<internal_duration>(
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::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute); tp += std::chrono::minutes(ofs.minute);
// We got `12:00:00Z` by correcting local timezone applied by mktime. // here, tp represents 12:00:00 in UTC but we have offset information.
// Then we will apply the offset. Let's say `12:00:00-08:00` is given. // we need to subtract it. For example, let's say the input is
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. // 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above
// So we need to subtract the offset. // conversion. But the actual time we need to return is 20:00:00Z
// because of -08:00.
tp -= std::chrono::minutes(this->offset); tp -= std::chrono::minutes(this->offset);
return tp; return tp;
} }
@@ -573,10 +500,11 @@ struct offset_datetime
private: private:
static time_offset get_local_offset(const std::time_t* tp) static time_offset get_local_offset()
{ {
// get local timezone with the same date-time information as mktime // get current timezone
const auto t = detail::localtime_s(tp); const auto tmp1 = std::time(nullptr);
const auto t = detail::localtime_s(&tmp1);
std::array<char, 6> buf; std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0

View File

@@ -5,29 +5,21 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include "source_location.hpp"
namespace toml namespace toml
{ {
struct exception : public std::exception struct exception : public std::exception
{ {
public: public:
explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default; virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";} 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 struct syntax_error : public toml::exception
{ {
public: public:
explicit syntax_error(const std::string& what_arg, const source_location& loc) explicit syntax_error(const std::string& what_arg) : what_(what_arg){}
: exception(loc), what_(what_arg) explicit syntax_error(const char* what_arg) : what_(what_arg){}
{}
virtual ~syntax_error() noexcept override = default; virtual ~syntax_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();} virtual const char* what() const noexcept override {return what_.c_str();}
@@ -38,9 +30,8 @@ struct syntax_error : public toml::exception
struct type_error : public toml::exception struct type_error : public toml::exception
{ {
public: public:
explicit type_error(const std::string& what_arg, const source_location& loc) explicit type_error(const std::string& what_arg) : what_(what_arg){}
: exception(loc), what_(what_arg) explicit type_error(const char* what_arg) : what_(what_arg){}
{}
virtual ~type_error() noexcept override = default; virtual ~type_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();} virtual const char* what() const noexcept override {return what_.c_str();}
@@ -51,12 +42,10 @@ struct type_error : public toml::exception
struct internal_error : public toml::exception struct internal_error : public toml::exception
{ {
public: public:
explicit internal_error(const std::string& what_arg, const source_location& loc) explicit internal_error(const std::string& what_arg) : what_(what_arg){}
: exception(loc), what_(what_arg) explicit internal_error(const char* what_arg) : what_(what_arg){}
{}
virtual ~internal_error() noexcept override = default; virtual ~internal_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();} virtual const char* what() const noexcept override {return what_.c_str();}
protected: protected:
std::string what_; std::string what_;
}; };

786
toml/find.hpp Normal file
View File

@@ -0,0 +1,786 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FIND_HPP
#define TOML11_FIND_HPP
#include "get.hpp"
#include <numeric>
namespace toml
{
// ----------------------------------------------------------------------------
// these overloads do not require to set T. and returns value itself.
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&& find(basic_value<C, M, V>&& v, const key& ky)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return std::move(tab.at(ky));
}
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const key& ky)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const key& ky)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const key& ky)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, const basic_value<C, M, V>&>
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, basic_value<C, M, V>&>
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, basic_value<C, M, V>&&>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<const basic_value<C, M, V>&>()))>
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&>()))>
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
detail::enable_if_t<detail::conjunction<std::is_convertible<Ts, std::string>...
>::value, decltype(get<T>(std::declval<basic_value<C, M, V>&&>()))>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
}
// ===========================================================================
// find_or(value, key, fallback)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find_or(const basic_value<C, M, V>& v, const key& ky,
const basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab[ky];
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
{
if(!v.is_table()) {return std::move(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::move(opt);}
return std::move(tab[ky]);
}
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
find_or(const basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
find_or(basic_value<C, M, V>& 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[ky], opt);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
find_or(const basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
find_or(basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::string>
find_or(const basic_value<C, M, V>& 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<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
// T is not an exact toml type
detail::negation<detail::is_exact_toml_type<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
basic_value<C, M, V>>>,
// T is not std::string
detail::negation<std::is_same<std::string,
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
// T is not a string literal
detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>>
>::value, typename std::remove_cv<typename std::remove_reference<T>::type>::type>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ============================================================================
// expect
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string>
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
{
try
{
return ok(find<T>(v, k));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename Table>
detail::enable_if_t<detail::conjunction<
detail::is_map<Table>, detail::is_basic_value<typename Table::mapped_type>
>::value, result<T, std::string>>
expect(const Table& t, const toml::key& k,
std::string tablename = "unknown table") noexcept
{
try
{
return ok(find<T>(t, k, std::move(tablename)));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
// ===========================================================================
// find_fuzzy
// ---------------------------------------------------------------------------
// default fuzzy matcher; levenstein distance (all cost is 1)
struct levenstein_matcher
{
levenstein_matcher(): tolerance(1) {}
levenstein_matcher(const std::uint32_t tol): tolerance(tol) {}
~levenstein_matcher() = default;
levenstein_matcher(levenstein_matcher const&) = default;
levenstein_matcher(levenstein_matcher &&) = default;
levenstein_matcher& operator=(levenstein_matcher const&) = default;
levenstein_matcher& operator=(levenstein_matcher &&) = default;
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
bool operator()(const std::basic_string<charT, traitsT, Alloc1>& lhs,
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
{
return this->distance(lhs, rhs) <= this->tolerance;
}
template<typename charT, typename traitsT, typename Alloc1, typename Alloc2>
std::uint32_t distance(
const std::basic_string<charT, traitsT, Alloc1>& lhs,
const std::basic_string<charT, traitsT, Alloc2>& rhs) const
{
// force `lhs.size() <= rhs.size()`
if(lhs.size() > rhs.size()) {return this->distance(rhs, lhs);}
std::vector<std::uint32_t> matrix(lhs.size() + 1u);
std::iota(matrix.begin(), matrix.end(), 0);
for(const charT r : rhs)
{
std::uint32_t prev_diag = matrix.front();
matrix.front() += 1;
for(std::size_t i=0; i<lhs.size(); ++i)
{
const charT l = lhs[i];
if(traitsT::eq(l, r))
{
std::swap(matrix[i+1], prev_diag);
}
else
{
const auto tmp = matrix[i+1];
matrix[i+1] = std::min(prev_diag, std::min(matrix[i], matrix[i+1])) + 1;
prev_diag = tmp;
}
}
}
return matrix.back();
}
private:
std::uint32_t tolerance;
};
// ---------------------------------------------------------------------------
// toml::find_fuzzy<T>(v, "tablename", FuzzyMatcher);
namespace detail
{
template<typename Iterator, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
Iterator find_unique(
Iterator iter, const Iterator end, const basic_value<C, M, V>& v,
const toml::key& k, const FuzzyMatcher& match)
{
Iterator found = end;
for(; iter != end; ++iter)
{
if(match(iter->first, k))
{
if(found != end)
{
throw std::out_of_range(detail::format_underline(
concat_to_string("[error] key \"", k, "\" not found."),
{
{std::addressof(detail::get_region(v)),"in this table"},
{std::addressof(detail::get_region(found->second)),
"did you mean this here?"},
{std::addressof(detail::get_region(iter->second)),
"or this?"}
}));
}
found = iter;
}
}
return found;
}
} // detail
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<const basic_value<C, M, V>&>(), ky))
{
try
{
return find<T>(v, ky);
}
catch(const std::out_of_range& oor)
{
const auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(found->second);
}
throw;
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<basic_value<C, M, V>&>(), ky))
{
try
{
return find<T>(v, ky);
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(found->second);
}
throw;
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
auto find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
-> decltype(find<T>(std::declval<basic_value<C, M, V>&&>(), ky))
{
basic_value<C, M, V> v = v_; // to re-use later, store it once
try
{
return std::move(find<T>(v, ky)); // pass lref, move later
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table(); // because v is used here
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return get<T>(std::move(found->second));
}
throw;
}
}
// ---------------------------------------------------------------------------
// no-template-argument case (by default, return toml::value).
// toml::find_fuzzy(v, "tablename", FuzzyMatcher);
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V> const&
find_fuzzy(const basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
try
{
return find(v, ky);
}
catch(const std::out_of_range& oor)
{
const auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return found->second;
}
throw;
}
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V>&
find_fuzzy(basic_value<C, M, V>& v, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
try
{
return find(v, ky);
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return found->second;
}
throw;
}
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher = levenstein_matcher>
basic_value<C, M, V>&&
find_fuzzy(basic_value<C, M, V>&& v_, const key& ky,
const FuzzyMatcher match = levenstein_matcher(1))
{
basic_value<C, M, V> v = v_; // to re-use later, store it once
try
{
return std::move(find(v, ky));
}
catch(const std::out_of_range& oor)
{
auto& t = v.as_table();
const auto found = detail::find_unique(t.begin(), t.end(), v, ky, match);
if(found != t.end())
{
return std::move(found->second);
}
throw;
}
}
// ===========================================================================
// find(v, k, matcher)
//
// when matcher is passed, check a key that matches exists or not. if it exists,
// suggest that in the error message
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
const auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V>&
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
auto& tab = v.template cast<value_t::table>();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
basic_value<C, M, V>&&
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
{
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return std::move(tab.at(ky));
}
// ----------------------------------------------------------------------------
// find<T>(value, key, fuzzy_matcher);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))>
find(const basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))>
find(basic_value<C, M, V>& v, const key& ky, FuzzyMatcher match)
{
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename FuzzyMatcher>
detail::enable_if_t<
detail::negation<std::is_convertible<FuzzyMatcher, std::string>>::value,
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))>
find(basic_value<C, M, V>&& v, const key& ky, FuzzyMatcher match)
{
auto tab = v.as_table();
if(tab.count(ky) == 0)
{
for(const auto& kv : tab)
{
if(match(kv.first, ky))
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found."), {
{std::addressof(detail::get_region(v)), "in this table"},
{std::addressof(detail::get_region(kv.second)),
"did you mean this here?"}
}));
}
}
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
} // toml
#endif// TOML11_FIND_HPP

View File

@@ -2,11 +2,10 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_GET_HPP #ifndef TOML11_GET_HPP
#define TOML11_GET_HPP #define TOML11_GET_HPP
#include <algorithm>
#include "from.hpp" #include "from.hpp"
#include "result.hpp" #include "result.hpp"
#include "value.hpp" #include "value.hpp"
#include <algorithm>
namespace toml namespace toml
{ {
@@ -32,10 +31,10 @@ get(const basic_value<C, M, V>& v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> &&
get(basic_value<C, M, V>&& v) get(basic_value<C, M, V>&& v)
{ {
return T(std::move(v).template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>()); return std::move(v).template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>();
} }
// ============================================================================ // ============================================================================
@@ -59,10 +58,10 @@ get(const basic_value<C, M, V>& v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T> inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T> &&
get(basic_value<C, M, V>&& v) get(basic_value<C, M, V>&& v)
{ {
return basic_value<C, M, V>(std::move(v)); return std::move(v);
} }
// ============================================================================ // ============================================================================
@@ -91,7 +90,7 @@ inline detail::enable_if_t<detail::conjunction<
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return static_cast<T>(v.as_integer()); return static_cast<T>(v.template cast<value_t::integer>());
} }
// ============================================================================ // ============================================================================
@@ -106,7 +105,7 @@ inline detail::enable_if_t<detail::conjunction<
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return static_cast<T>(v.as_floating()); return static_cast<T>(v.template cast<value_t::floating>());
} }
// ============================================================================ // ============================================================================
@@ -118,7 +117,7 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>& inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
get(basic_value<C, M, V>& v) get(basic_value<C, M, V>& v)
{ {
return v.as_string().str; return v.template cast<value_t::string>().str;
} }
template<typename T, typename C, template<typename T, typename C,
@@ -126,15 +125,15 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const& inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return v.as_string().str; return v.template cast<value_t::string>().str;
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
get(basic_value<C, M, V>&& v) get(basic_value<C, M, V>&& v)
{ {
return std::string(std::move(v.as_string().str)); return std::move(v.template cast<value_t::string>().str);
} }
// ============================================================================ // ============================================================================
@@ -146,7 +145,7 @@ template<typename T, typename C,
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view> inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return std::string_view(v.as_string().str); return std::string_view(v.template cast<value_t::string>().str);
} }
#endif #endif
@@ -159,7 +158,7 @@ inline detail::enable_if_t<detail::is_chrono_duration<T>::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
return std::chrono::duration_cast<T>( return std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.as_local_time())); std::chrono::nanoseconds(v.template cast<value_t::local_time>()));
} }
// ============================================================================ // ============================================================================
@@ -175,22 +174,26 @@ get(const basic_value<C, M, V>& v)
{ {
case value_t::local_date: case value_t::local_date:
{ {
return std::chrono::system_clock::time_point(v.as_local_date()); return std::chrono::system_clock::time_point(
v.template cast<value_t::local_date>());
} }
case value_t::local_datetime: case value_t::local_datetime:
{ {
return std::chrono::system_clock::time_point(v.as_local_datetime()); return std::chrono::system_clock::time_point(
v.template cast<value_t::local_datetime>());
} }
case value_t::offset_datetime: case value_t::offset_datetime:
{ {
return std::chrono::system_clock::time_point(v.as_offset_datetime()); return std::chrono::system_clock::time_point(
v.template cast<value_t::offset_datetime>());
} }
default: default:
{ {
throw type_error(detail::format_underline("toml::value: " throw type_error(detail::format_underline("[error] toml::value "
"bad_cast to std::chrono::system_clock::time_point", { "bad_cast to std::chrono::system_clock::time_point", {
{v.location(), concat_to_string("the actual type is ", v.type())} {std::addressof(detail::get_region(v)),
}), v.location()); concat_to_string("the actual type is ", v.type())}
}));
} }
} }
} }
@@ -198,24 +201,24 @@ get(const basic_value<C, M, V>& v)
// ============================================================================ // ============================================================================
// forward declaration to use this recursively. ignore this and go ahead. // forward declaration to use this recursively. ignore this and go ahead.
// array-like type with push_back(value) method // array-like type with resize(N) method
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is container
detail::has_push_back_method<T>, // T::push_back(value) works detail::has_resize_method<T>, // T::resize(N) works
detail::negation< // but not toml::array detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
// array-like type without push_back(value) method // array-like type with resize(N) method
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...) detail::negation<detail::has_resize_method<T>>, // no T::resize() exists
detail::negation< // not toml::array detail::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
@@ -256,92 +259,58 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v) // toml::from<T>::from_toml(v)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
std::size_t S = sizeof(::toml::from<T>)> std::size_t S = sizeof(::toml::into<T>)>
T get(const basic_value<C, M, V>&); T get(const basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>&);
// ============================================================================ // ============================================================================
// array-like types; most likely STL container, like std::vector, etc. // array-like types; most likely STL container, like std::vector, etc.
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is container
detail::has_push_back_method<T>, // container.push_back(elem) works detail::has_resize_method<T>, // T::resize(N) works
detail::negation< // but not toml::array detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
using value_type = typename T::value_type; using value_type = typename T::value_type;
const auto& ary = v.as_array(); const auto& ar = v.template cast<value_t::array>();
T container; T container;
try_reserve(container, ary.size()); container.resize(ar.size());
std::transform(ar.cbegin(), ar.cend(), container.begin(),
for(const auto& elem : ary) [](const value& x){return ::toml::get<value_type>(x);});
{
container.push_back(get<value_type>(elem));
}
return container; return container;
} }
// ============================================================================ // ============================================================================
// std::forward_list does not have push_back, insert, or emplace. // array-like types; but does not have resize(); most likely std::array.
// It has insert_after, emplace_after, push_front.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_std_forward_list<T>::value, T>
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array())
{
container.push_front(get<value_type>(elem));
}
container.reverse();
return container;
}
// ============================================================================
// array-like types, without push_back(). most likely [std|boost]::array.
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container detail::is_container<T>, // T is container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back detail::negation<detail::has_resize_method<T>>, // no T::resize() exists
detail::negation< // T is not toml::array detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
using value_type = typename T::value_type; using value_type = typename T::value_type;
const auto& ar = v.as_array(); const auto& ar = v.template cast<value_t::array>();
T container; T container;
if(ar.size() != container.size()) if(ar.size() != container.size())
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified container size is ", container.size(), "[erorr] toml::get specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."), { " but there are ", ar.size(), " elements in toml array."), {
{v.location(), "here"} {std::addressof(detail::get_region(v)), "here"}
})); }));
} }
for(std::size_t i=0; i<ar.size(); ++i) std::transform(ar.cbegin(), ar.cend(), container.begin(),
{ [](const value& x){return ::toml::get<value_type>(x);});
container[i] = ::toml::get<value_type>(ar[i]);
}
return container; return container;
} }
@@ -356,12 +325,14 @@ get(const basic_value<C, M, V>& v)
using first_type = typename T::first_type; using first_type = typename T::first_type;
using second_type = typename T::second_type; using second_type = typename T::second_type;
const auto& ar = v.as_array(); const auto& ar = v.template cast<value_t::array>();
if(ar.size() != 2) if(ar.size() != 2)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::pair but there are ", ar.size(), "[erorr] toml::get specified std::pair but there are ", ar.size(),
" elements in toml array."), {{v.location(), "here"}})); " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
} }
return std::make_pair(::toml::get<first_type >(ar.at(0)), return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1))); ::toml::get<second_type>(ar.at(1)));
@@ -385,13 +356,15 @@ template<typename T, typename C,
detail::enable_if_t<detail::is_std_tuple<T>::value, T> detail::enable_if_t<detail::is_std_tuple<T>::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
const auto& ar = v.as_array(); const auto& ar = v.template cast<value_t::array>();
if(ar.size() != std::tuple_size<T>::value) if(ar.size() != std::tuple_size<T>::value)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::tuple with ", "[erorr] toml::get specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(), std::tuple_size<T>::value, "elements, but there are ", ar.size(),
" elements in toml array."), {{v.location(), "here"}})); " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
} }
return detail::get_tuple_impl<T>(ar, return detail::get_tuple_impl<T>(ar,
detail::make_index_sequence<std::tuple_size<T>::value>{}); detail::make_index_sequence<std::tuple_size<T>::value>{});
@@ -415,9 +388,9 @@ get(const basic_value<C, M, V>& v)
"toml::get only supports map type of which key_type is " "toml::get only supports map type of which key_type is "
"convertible from std::string."); "convertible from std::string.");
T map; T map;
for(const auto& kv : v.as_table()) for(const auto& kv : v.template cast<value_t::table>())
{ {
map.emplace(key_type(kv.first), get<mapped_type>(kv.second)); map[key_type(kv.first)] = ::toml::get<mapped_type>(kv.second);
} }
return map; return map;
} }
@@ -447,280 +420,6 @@ T get(const basic_value<C, M, V>& v)
return ::toml::from<T>::from_toml(v); return ::toml::from<T>::from_toml(v);
} }
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================
// find
// ----------------------------------------------------------------------------
// these overloads do not require to set T. and returns value itself.
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const& find(const basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
{
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
detail::throw_key_not_found_error(v, ky);
}
return basic_value<C, M, V>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find(value, idx)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& 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<C, M, V>(std::move(ary.at(idx)));
}
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& 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<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& 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<T>(tab.at(ky));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const key& ky)
{
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
detail::throw_key_not_found_error(v, ky);
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& 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<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& 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<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const std::size_t idx)
{
typename basic_value<C, M, V>::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<T>(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<std::string>(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<typename T>
enable_if_t<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>::value, std::size_t>
key_cast(T&& v) noexcept
{
return std::size_t(v);
}
template<typename T>
enable_if_t<negation<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>>::value, std::string>
key_cast(T&& v) noexcept
{
return std::string(std::forward<T>(v));
}
} // detail
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
const basic_value<C, M, V>&
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>&
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(std::move(v), std::forward<Key1>(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
// ============================================================================ // ============================================================================
// get_or(value, fallback) // get_or(value, fallback)
@@ -757,7 +456,8 @@ get_or(const basic_value<C, M, V>& v, const T& opt)
{ {
try try
{ {
return get<detail::remove_cvref_t<T>>(v); return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
} }
catch(...) catch(...)
{ {
@@ -772,7 +472,8 @@ get_or(basic_value<C, M, V>& v, T& opt)
{ {
try try
{ {
return get<detail::remove_cvref_t<T>>(v); return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
} }
catch(...) catch(...)
{ {
@@ -781,17 +482,18 @@ get_or(basic_value<C, M, V>& v, T& opt)
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_exact_toml_type<detail::remove_cvref_t<T>, detail::enable_if_t<
basic_value<C, M, V>>::value, detail::remove_cvref_t<T>> detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&&
get_or(basic_value<C, M, V>&& v, T&& opt) get_or(basic_value<C, M, V>&& v, T&& opt)
{ {
try try
{ {
return get<detail::remove_cvref_t<T>>(std::move(v)); return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
} }
catch(...) catch(...)
{ {
return detail::remove_cvref_t<T>(std::forward<T>(opt)); return opt;
} }
} }
@@ -800,13 +502,14 @@ get_or(basic_value<C, M, V>&& v, T&& opt)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<detail::remove_cvref_t<T>, std::string>::value, detail::enable_if_t<std::is_same<
std::string> const& typename std::remove_cv<typename std::remove_reference<T>::type>::type,
get_or(const basic_value<C, M, V>& v, const T& opt) std::string>::value, std::string> const&
get_or(const basic_value<C, M, V>& v, T&& opt)
{ {
try try
{ {
return v.as_string().str; return v.template cast<value_t::string>().str;
} }
catch(...) catch(...)
{ {
@@ -820,7 +523,7 @@ get_or(basic_value<C, M, V>& v, T& opt)
{ {
try try
{ {
return v.as_string().str; return v.template cast<value_t::string>().str;
} }
catch(...) catch(...)
{ {
@@ -829,17 +532,18 @@ get_or(basic_value<C, M, V>& v, T& opt)
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t< detail::enable_if_t<std::is_same<
std::is_same<detail::remove_cvref_t<T>, std::string>::value, std::string> typename std::remove_cv<typename std::remove_reference<T>::type>::type,
std::string>::value, std::string>
get_or(basic_value<C, M, V>&& v, T&& opt) get_or(basic_value<C, M, V>&& v, T&& opt)
{ {
try try
{ {
return std::move(v.as_string().str); return std::move(v.template cast<value_t::string>().str);
} }
catch(...) catch(...)
{ {
return std::string(std::forward<T>(opt)); return std::forward<T>(opt);
} }
} }
@@ -854,11 +558,11 @@ get_or(const basic_value<C, M, V>& v, T&& opt)
{ {
try try
{ {
return std::move(v.as_string().str); return std::move(v.template cast<value_t::string>().str);
} }
catch(...) catch(...)
{ {
return std::string(std::forward<T>(opt)); return std::string(opt);
} }
} }
@@ -868,199 +572,24 @@ get_or(const basic_value<C, M, V>& v, T&& opt)
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_exact_toml_type<detail::remove_cvref_t<T>, detail::negation<detail::is_exact_toml_type<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
basic_value<C, M, V>>>, basic_value<C, M, V>>>,
detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>, detail::negation<std::is_same<std::string,
typename std::remove_cv<typename std::remove_reference<T>::type>::type>>,
detail::negation<detail::is_string_literal< detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>> typename std::remove_reference<T>::type>>
>::value, detail::remove_cvref_t<T>> >::value, typename std::remove_reference<T>::type>
get_or(const basic_value<C, M, V>& v, T&& opt) get_or(const basic_value<C, M, V>& v, T&& opt)
{ {
try try
{ {
return get<detail::remove_cvref_t<T>>(v); return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
} }
catch(...) catch(...)
{ {
return detail::remove_cvref_t<T>(std::forward<T>(opt)); return std::forward<T>(opt);
}
}
// ===========================================================================
// find_or(value, key, fallback)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find_or(const basic_value<C, M, V>& v, const key& ky,
const basic_value<C, M, V>& 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<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>&
find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = v.as_table();
if(tab.count(ky) == 0) {return opt;}
return tab.at(ky);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
{
if(!v.is_table()) {return opt;}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return opt;}
return basic_value<C, M, V>(std::move(tab.at(ky)));
}
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
find_or(const basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
find_or(basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_exact_toml_type<T, basic_value<C, M, V>>::value,
detail::remove_cvref_t<T>>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
find_or(const basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
find_or(basic_value<C, M, V>& 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<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
auto tab = std::move(v).as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::string>
find_or(const basic_value<C, M, V>& 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<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
// T is not an exact toml type
detail::negation<detail::is_exact_toml_type<
detail::remove_cvref_t<T>, basic_value<C, M, V>>>,
// T is not std::string
detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>,
// T is not a string literal
detail::negation<detail::is_string_literal<
typename std::remove_reference<T>::type>>
>::value, detail::remove_cvref_t<T>>
find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return std::forward<T>(opt);}
const auto& tab = v.as_table();
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ============================================================================
// expect
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
{
try
{
return ok(get<T>(v));
}
catch(const std::exception& e)
{
return err(e.what());
}
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
result<T, std::string>
expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
{
try
{
return ok(find<T>(v, k));
}
catch(const std::exception& e)
{
return err(e.what());
} }
} }

View File

@@ -2,13 +2,12 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_LEXER_HPP #ifndef TOML11_LEXER_HPP
#define TOML11_LEXER_HPP #define TOML11_LEXER_HPP
#include "combinator.hpp"
#include <stdexcept>
#include <istream> #include <istream>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <fstream> #include <fstream>
#include "combinator.hpp"
namespace toml namespace toml
{ {
namespace detail namespace detail
@@ -64,15 +63,12 @@ using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>; using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>; using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>; using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>, lex_dec_int>;
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit, using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
sequence<lex_underscore, lex_digit>>, unlimited>>; sequence<lex_underscore, lex_digit>>, unlimited>>;
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>; using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>;
using lex_float = either<lex_special_float, using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part, sequence<lex_dec_int, either<lex_exponent_part,
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>; sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
@@ -123,7 +119,6 @@ using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
in_range<0x0a, 0x1F>, // is allowed in_range<0x0a, 0x1F>, // is allowed
character<0x22>, character<0x5C>, character<0x22>, character<0x5C>,
character<0x7F>>>; character<0x7F>>>;
using lex_escape = character<'\\'>; using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>, using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>; repeat<lex_hex_dig, exactly<4>>>;
@@ -142,46 +137,11 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>, repeat<lex_basic_char, unlimited>,
lex_quotation_mark>; 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
// splitted 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<lex_quotation_mark, exactly<3>>; using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab in_range<0x0a, 0x1F>, // is tab
character<0x5C>, // backslash character<0x5C>,
character<0x7F>, // DEL character<0x7F>,
lex_ml_basic_string_delim>>; lex_ml_basic_string_delim>>;
using lex_ml_basic_escaped_newline = sequence< using lex_ml_basic_escaped_newline = sequence<
@@ -192,9 +152,9 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline, using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>, lex_ml_basic_escaped_newline>,
unlimited>; unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open, using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
lex_ml_basic_body, lex_ml_basic_body,
lex_ml_basic_string_close>; lex_ml_basic_string_delim>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>; in_range<0x10, 0x19>, character<0x27>>>;
@@ -203,13 +163,7 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>, repeat<lex_literal_char, unlimited>,
lex_apostrophe>; lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>; using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>, in_range<0x10, 0x1F>,
@@ -217,9 +171,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>; lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>; unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open, using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
lex_ml_literal_body, lex_ml_literal_body,
lex_ml_literal_string_close>; lex_ml_literal_string_delim>;
using lex_string = either<lex_ml_basic_string, lex_basic_string, using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>; lex_ml_literal_string, lex_literal_string>;

View File

@@ -11,12 +11,12 @@ inline namespace literals
inline namespace toml_literals inline namespace toml_literals
{ {
// implementation inline ::toml::value operator"" _toml(const char* str, std::size_t len)
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{ {
using value_type = ::toml::basic_value< ::toml::detail::location<std::vector<char>>
::toml::discard_comments, std::unordered_map, std::vector>; loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
/* contents = */ std::vector<char>(str, str + len));
// if there are some comments or empty lines, skip them. // if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence< using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>, ::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -53,7 +53,7 @@ literal_internal_impl(::toml::detail::location loc)
// If it is neither a table-key or a array-of-table-key, it may be a value. // 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(!is_table_key && !is_aots_key)
{ {
if(auto data = ::toml::detail::parse_value<value_type>(loc)) if(auto data = ::toml::detail::parse_value<::toml::value>(loc))
{ {
return data.unwrap(); return data.unwrap();
} }
@@ -70,42 +70,16 @@ literal_internal_impl(::toml::detail::location loc)
// It is a valid toml file. // It is a valid toml file.
// It should be parsed as if we parse a file with this content. // It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc)) if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc))
{ {
return data.unwrap(); return data.unwrap();
} }
else // none of them. else // none of them.
{ {
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); throw ::toml::syntax_error(data.unwrap_err());
} }
} }
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
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<::toml::discard_comments, std::unordered_map, std::vector>
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<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals } // toml_literals
} // literals } // literals
} // toml } // toml

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_REGION_HPP #ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP #define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
@@ -9,7 +10,6 @@
#include <iterator> #include <iterator>
#include <iomanip> #include <iomanip>
#include <cassert> #include <cassert>
#include "color.hpp"
namespace toml namespace toml
{ {
@@ -41,7 +41,6 @@ struct region_base
region_base& operator=(region_base&& ) = default; region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;} 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 str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");} virtual std::string name() const {return std::string("unknown file");}
@@ -55,7 +54,7 @@ struct region_base
// number of characters in the line after the region // number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;}
virtual std::vector<std::string> comments() const {return {};} virtual std::vector<std::string> comments()const {return {};}
// ```toml // ```toml
// # comment_before // # comment_before
// key = "value" # comment_inline // key = "value" # comment_inline
@@ -67,21 +66,22 @@ struct region_base
// //
// it contains pointer to the file content and iterator that points the current // it contains pointer to the file content and iterator that points the current
// location. // location.
template<typename Container>
struct location final : public region_base struct location final : public region_base
{ {
using const_iterator = typename std::vector<char>::const_iterator; using const_iterator = typename Container::const_iterator;
using difference_type = typename const_iterator::difference_type; using difference_type = typename const_iterator::difference_type;
using source_ptr = std::shared_ptr<const std::vector<char>>; using source_ptr = std::shared_ptr<const Container>;
location(std::string name, std::vector<char> cont) static_assert(std::is_same<char, typename Container::value_type>::value,"");
: source_(std::make_shared<std::vector<char>>(std::move(cont))), static_assert(std::is_same<std::random_access_iterator_tag,
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin()) typename std::iterator_traits<const_iterator>::iterator_category>::value,
{} "container should be randomly accessible");
location(std::string name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default; location(const location&) = default;
location(location&&) = default; location(location&&) = default;
location& operator=(const location&) = default; location& operator=(const location&) = default;
@@ -89,7 +89,6 @@ struct location final : public region_base
~location() = default; ~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);} bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`. // this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;} const const_iterator iter() const noexcept {return iter_;}
@@ -194,27 +193,33 @@ struct location final : public region_base
// //
// it contains pointer to the file content and iterator that points the first // it contains pointer to the file content and iterator that points the first
// and last location. // and last location.
template<typename Container>
struct region final : public region_base struct region final : public region_base
{ {
using const_iterator = typename std::vector<char>::const_iterator; using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const std::vector<char>>; using source_ptr = std::shared_ptr<const Container>;
static_assert(std::is_same<char, typename Container::value_type>::value,"");
static_assert(std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"container should be randomly accessible");
// delete default constructor. source_ never be null. // delete default constructor. source_ never be null.
region() = delete; region() = delete;
explicit region(const location& loc) region(const location<Container>& loc)
: source_(loc.source()), source_name_(loc.name()), : source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter()) first_(loc.iter()), last_(loc.iter())
{} {}
explicit region(location&& loc) region(location<Container>&& loc)
: source_(loc.source()), source_name_(loc.name()), : source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter()) first_(loc.iter()), last_(loc.iter())
{} {}
region(const location& loc, const_iterator f, const_iterator l) region(const location<Container>& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{} {}
region(location&& loc, const_iterator f, const_iterator l) region(location<Container>&& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{} {}
@@ -226,16 +231,16 @@ struct region final : public region_base
region& operator+=(const region& other) region& operator+=(const region& other)
{ {
// different regions cannot be concatenated if(this->begin() != other.begin() || this->end() != other.end() ||
assert(this->begin() == other.begin() && this->end() == other.end() && this->last_ != other.first_)
this->last_ == other.first_); {
throw internal_error("invalid region concatenation");
}
this->last_ = other.last_; this->last_ = other.last_;
return *this; return *this;
} }
bool is_ok() const noexcept override {return static_cast<bool>(source_);} bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);} std::string str() const override {return make_string(first_, last_);}
std::string line() const override std::string line() const override
@@ -344,7 +349,7 @@ struct region final : public region_base
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter); auto str = make_string(std::next(comment_found), iter);
if(!str.empty() && str.back() == '\r') {str.pop_back();} if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(str));
} }
else else
@@ -397,7 +402,7 @@ struct region final : public region_base
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), this->line_end()); auto str = make_string(std::next(comment_found), this->line_end());
if(!str.empty() && str.back() == '\r') {str.pop_back();} if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(str));
} }
} }
@@ -412,6 +417,78 @@ struct region final : public region_base
const_iterator first_, last_; const_iterator first_, last_;
}; };
// to show a better error message.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<region_base const*, std::string>>& reg_com,
const std::vector<std::string>& helps = {})
{
assert(!reg_com.empty());
const auto line_num_width = static_cast<int>(std::max_element(
reg_com.begin(), reg_com.end(),
[](std::pair<region_base const*, std::string> const& lhs,
std::pair<region_base const*, std::string> const& rhs)
{
return lhs.first->line_num().size() < rhs.first->line_num().size();
}
)->first->line_num().size());
std::ostringstream retval;
retval << message << '\n';
for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
{
// if the filenames are the same, print "..."
if(iter != reg_com.begin() &&
std::prev(iter)->first->name() == iter->first->name())
{
retval << "\n ...\n";
}
else // if filename differs, print " --> filename.toml"
{
if(iter != reg_com.begin()) {retval << '\n';}
retval << " --> " << iter->first->name() << '\n';
}
const region_base* const reg = iter->first;
const std::string& comment = iter->second;
retval << ' ' << std::setw(line_num_width) << reg->line_num();
retval << " | " << reg->line() << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << " | " << make_string(reg->before(), ' ');
if(reg->size() == 1)
{
// invalid
// ^------
retval << '^';
retval << make_string(reg->after(), '-');
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = std::min(reg->size(), reg->line().size());
retval << make_string(underline_len, '~');
}
retval << ' ';
retval << comment;
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << " | ";
for(const auto help : helps)
{
retval << "\nHint: ";
retval << help;
}
}
return retval.str();
}
} // detail } // detail
} // toml } // toml
#endif// TOML11_REGION_H #endif// TOML11_REGION_H

View File

@@ -2,12 +2,10 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP #ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP
#include <cstdio>
#include <limits>
#include "lexer.hpp"
#include "value.hpp" #include "value.hpp"
#include "lexer.hpp"
#include <limits>
#include <cstdio>
namespace toml namespace toml
{ {
@@ -24,62 +22,39 @@ namespace toml
// Since toml restricts characters available in a bare key, generally a string // 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 // should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring. // a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc> inline std::string format_key(const toml::key& key)
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key)
{ {
// check the key can be a bare (unquoted) key detail::location<toml::key> loc(key, key);
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
detail::lex_unquoted_key::invoke(loc); detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end()) if(loc.iter() == loc.end())
{ {
return key; // all the tokens are consumed. the key is unquoted-key. return key; // all the tokens are consumed. the key is unquoted-key.
} }
std::string token("\"");
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : key) for(const char c : key)
{ {
switch(c) switch(c)
{ {
case '\\': {serialized += "\\\\"; break;} case '\\': {token += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;} case '\"': {token += "\\\""; break;}
case '\b': {serialized += "\\b"; break;} case '\b': {token += "\\b"; break;}
case '\t': {serialized += "\\t"; break;} case '\t': {token += "\\t"; break;}
case '\f': {serialized += "\\f"; break;} case '\f': {token += "\\f"; break;}
case '\n': {serialized += "\\n"; break;} case '\n': {token += "\\n"; break;}
case '\r': {serialized += "\\r"; break;} case '\r': {token += "\\r"; break;}
default : {serialized += c; break;} default : {token += c; break;}
} }
} }
serialized += "\""; token += "\"";
return serialized; return token;
} }
template<typename charT, typename traits, typename Alloc> template<typename Comment,
std::basic_string<charT, traits, Alloc> template<typename ...> class Table,
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys) template<typename ...> class Array>
{
std::basic_string<charT, traits, Alloc> serialized;
if(keys.empty()) {return serialized;}
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer struct serializer
{ {
static_assert(detail::is_basic_value<Value>::value, using value_type = basic_value<Comment, Table, Array>;
"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 key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ; using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ; using boolean_type = typename value_type::boolean_type ;
@@ -97,10 +72,8 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10, const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false, const bool can_be_inlined = false,
const bool no_comment = false, const bool no_comment = false,
std::vector<toml::key> ks = {}, std::vector<toml::key> ks = {})
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment), : 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)) float_prec_(float_prec), width_(w), keys_(std::move(ks))
{} {}
~serializer() = default; ~serializer() = default;
@@ -122,7 +95,7 @@ struct serializer
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
std::string token(buf.begin(), std::prev(buf.end())); std::string token(buf.begin(), std::prev(buf.end()));
if(!token.empty() && token.back() == '.') // 1. => 1.0 if(token.back() == '.') // 1. => 1.0
{ {
token += '0'; token += '0';
} }
@@ -139,6 +112,29 @@ struct serializer
{ {
// the resulting value does not have any float specific part! // the resulting value does not have any float specific part!
token += ".0"; token += ".0";
return token;
}
if(!has_exponent)
{
return token; // there is no exponent part. just return it.
}
// zero-prefix in an exponent is NOT allowed in TOML.
// remove it if it exists.
bool sign_exists = false;
std::size_t zero_prefix = 0;
for(auto iter = std::next(e), iend = token.cend(); iter != iend; ++iter)
{
if(*iter == '+' || *iter == '-'){sign_exists = true; continue;}
if(*iter == '0'){zero_prefix += 1;}
else {break;}
}
if(zero_prefix != 0)
{
const auto offset = std::distance(token.cbegin(), e) +
(sign_exists ? 2 : 1);
token.erase(static_cast<typename std::string::size_type>(offset),
zero_prefix);
} }
return token; return token;
} }
@@ -146,23 +142,12 @@ struct serializer
{ {
if(s.kind == string_t::basic) if(s.kind == string_t::basic)
{ {
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
{ {
// if linefeed or double-quote is contained, // if linefeed is contained, make it multiline-string.
// make it multiline basic string. const std::string open("\"\"\"\n");
const auto escaped = this->escape_ml_basic_string(s.str); const std::string close("\\\n\"\"\"");
std::string open("\"\"\""); return open + this->escape_ml_basic_string(s.str) + close;
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. // no linefeed. try to make it oneline-string.
@@ -203,11 +188,7 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || 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::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{ {
std::string open("'''"); const std::string open("'''\n");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''"); const std::string close("'''");
return open + s.str + close; return open + s.str + close;
} }
@@ -246,18 +227,92 @@ struct serializer
std::string operator()(const array_type& v) const std::string operator()(const array_type& v) const
{ {
if(!v.empty() && v.front().is_table())// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined,
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += this->serialize_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;
}
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
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())
{
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)
{
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += "[[";
token += this->serialize_dotted_key(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
if(v.empty()) if(v.empty())
{ {
return std::string("[]"); return std::string("[]");
} }
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array. // not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment. // first, try to make it inline if none of the elements have a comment.
if( ! this->has_comment_inside(v)) if(!this->has_comment_inside(v))
{ {
const auto inl = this->make_inline_array(v); const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ && if(inl.size() < this->width_ &&
@@ -278,7 +333,7 @@ struct serializer
token += "[\n"; token += "[\n";
for(const auto& item : v) for(const auto& item : v)
{ {
if( ! item.comments().empty() && !no_comment_) if(!item.comments().empty() && !no_comment_)
{ {
// if comment exists, the element must be the only element in the line. // if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed. // e.g. the following is not allowed.
@@ -304,7 +359,7 @@ struct serializer
token += '\n'; token += '\n';
} }
token += toml::visit(*this, item); token += toml::visit(*this, item);
if(!token.empty() && token.back() == '\n') {token.pop_back();} if(token.back() == '\n') {token.pop_back();}
token += ",\n"; token += ",\n";
continue; continue;
} }
@@ -312,7 +367,7 @@ struct serializer
next_elem += toml::visit(*this, item); next_elem += toml::visit(*this, item);
// comma before newline. // comma before newline.
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} if(next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue. // if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_) if(current_line.size() + next_elem.size() + 1 < this->width_)
@@ -339,10 +394,7 @@ struct serializer
} }
if(!current_line.empty()) if(!current_line.empty())
{ {
if(!current_line.empty() && current_line.back() != '\n') if(current_line.back() != '\n') {current_line += '\n';}
{
current_line += '\n';
}
token += current_line; token += current_line;
} }
token += "]\n"; token += "]\n";
@@ -359,7 +411,7 @@ struct serializer
std::string token; std::string token;
if(!this->keys_.empty()) if(!this->keys_.empty())
{ {
token += format_key(this->keys_.back()); token += this->serialize_key(this->keys_.back());
token += " = "; token += " = ";
} }
token += this->make_inline_table(v); token += this->make_inline_table(v);
@@ -374,7 +426,7 @@ struct serializer
if(!keys_.empty()) if(!keys_.empty())
{ {
token += '['; token += '[';
token += format_keys(keys_); token += this->serialize_dotted_key(keys_);
token += "]\n"; token += "]\n";
} }
token += this->make_multiline_table(v); token += this->make_multiline_table(v);
@@ -383,6 +435,25 @@ struct serializer
private: private:
std::string serialize_key(const toml::key& key) const
{
return ::toml::format_key(key);
}
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
{
std::string token;
if(keys.empty()){return token;}
for(const auto& k : keys)
{
token += this->serialize_key(k);
token += '.';
}
token.erase(token.size() - 1, 1); // remove trailing `.`
return token;
}
std::string escape_basic_string(const std::string& s) const std::string escape_basic_string(const std::string& s) const
{ {
//XXX assuming `s` is a valid utf-8 sequence. //XXX assuming `s` is a valid utf-8 sequence.
@@ -412,9 +483,7 @@ struct serializer
switch(*i) switch(*i)
{ {
case '\\': {retval += "\\\\"; break;} case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed. case '\"': {retval += "\\\""; break;}
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;} case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;} case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;} case '\f': {retval += "\\f"; break;}
@@ -435,23 +504,6 @@ struct serializer
default: {retval += *i; break;} default: {retval += *i; break;}
} }
} }
// 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; return retval;
} }
@@ -488,10 +540,8 @@ struct serializer
for(const auto& item : v) for(const auto& item : v)
{ {
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer( token += visit(serializer(std::numeric_limits<std::size_t>::max(),
(std::numeric_limits<std::size_t>::max)(), this->float_prec_, this->float_prec_, true), item);
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
} }
token += ']'; token += ']';
return token; return token;
@@ -508,12 +558,10 @@ struct serializer
{ {
// in inline tables, trailing comma is not allowed (toml-lang #569). // in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first); token += this->serialize_key(kv.first);
token += '='; token += '=';
token += visit(serializer( token += visit(serializer(std::numeric_limits<std::size_t>::max(),
(std::numeric_limits<std::size_t>::max)(), this->float_prec_, this->float_prec_, true), kv.second);
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
} }
token += '}'; token += '}';
return token; return token;
@@ -523,33 +571,30 @@ struct serializer
{ {
std::string token; std::string token;
// print non-table elements first. // print non-table stuff first. because after printing [foo.bar], the
// ```toml // remaining non-table values will be assigned into [foo.bar], not [foo]
// [foo] # a table we're writing now here for(const auto kv : v)
// 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)) if(kv.second.is_table() || is_array_of_tables(kv.second))
{ {
continue; continue;
} }
token += write_comments(kv.second); if(!kv.second.comments().empty() && !no_comment_)
{
const auto key_and_sep = format_key(kv.first) + " = "; for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ? const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0; this->width_ - key_and_sep.size() : 0;
token += key_and_sep; token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, token += visit(serializer(residual_width, this->float_prec_, true),
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, kv.second);
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n') if(token.back() != '\n')
{ {
token += '\n'; token += '\n';
@@ -575,8 +620,8 @@ struct serializer
ks.push_back(kv.first); ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_, auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks, !multiline_table_printed, this->no_comment_, ks),
/*has_comment*/ !kv.second.comments().empty()), kv.second); kv.second);
if((!multiline_table_printed) && if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
@@ -589,142 +634,31 @@ struct serializer
tmp += '\n'; tmp += '\n';
} }
token += write_comments(kv.second); if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += tmp; token += tmp;
} }
return token; 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 bool is_array_of_tables(const value_type& v) const
{ {
if(!v.is_array() || v.as_array().empty()) {return false;} if(!v.is_array()) {return false;}
return is_array_of_tables(v.as_array()); const auto& a = v.as_array();
} return !a.empty() && a.front().is_table();
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: private:
bool can_be_inlined_; bool can_be_inlined_;
bool no_comment_; bool no_comment_;
bool value_has_comment_;
int float_prec_; int float_prec_;
std::size_t width_; std::size_t width_;
std::vector<toml::key> keys_; std::vector<toml::key> keys_;
@@ -737,7 +671,6 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10, int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false) bool no_comment = false, bool force_inline = false)
{ {
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object. // if value is a table, it is considered to be a root object.
// the root object can't be an inline table. // the root object can't be an inline table.
if(v.is_table()) if(v.is_table())
@@ -748,11 +681,10 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments(); oss << v.comments();
oss << '\n'; // to split the file comment from the first element oss << '\n'; // to split the file comment from the first element
} }
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v); oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v);
oss << serialized;
return oss.str(); return oss.str();
} }
return visit(serializer<value_type>(w, fprec, force_inline), v); return visit(serializer<C, M, V>(w, fprec, force_inline), v);
} }
namespace detail namespace detail
@@ -788,8 +720,6 @@ template<typename charT, typename traits, typename C,
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v) operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{ {
using value_type = basic_value<C, M, V>;
// get status of std::setw(). // get status of std::setw().
const auto w = static_cast<std::size_t>(os.width()); const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision()); const int fprec = static_cast<int>(os.precision());
@@ -805,8 +735,7 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
os << '\n'; // to split the file comment from the first element os << '\n'; // to split the file comment from the first element
} }
// the root object can't be an inline table. so pass `false`. // the root object can't be an inline table. so pass `false`.
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v); os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v);
os << serialized;
// if v is a non-table value, and has only one comment, then // if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way. // put a comment just after a value. in the following way.

View File

@@ -2,10 +2,8 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP
#include <cstdint>
#include <sstream>
#include "region.hpp" #include "region.hpp"
#include <cstdint>
namespace toml namespace toml
{ {
@@ -40,21 +38,17 @@ struct source_location
public: public:
source_location() source_location()
: line_num_(1), column_num_(1), region_size_(1), : line_num_(0), column_num_(0), region_size_(0),
file_name_("unknown file"), line_str_("") file_name_("unknown file"), line_str_("")
{} {}
explicit source_location(const detail::region_base* reg) explicit source_location(const detail::region_base* reg)
: line_num_(1), column_num_(1), region_size_(1), : line_num_(0), column_num_(0), region_size_(0),
file_name_("unknown file"), line_str_("") file_name_("unknown file"), line_str_("")
{ {
if(reg) if(reg)
{ {
if(reg->line_num() != detail::region_base().line_num()) line_num_ = static_cast<std::uint_least32_t>(std::stoul(reg->line_num()));
{
line_num_ = static_cast<std::uint_least32_t>(
std::stoul(reg->line_num()));
}
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1); column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
region_size_ = static_cast<std::uint_least32_t>(reg->size()); region_size_ = static_cast<std::uint_least32_t>(reg->size());
file_name_ = reg->name(); file_name_ = reg->name();
@@ -62,21 +56,6 @@ struct source_location
} }
} }
explicit source_location(const detail::region& reg)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(reg.size())),
file_name_(reg.name()),
line_str_ (reg.line())
{}
explicit source_location(const detail::location& loc)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(loc.size())),
file_name_(loc.name()),
line_str_ (loc.line())
{}
~source_location() = default; ~source_location() = default;
source_location(source_location const&) = default; source_location(source_location const&) = default;
source_location(source_location &&) = default; source_location(source_location &&) = default;
@@ -99,135 +78,5 @@ struct source_location
std::string line_str_; std::string line_str_;
}; };
namespace detail
{
// internal error message generation.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<source_location, std::string>>& loc_com,
const std::vector<std::string>& 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<std::size_t>(line_num_width, 1);
std::ostringstream retval;
if(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 << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< 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<int>(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<std::size_t>(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<loc_com.size(); ++i)
{
const auto& prev = loc_com.at(i-1);
const auto& curr = loc_com.at(i);
retval << '\n';
// if the filenames are the same, print "..."
if(prev.first.file_name() == curr.first.file_name())
{
retval << color::bold << color::blue << " ...\n" << color::reset;
}
else // if filename differs, print " --> 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 } // toml
#endif// TOML11_SOURCE_LOCATION_HPP #endif// TOML11_SOURCE_LOCATION_HPP

View File

@@ -16,9 +16,10 @@ struct storage
{ {
using value_type = T; using value_type = T;
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {} storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {} storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default; ~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {} storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs) storage& operator=(const storage& rhs)
{ {

View File

@@ -2,11 +2,8 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_STRING_HPP #ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP #define TOML11_STRING_HPP
#include <cstdint>
#include <algorithm>
#include <string> #include <string>
#include <cstdint>
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#include <string_view> #include <string_view>
@@ -48,11 +45,6 @@ struct string
operator std::string const& () const& noexcept {return str;} operator std::string const& () const& noexcept {return str;}
operator std::string&& () && noexcept {return std::move(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 __cplusplus >= 201703L #if __cplusplus >= 201703L
explicit string(std::string_view s): kind(string_t::basic), str(s){} 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(std::string_view s, string_t k): kind(k), str(s){}
@@ -62,8 +54,6 @@ struct string
explicit operator std::string_view() const noexcept explicit operator std::string_view() const noexcept
{return std::string_view(str);} {return std::string_view(str);}
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
#endif #endif
string_t kind; string_t kind;

View File

@@ -2,13 +2,11 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP #ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <chrono>
#include <tuple>
#include <string>
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#include <string_view> #include <string_view>
@@ -45,23 +43,17 @@ struct has_mapped_type_impl
template<typename T> static std::true_type check(typename T::mapped_type*); template<typename T> static std::true_type check(typename T::mapped_type*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
}; };
struct has_reserve_method_impl struct has_resize_method_impl
{ {
constexpr static std::size_t dummy=0;
template<typename T> static std::true_type check(decltype(std::declval<T>().resize(dummy))*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
};
struct has_push_back_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
}; };
struct is_comparable_impl struct is_comparable_impl
{ {
template<typename T> static std::true_type check(decltype(std::declval<T>() < std::declval<T>())*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>() < std::declval<T>())*);
}; };
struct has_from_toml_method_impl struct has_from_toml_method_impl
@@ -99,9 +91,7 @@ struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){}; struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){}; struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){}; struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
@@ -115,20 +105,12 @@ struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){}; : decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
#undef decltype #undef decltype(...)
#endif #endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++17 and/or/not // C++17 and/or/not
#if __cplusplus >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{}; template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{}; template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts> template<typename T, typename ... Ts>
@@ -146,8 +128,6 @@ struct disjunction<T, Ts...> :
template<typename T> template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{}; struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// type checkers // type checkers
@@ -159,10 +139,6 @@ template<typename T> struct is_std_tuple : std::false_type{};
template<typename ... Ts> template<typename ... Ts>
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{}; struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
template<typename T> struct is_std_forward_list : std::false_type{};
template<typename T>
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
template<typename T> struct is_chrono_duration: std::false_type{}; template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period> template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{}; struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
@@ -206,13 +182,6 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 index_sequence // C++14 index_sequence
#if __cplusplus >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{}; template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{}; template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -236,22 +205,11 @@ struct index_sequence_maker<0>
template<std::size_t N> template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type; using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // __cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 enable_if_t // C++14 enable_if_t
#if __cplusplus >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T> template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type; using enable_if_t = typename std::enable_if<B, T>::type;
#endif // __cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// return_type_of_t // return_type_of_t
@@ -282,19 +240,6 @@ disjunction<
> >
>{}; >{};
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
template<typename T>
struct remove_cvref
{
using type = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
}// detail }// detail
}//toml }//toml
#endif // TOML_TRAITS #endif // TOML_TRAITS

View File

@@ -2,13 +2,12 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TYPES_HPP #ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP #define TOML11_TYPES_HPP
#include <unordered_map>
#include <vector>
#include "comments.hpp"
#include "datetime.hpp" #include "datetime.hpp"
#include "string.hpp" #include "string.hpp"
#include "traits.hpp" #include "traits.hpp"
#include "comments.hpp"
#include <vector>
#include <unordered_map>
namespace toml namespace toml
{ {

View File

@@ -2,11 +2,10 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_UTILITY_HPP #ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP #define TOML11_UTILITY_HPP
#include "traits.hpp"
#include <utility>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <utility>
#include "traits.hpp"
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
@@ -21,41 +20,38 @@
namespace toml namespace toml
{ {
#if __cplusplus >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts> template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args) inline std::unique_ptr<T> make_unique(Ts&& ... args)
{ {
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
} }
#endif // __cplusplus >= 2014
namespace detail namespace detail
{ {
template<typename Container>
void try_reserve_impl(Container& container, std::size_t N, std::true_type) template<typename T>
inline void resize_impl(T& container, std::size_t N, std::true_type)
{ {
container.reserve(N); container.resize(N);
return; return ;
} }
template<typename Container>
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept template<typename T>
inline void resize_impl(T& container, std::size_t N, std::false_type)
{ {
return; if(container.size() >= N) {return;}
throw std::invalid_argument("not resizable type");
} }
} // detail } // detail
template<typename Container> template<typename T>
void try_reserve(Container& container, std::size_t N) inline void resize(T& container, std::size_t N)
{ {
if(N <= container.size()) {return;} if(container.size() == N) {return;}
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
return; return detail::resize_impl(container, N, detail::has_resize_method<T>());
} }
namespace detail namespace detail
@@ -80,10 +76,10 @@ std::string concat_to_string(Ts&& ... args)
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...); return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
} }
template<typename T> template<typename T, typename U>
T from_string(const std::string& str, T opt) T from_string(const std::string& str, U&& opt)
{ {
T v(opt); T v(static_cast<T>(std::forward<U>(opt)));
std::istringstream iss(str); std::istringstream iss(str);
iss >> v; iss >> v;
return v; return v;

File diff suppressed because it is too large Load Diff