Skip to content

Commit

Permalink
Feature add date time datatype (#23)
Browse files Browse the repository at this point in the history
closes #20 

* Added a date_time type

* Refactoring

* Added date time to TValue. Added more tests. Updated tests to new value type.
  • Loading branch information
fuersten authored Mar 29, 2023
1 parent 4bde9a6 commit 1a15997
Show file tree
Hide file tree
Showing 30 changed files with 437 additions and 120 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ option(BUILD_WITH_DOCS "Build documentation" ${REXSAPI_MASTER_PROJECT})

include(cmake/create_docs.cmake)
include(cmake/fetch_cli11.cmake)
include(cmake/fetch_date.cmake)
include(cmake/fetch_fmt.cmake)
include(cmake/fetch_json.cmake)
include(cmake/fetch_miniz.cmake)
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
![Supported Platforms](https://img.shields.io/badge/platforms-Linux%20%7C%20Windows%20%7C%20Mac-blue.svg)
![License: Apache 2](https://img.shields.io/badge/license-Apache%202-blue)
![Language: C++17](https://img.shields.io/badge/language-C%2B%2B17-blue.svg)
![Version:](https://img.shields.io/badge/version-1.0.0-green)
![Version:](https://img.shields.io/badge/version-1.1.0-green)
[![GitHub Build Status](https://github.com/fva-net/rexs-api-cpp/workflows/CMake%20Build%20Matrix/badge.svg)](https://github.com/fva-net/rexs-api-cpp/actions)
[![Coverage Status](https://coveralls.io/repos/github/BearinxSimulationSuite/REXSapi/badge.svg?branch=main)](https://coveralls.io/github/BearinxSimulationSuite/REXSapi?branch=main)

Expand All @@ -11,11 +11,11 @@ The REXSapi library is a C++ implementation of the [REXS specification](https://

# Status

The project is now in beta phase and the API should be stable. You can checkout the library and play with it. Probably still a bit early for production use, though.
The project is now released and is already used in production. However, the API will probably still change a bit due to the adaption of the 1.5 standards version.

# Supported REXS Versions

The library uses REXS database model files in order to validate REXS model files. Database model files can be downloaded from the [REXS database page](https://database.rexs.info/). Currently, the implementation supports versions 1.0 to 1.4, but newer database files should also work. Version 1.0 to 1.4 database model files in english and german can also be found in the models directory of this project.
The library uses REXS database model files in order to validate REXS model files. Database model files can be downloaded from the [REXS database page](https://database.rexs.info/). Currently, the implementation supports versions 1.0 to 1.5, but newer database files should also work. Version 1.0 to 1.5 database model files in english and german can also be found in the models directory of this project.

The library supports REXS model files in xml and json format. Compressed REXS zip archives can also be loaded. The loading and storing mechanism can be easily extended to support other sources besides files for model loading and storing.

Expand All @@ -32,7 +32,7 @@ You need a C++17 compatible compiler to use the library.

In order to use REXSapi it is most convinient to just include the `Rexsapi.hxx` header. Mind that you have to include this header along the `REXSAPI_MINIZ_IMPL` define right before the include in *exactly* one compilation unit (cpp file) in order to add the miniz implementation to the project.

```c++
```cpp
#define REXSAPI_MINIZ_IMPL
#include <rexsapi/Rexsapi.hxx>
```
Expand All @@ -41,7 +41,7 @@ In order to use REXSapi it is most convinient to just include the `Rexsapi.hxx`

Loading a REXS model file is straight forward. You need the REXS database model files for the API to validate the model.

```c++
```cpp
const rexsapi::TModelLoader loader{"/path/to/rexs/database/models"};
rexsapi::TResult result;
const std::optional<rexsapi::TModel> model =
Expand All @@ -62,7 +62,7 @@ The `TModelLoader` class can load json and xml REXS model files. If successful,

The Model itself provides methods for accessing every aspect of a model.

```c++
```cpp
const rexsapi::TModel model = loadModel();
for (const auto& relation : model.getRelations()) {
for (const auto& ref : relation.getReferences()) {
Expand All @@ -82,7 +82,7 @@ Alternatively, if the model shall be processed in a complete way, the `TModelVis

The most reliable way to create a REXS model is to use the `TModelBuilder` class. It can create every aspect of a model, be it components, relations, or load spectrum, of a REXS model and highly abstracts the construction of REXS standard compliant models. The `TModelBuilder` needs a specific REXS database model for checking and validating the model. Most model builder methods return a reference to the model builder in order to allow chaining of method calls resulting in dense easy to read code.

```c++
```cpp
const auto registry = rexsapi::createModelRegistry("/path/to/rexs/database/models");
const auto& databaseModel = registry.getModel(rexsapi::TRexsVersion{"1.4"}, "en");
rexsapi::TModelBuilder modelBuilder{databaseModel};
Expand Down Expand Up @@ -111,7 +111,7 @@ If all necessary components have been added, you can start to add relations that
Saving a REXS model to a file is straight forward using the `TModelSaver` convenience class.
```c++
```cpp
const rexsapi::TModel model = createModel();
rexsapi::TResult result;
rexsapi::TModelSaver{}.store(result, model, "/path/to/your/rexs/model/file",
Expand Down Expand Up @@ -278,12 +278,13 @@ The library is header only. A build is only necessary if you want to run the tes
REXSapi uses the following thirdparty open source software

- [cli11 2.3.2](https://github.com/CLIUtils/CLI11)
- [date 3.0.1](https://github.com/HowardHinnant/date)
- [doctest 2.4.10](https://github.com/doctest/doctest)
- [fmt 9.1.0](https://github.com/fmtlib/fmt)
- [nlohmann json 3.11.2](https://github.com/nlohmann/json)
- [miniz 3.0.2](https://github.com/richgel999/miniz)
- [pugixml 1.13](https://github.com/zeux/pugixml)
- [valijson 1.0](https://github.com/tristanpenman/valijson)
- [doctest 2.4.10](https://github.com/doctest/doctest)

# License
REXsapi is licensed under the Apache-2.0 license.
Expand Down
10 changes: 10 additions & 0 deletions cmake/fetch_date.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FetchContent_Declare(
date
URL https://github.com/HowardHinnant/date/archive/refs/tags/v3.0.1.tar.gz
)

FetchContent_GetProperties(date)

if(NOT date_POPULATED)
FetchContent_Populate(date)
endif()
2 changes: 1 addition & 1 deletion include/rexsapi/Exception.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace rexsapi
{
/**
* @brief Exception class for all REXSapi exceptions
* @brief Exception class for all REXS exceptions
*
* There is only one exception type and no exception hierarchie.
*/
Expand Down
3 changes: 3 additions & 0 deletions include/rexsapi/JsonModelSerializer.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ namespace rexsapi
[&j](rexsapi::TFileReferenceTag, const auto& s) -> void {
j = s;
},
[&j](rexsapi::TDatetimeTag, const auto& d) -> void {
j = d.asUTCString();
},
[&j, &attribute](rexsapi::TFloatArrayTag, const auto& a) -> void {
encodeCodedArray(j, attribute.getValue().coded(), a);
},
Expand Down
4 changes: 4 additions & 0 deletions include/rexsapi/JsonSerializer.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace rexsapi
/**
* @brief Outputs a json object into a string
*
* Will add a UTF-8 BOM to the beginning of the buffer.
*
*/
class TJsonStringSerializer
{
Expand Down Expand Up @@ -54,6 +56,8 @@ namespace rexsapi
/**
* @brief Outputs a json object into a file
*
* Will add a UTF-8 BOM to the beginning of the file.
*
*/
class TJsonFileSerializer
{
Expand Down
33 changes: 27 additions & 6 deletions include/rexsapi/JsonValueDecoder.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ namespace rexsapi::detail
const rexsapi::json& node) const override
{
auto value = node.at("string").get<std::string>();
return std::make_pair(TValue{value}, !value.empty() ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
auto empty = value.empty();
return std::make_pair(TValue{std::move(value)}, !empty ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
}
};

Expand All @@ -113,7 +114,8 @@ namespace rexsapi::detail
const rexsapi::json& node) const override
{
auto value = node.at("file_reference").get<std::string>();
return std::make_pair(TValue{value}, !value.empty() ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
auto empty = value.empty();
return std::make_pair(TValue{std::move(value)}, !empty ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
}
};

Expand Down Expand Up @@ -206,11 +208,29 @@ namespace rexsapi::detail
auto value = node.at("enum").get<std::string>();

if (enumValue) {
return std::make_pair(TValue{value},
enumValue->check(value) ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
auto check = enumValue->check(value);
return std::make_pair(TValue{std::move(value)}, check ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
}
/// No enum values means this is a custom enum attribute, so accept any text
return std::make_pair(TValue{value}, TDecoderResult::SUCCESS);
return std::make_pair(TValue{std::move(value)}, TDecoderResult::SUCCESS);
}
};

class TDatetimeDecoder : public TJsonDecoder
{
public:
using Type = double;

bool isEmpty(const rexsapi::json& node) const noexcept override
{
return node.at("date_time").is_null();
}

private:
std::pair<TValue, TDecoderResult> onDecode(const std::optional<const database::TEnumValues>&,
const rexsapi::json& node) const override
{
return std::make_pair(TValue{TDatetime{node.at("date_time").get<std::string>()}}, TDecoderResult::SUCCESS);
}
};

Expand Down Expand Up @@ -359,7 +379,7 @@ namespace rexsapi::detail
}
matrix.m_Values.emplace_back(std::move(r));
}
bool result = matrix.validate();
const bool result = matrix.validate();
return std::make_pair(TValue{std::move(matrix)}, result ? TDecoderResult::SUCCESS : TDecoderResult::FAILURE);
}
std::string m_Name;
Expand Down Expand Up @@ -475,6 +495,7 @@ namespace rexsapi::detail
m_Decoder[TValueType::FLOATING_POINT] = std::make_unique<json::TFloatDecoder>();
m_Decoder[TValueType::STRING] = std::make_unique<json::TStringDecoder>();
m_Decoder[TValueType::ENUM] = std::make_unique<json::TEnumDecoder>();
m_Decoder[TValueType::DATE_TIME] = std::make_unique<json::TDatetimeDecoder>();
m_Decoder[TValueType::INTEGER_ARRAY] = std::make_unique<json::TCodedArrayDecoder<int64_t>>("integer_array");
m_Decoder[TValueType::FLOATING_POINT_ARRAY] =
std::make_unique<json::TCodedArrayDecoder<double>>("floating_point_array");
Expand Down
2 changes: 1 addition & 1 deletion include/rexsapi/Model.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ namespace rexsapi
/**
* @brief Represents a REXS model loaded from a file or created by the TModelBuilder
*
* A TModel instance is the main REXSapi object that abstracts a complete REXS model as described by the REXS
* A TModel instance is the main REXS object that abstracts a complete REXS model as described by the REXS
* standard. It can be queried for meta information, components, relations, and load spectrum, drilling down to
* attributes and values using getters from components and relations.
*
Expand Down
2 changes: 1 addition & 1 deletion include/rexsapi/ModelLoader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace rexsapi
* All allowed kinds of REXS model files can be loaded trasparently with this loader. The loader will also create it's
* own model registry. For the successful creation of the model registry, all schema files have to be available to
* the loader. Additionally, all necessary REXS database model files for different versions and languages have to be
* available. The REXSapi project contains the directory ```models``` with all relevant files that can be used with
* available. The REXS project contains the directory ```models``` with all relevant files that can be used with
* the loader.
*
* Allows loading of multiple REXS model files with the same loader.
Expand Down
130 changes: 111 additions & 19 deletions include/rexsapi/Types.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
#ifndef REXSAPI_TYPES_HXX
#define REXSAPI_TYPES_HXX

#include <rexsapi/ConversionHelper.hxx>
#include <rexsapi/Exception.hxx>
#include <rexsapi/Format.hxx>

#include <date/date.h>
#include <vector>

namespace rexsapi
Expand All @@ -29,23 +31,24 @@ namespace rexsapi
*
*/
enum class TValueType : uint8_t {
FLOATING_POINT, //!< floating_point
BOOLEAN, //!< boolean
INTEGER, //!< integer
ENUM, //!< enum
STRING, //!< string
FILE_REFERENCE, //!< file_reference
FLOATING_POINT_ARRAY, //!< floating_point_array
BOOLEAN_ARRAY, //!< boolean_array
INTEGER_ARRAY, //!< integer_array
STRING_ARRAY, //!< string_array
ENUM_ARRAY, //!< enum_array
REFERENCE_COMPONENT, //!< reference_component
FLOATING_POINT_MATRIX, //!< floating_point_matrix
INTEGER_MATRIX, //!< integer_matrix
BOOLEAN_MATRIX, //!< boolean_matrix
STRING_MATRIX, //!< string_matrix
ARRAY_OF_INTEGER_ARRAYS //!< array_of_integer_arrays
FLOATING_POINT, //!< floating_point
BOOLEAN, //!< boolean
INTEGER, //!< integer
ENUM, //!< enum
STRING, //!< string
FILE_REFERENCE, //!< file_reference
FLOATING_POINT_ARRAY, //!< floating_point_array
BOOLEAN_ARRAY, //!< boolean_array
INTEGER_ARRAY, //!< integer_array
STRING_ARRAY, //!< string_array
ENUM_ARRAY, //!< enum_array
REFERENCE_COMPONENT, //!< reference_component
FLOATING_POINT_MATRIX, //!< floating_point_matrix
INTEGER_MATRIX, //!< integer_matrix
BOOLEAN_MATRIX, //!< boolean_matrix
STRING_MATRIX, //!< string_matrix
ARRAY_OF_INTEGER_ARRAYS, //!< array_of_integer_arrays
DATE_TIME //!< date_time
};

/**
Expand Down Expand Up @@ -187,6 +190,94 @@ namespace rexsapi
};


/**
* @brief Represents the REXS date_time type
*
*/
class TDatetime
{
public:
using time_point = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;

/**
* @brief Construct a new TDatetime object from a string
*
* @param datetime The string has to be in ISO8601 format `yyyy-mm-ddThh:mm:ss[+/-]<offset to UTC>`
* @throws std::exception if the string cannot be parsed or the date time is invalid
*/
explicit TDatetime(const std::string& datetime)
{
auto op = date::parse("%FT%T%Ez", m_Timepoint);
std::istringstream in{datetime};
in >> op;

if (!in.good()) {
throw std::runtime_error{"illegal date specified: " + datetime};
}
}

/**
* @brief Construct a new TDatetime object from a std::chrono::time_point
*
* @param datetime A time point
*/
explicit TDatetime(time_point datetime) noexcept
: m_Timepoint{datetime}
{
}

friend bool operator==(const TDatetime& lhs, const TDatetime& rhs) noexcept
{
return lhs.m_Timepoint == rhs.m_Timepoint;
}

/**
* @brief Returns a new TDatetime object constructed with the current date and time
*
* @return A new TDatetime object set to the current date and time
*/
static TDatetime now() noexcept
{
return TDatetime{std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now())};
}

/**
* @brief Returns the UTC string representation in ISO8601 format `yyyy-mm-ddThh:mm:ss[+/-]<offset to UTC>`.
* The offset will always be +00:00.
*
* @return The UTC string representation
*/
inline std::string asUTCString() const
{
return date::format("%FT%T%Ez", m_Timepoint);
}

/**
* @brief Returns the locale string representation in ISO8601 format `yyyy-mm-ddThh:mm:ss[+/-]<offset to UTC>`.
* The offset will be set to the current timezone offset from UTC.
*
* @return The locale string representation
*/
inline std::string asLocaleString() const
{
return getTimeStringISO8601(m_Timepoint);
}

/**
* @brief Returns the time point
*
* @return time_point
*/
inline time_point asTimepoint() const noexcept
{
return m_Timepoint;
}

private:
time_point m_Timepoint;
};


/**
* @brief Represents all currently allowed REXS relation types
*
Expand Down Expand Up @@ -353,8 +444,7 @@ namespace rexsapi
return TValueType::ARRAY_OF_INTEGER_ARRAYS;
}
if (type == "date_time") {
// TODO(LCF): just to make the 1.5 version working
return TValueType::STRING;
return TValueType::DATE_TIME;
}
throw TException{fmt::format("unknown value type '{}'", type)};
}
Expand Down Expand Up @@ -396,6 +486,8 @@ namespace rexsapi
return "string_matrix";
case TValueType::ARRAY_OF_INTEGER_ARRAYS:
return "array_of_integer_arrays";
case TValueType::DATE_TIME:
return "date_time";
}
throw TException{fmt::format("unknown value type '{}'", static_cast<int64_t>(type))};
}
Expand Down
Loading

0 comments on commit 1a15997

Please sign in to comment.