mirror of
https://github.com/pantor/inja.git
synced 2026-04-04 23:28:52 +00:00
add divisibleBy, odd, even functions, add ElementNotation for dot or pointer notation
This commit is contained in:
@@ -75,6 +75,10 @@ Environment env = Environment("../path/templates/");
|
||||
// With global path where to save rendered files
|
||||
Environment env = Environment("../path/templates/", "../path/results/");
|
||||
|
||||
// Choose between JSON pointer or dot notation to access elements
|
||||
env.setElementNotation(inja::ElementNotation::Pointer); // (default) e.g. time/start
|
||||
env.setElementNotation(inja::ElementNotation::Dot); // e.g. time.start
|
||||
|
||||
// With other opening and closing strings (here the defaults, as regex)
|
||||
env.setVariables("\\{\\{", "\\}\\}"); // Variables {{ }}
|
||||
env.setComments("\\{#", "#\\}"); // Comments {# #}
|
||||
@@ -162,6 +166,11 @@ render("I count {{ length(guests) }} guests.", data); // "I count 3 guests."
|
||||
// Round numbers to a given precision
|
||||
render({{ round(3.1415, 0) }}, data) // 3
|
||||
render({{ round(3.1415, 3) }}, data) // 3.142
|
||||
|
||||
// Check if a value is odd, even or divisible by a number
|
||||
render({{ odd(42) }}, data) // false
|
||||
render({{ even(42) }}, data) // true
|
||||
render({{ divisibleBy(42, 7) }}, data) // true
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
66
src/inja.hpp
66
src/inja.hpp
@@ -17,7 +17,9 @@ namespace inja {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
/*!
|
||||
@brief returns the values of a std key-value-map
|
||||
*/
|
||||
template<typename S, typename T>
|
||||
inline std::vector<T> get_values(std::map<S, T> map) {
|
||||
std::vector<T> result;
|
||||
@@ -26,6 +28,25 @@ inline std::vector<T> get_values(std::map<S, T> map) {
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@brief dot notation to json pointer notiation
|
||||
*/
|
||||
inline std::string dot_to_json_pointer_notation(std::string dot) {
|
||||
std::string result = dot;
|
||||
while (result.find(".") != std::string::npos) {
|
||||
result.replace(result.find("."), 1, "/");
|
||||
}
|
||||
result.insert(0, "/");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
enum class ElementNotation {
|
||||
Pointer,
|
||||
Dot
|
||||
};
|
||||
|
||||
|
||||
class Regex: public std::regex {
|
||||
std::string pattern_;
|
||||
|
||||
@@ -239,7 +260,10 @@ public:
|
||||
Lower,
|
||||
Range,
|
||||
Length,
|
||||
Round
|
||||
Round,
|
||||
DivisibleBy,
|
||||
Odd,
|
||||
Even
|
||||
};
|
||||
|
||||
const std::map<Function, Regex> regex_map_functions = {
|
||||
@@ -248,6 +272,9 @@ public:
|
||||
{Function::Range, Regex{"range\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Length, Regex{"length\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Round, Regex{"round\\(\\s*(.*?)\\s*,\\s*(.*?)\\s*\\)"}},
|
||||
{Function::DivisibleBy, Regex{"divisibleBy\\(\\s*(.*?)\\s*,\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Odd, Regex{"odd\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Even, Regex{"even\\(\\s*(.*?)\\s*\\)"}}
|
||||
};
|
||||
|
||||
Parser() { }
|
||||
@@ -367,6 +394,8 @@ public:
|
||||
class Environment {
|
||||
const std::string global_path;
|
||||
|
||||
ElementNotation elementNotation = ElementNotation::Pointer;
|
||||
|
||||
Parser parser;
|
||||
|
||||
public:
|
||||
@@ -389,6 +418,10 @@ public:
|
||||
parser.regex_map_delimiters[Parser::Delimiter::Comment] = Regex{open + "\\s*(.+?)\\s*" + close};
|
||||
}
|
||||
|
||||
void setElementNotation(const ElementNotation elementNotation_) {
|
||||
elementNotation = elementNotation_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
json eval_variable(const std::string& input, json data) {
|
||||
@@ -434,10 +467,37 @@ public:
|
||||
if (not precision.is_number()) { throw std::runtime_error("Argument in round function is not a number."); }
|
||||
return std::round(number.get<double>() * std::pow(10.0, precision.get<int>())) / std::pow(10.0, precision.get<int>());
|
||||
}
|
||||
case Parser::Function::DivisibleBy: {
|
||||
const json number = eval_variable(match_function.str(1), data);
|
||||
const json divisor = eval_variable(match_function.str(2), data);
|
||||
if (not number.is_number()) { throw std::runtime_error("Argument in divisibleBy function is not a number."); }
|
||||
if (not divisor.is_number()) { throw std::runtime_error("Argument in divisibleBy function is not a number."); }
|
||||
return (number.get<int>() % divisor.get<int>() == 0);
|
||||
}
|
||||
case Parser::Function::Odd: {
|
||||
const json number = eval_variable(match_function.str(1), data);
|
||||
if (not number.is_number()) { throw std::runtime_error("Argument in odd function is not a number."); }
|
||||
return (number.get<int>() % 2 != 0);
|
||||
}
|
||||
case Parser::Function::Even: {
|
||||
const json number = eval_variable(match_function.str(1), data);
|
||||
if (not number.is_number()) { throw std::runtime_error("Argument in even function is not a number."); }
|
||||
return (number.get<int>() % 2 == 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::string input_copy = input;
|
||||
if (input_copy[0] != '/') { input_copy.insert(0, "/"); }
|
||||
switch (elementNotation) {
|
||||
case ElementNotation::Pointer: {
|
||||
if (input_copy[0] != '/') { input_copy.insert(0, "/"); }
|
||||
break;
|
||||
}
|
||||
case ElementNotation::Dot: {
|
||||
input_copy = dot_to_json_pointer_notation(input_copy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
json::json_pointer ptr(input_copy);
|
||||
json result = data[ptr];
|
||||
|
||||
|
||||
10
test/src/unit-json-helper.cpp
Normal file
10
test/src/unit-json-helper.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "catch.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "inja.hpp"
|
||||
|
||||
|
||||
|
||||
TEST_CASE("Dot to pointer notation") {
|
||||
CHECK( inja::dot_to_json_pointer_notation("person.names.surname") == "/person/names/surname" );
|
||||
CHECK( inja::dot_to_json_pointer_notation("guests.2") == "/guests/2" );
|
||||
}
|
||||
@@ -132,6 +132,7 @@ lorem ipsum
|
||||
TEST_CASE("Parse variables") {
|
||||
inja::Environment env = inja::Environment();
|
||||
|
||||
|
||||
json data;
|
||||
data["name"] = "Peter";
|
||||
data["city"] = "Washington D.C.";
|
||||
@@ -149,16 +150,26 @@ TEST_CASE("Parse variables") {
|
||||
CHECK( env.eval_variable("[5, 6, 8]", data) == std::vector<int>({5, 6, 8}) );
|
||||
}
|
||||
|
||||
SECTION("Variables from JSON data") {
|
||||
SECTION("Variables from JSON data, dot notation") {
|
||||
env.setElementNotation(inja::ElementNotation::Dot);
|
||||
|
||||
CHECK( env.eval_variable("name", data) == "Peter" );
|
||||
CHECK( env.eval_variable("age", data) == 29 );
|
||||
CHECK( env.eval_variable("names.1", data) == "Seb" );
|
||||
CHECK( env.eval_variable("brother.name", data) == "Chris" );
|
||||
CHECK( env.eval_variable("brother.daughters.0", data) == "Maria" );
|
||||
|
||||
CHECK_THROWS_WITH( env.eval_variable("noelement", data), "JSON pointer found no element." );
|
||||
CHECK_THROWS_WITH( env.eval_variable("&4s-", data), "JSON pointer found no element." );
|
||||
}
|
||||
|
||||
SECTION("Variables from JSON data, pointer notation") {
|
||||
env.setElementNotation(inja::ElementNotation::Pointer);
|
||||
|
||||
CHECK( env.eval_variable("names/1", data) == "Seb" );
|
||||
CHECK( env.eval_variable("brother/name", data) == "Chris" );
|
||||
CHECK( env.eval_variable("brother/daughters/0", data) == "Maria" );
|
||||
CHECK( env.eval_variable("/age", data) == 29 );
|
||||
|
||||
CHECK_THROWS_WITH( env.eval_variable("noelement", data), "JSON pointer found no element." );
|
||||
CHECK_THROWS_WITH( env.eval_variable("&4s-", data), "JSON pointer found no element." );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,4 +256,23 @@ TEST_CASE("Parse functions") {
|
||||
CHECK( env.eval_variable("round(temperature, 2)", data) == 25.68 );
|
||||
CHECK_THROWS_WITH( env.eval_variable("round(name, 2)", data), "Argument in round function is not a number." );
|
||||
}
|
||||
|
||||
SECTION("DivisibleBy") {
|
||||
CHECK( env.eval_variable("divisibleBy(50, 5)", data) == true );
|
||||
CHECK( env.eval_variable("divisibleBy(12, 3)", data) == true );
|
||||
CHECK( env.eval_variable("divisibleBy(11, 3)", data) == false );
|
||||
CHECK_THROWS_WITH( env.eval_variable("divisibleBy(name, 2)", data), "Argument in divisibleBy function is not a number." );
|
||||
}
|
||||
|
||||
SECTION("Odd") {
|
||||
CHECK( env.eval_variable("odd(11)", data) == true );
|
||||
CHECK( env.eval_variable("odd(12)", data) == false );
|
||||
CHECK_THROWS_WITH( env.eval_variable("odd(name)", data), "Argument in odd function is not a number." );
|
||||
}
|
||||
|
||||
SECTION("Even") {
|
||||
CHECK( env.eval_variable("even(11)", data) == false );
|
||||
CHECK( env.eval_variable("even(12)", data) == true );
|
||||
CHECK_THROWS_WITH( env.eval_variable("even(name)", data), "Argument in even function is not a number." );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user