hemplateSimple XML template engine |
git clone git://git.dimitrijedobrota.com/hemplate.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 5a651f07da4b342dd11eabd93352dc7569ebaab8 |
parent | 71dee1ad15475c166cb7e23e7d86e93428ed0ec1 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Fri, 6 Jun 2025 16:54:55 +0200 |
Tooling upgrade and cleanup
M | .clang-format | | | +++++++ - |
M | .clang-tidy | | | +++++++++++++++++++++++++ ------------- |
M | CMakeLists.txt | | | ++ -- |
M | example/html.cpp | | | ++ -- |
M | include/hemplate/attribute.hpp | | | +++++ ----- |
M | include/hemplate/common.hpp | | | + -- |
M | include/hemplate/element.hpp | | | ++++++++++++++++++++++++ --------------------- |
M | test/source/common_test.cpp | | | ++++++++ ----------- |
M | test/source/element_test.cpp | | | ++++++++++++++++++++++++++++++++++++++++++ ---------------------------------------- |
M | vcpkg.json | | | ++ -- |
10 files changed, 132 insertions(+), 113 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -7,7 +7,7 @@
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignEscapedNewlines: Right
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
@@ -20,6 +20,7 @@
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
@@ -50,6 +51,7 @@
BreakBeforeBraces: Custom
# BreakBeforeInheritanceComma: true
BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true
BreakBeforeConceptDeclarations: Always
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: true
@@ -70,6 +72,7 @@
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
- BASED_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
# Standard library headers come before anything else
@@ -90,6 +93,8 @@
IndentPPDirectives: AfterHash
IndentExternBlock: NoIndent
IndentWidth: 2
IndentWrappedFunctionNames: false
IndentRequiresClause: true
RequiresClausePosition: OwnLine
InsertTrailingCommas: Wrapped
JavaScriptQuotes: Double
JavaScriptWrapImports: true
@@ -98,6 +103,7 @@
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
RequiresExpressionIndentation: OuterScope
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
diff --git a/ .clang-tidy b/ .clang-tidy
@@ -3,24 +3,24 @@
# misc-non-private-member-variables-in-classes: the options don't do anything
# modernize-use-nodiscard: too aggressive, attribute is situationally useful
Checks: "*,\
-google-readability-todo,\
-altera-*,\
-boost*,\
-cppcoreguidelines-avoid-do-while,\
-cppcoreguidelines-pro-bounds-constant-array-index,\
-fuchsia-*,\
fuchsia-multiple-inheritance,\
-google-readability-todo,\
-llvm-header-guard,\
-llvm-include-order,\
-llvmlibc-*,\
-cppcoreguidelines-avoid-do-while,\
-cppcoreguidelines-pro-bounds-constant-array-index,\
-bugprone-easily-swappable-parameters,\
-misc-include-cleaner,\
-misc-non-private-member-variables-in-classes,\
-misc-no-recursion,\
-modernize-use-nodiscard,\
-modernize-use-designated-initializers,\
-modernize-use-trailing-return-type,\
-readability-identifier-length,\
-*-magic-numbers,\
-*-use-ranges,\
-readability-suspicious-call-argument,\
-*-ranges,\
-cppcoreguidelines-missing-std-forward,\
-cppcoreguidelines-rvalue-reference-param-not-moved,\
"
WarningsAsErrors: ''
CheckOptions:
@@ -81,9 +81,9 @@
CheckOptions:
- key: 'readability-identifier-naming.ConstexprVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumCase'
value: 'CamelCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumConstantCase'
value: 'CamelCase'
value: 'lower_case'
- key: 'readability-identifier-naming.FunctionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalConstantCase'
@@ -137,7 +137,7 @@
CheckOptions:
- key: 'readability-identifier-naming.PublicMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ScopedEnumConstantCase'
value: 'CamelCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticVariableCase'
@@ -159,9 +159,21 @@
CheckOptions:
- key: 'readability-identifier-naming.UnionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ValueTemplateParameterCase'
value: 'CamelCase'
value: 'lower_case'
- key: 'readability-identifier-naming.VariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.VirtualMethodCase'
value: 'lower_case'
- key: 'readability-identifier-length.IgnoredVariableNames'
value: "^[abcdxyznm]$"
- key: 'readability-identifier-length.IgnoredParameterNames'
value: "^[abcdxyznm]$"
- key: 'google-runtime-int.UnsignedTypePrefix'
value: "u"
- key: 'google-runtime-int.SignedTypePrefix'
value: "i"
- key: 'cppcoreguidelines-missing-std-forward.ForwardFunction'
value: "::based::forward"
- key: 'cppcoreguidelines-rvalue-reference-param-not-moved.MoveFunction'
value: "::based::move"
...
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -4,7 +4,7 @@
include(cmake/prelude.cmake)
project(
hemplate
VERSION 0.4.0
VERSION 0.4.1
DESCRIPTION "Simple HTML template engine"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/hemplate.git"
LANGUAGES CXX
@@ -13,7 +13,7 @@
project(
include(cmake/project-is-top-level.cmake)
include(cmake/variables.cmake)
find_package(based 0.1.1 CONFIG REQUIRED)
find_package(based 0.2.0 CONFIG REQUIRED)
# ---- Declare library ----
diff --git a/ example/html.cpp b/ example/html.cpp
@@ -33,9 +33,9 @@
int main()
},
transform(
vec,
[](const auto& e)
[](const auto& elem)
{
return e;
return elem;
}
),
},
diff --git a/ include/hemplate/attribute.hpp b/ include/hemplate/attribute.hpp
@@ -20,7 +20,7 @@
public:
{
}
attribute(std::string_view name, std::string_view val)
attribute(std::string_view name, std::string_view val) // NOLINT(*swappable*)
: m_name(name)
, m_value(val)
{
@@ -34,12 +34,12 @@
public:
return name() + "=\"" + value() + "\"";
}
const std::string& name() const { return m_name; }
const std::string& value() const { return m_value; }
[[nodiscard]] const std::string& name() const { return m_name; }
[[nodiscard]] const std::string& value() const { return m_value; }
bool operator==(const attribute& rhs) const = default;
bool empty() const { return value().empty(); }
[[nodiscard]] bool empty() const { return value().empty(); }
attribute& append(std::string_view delim, const std::string& val);
};
@@ -56,7 +56,7 @@
public:
void set(const attribute_list& list);
void set(attribute attr);
bool empty() const
[[nodiscard]] bool empty() const
{
return m_attributes.empty() && m_class.empty() && m_style.empty();
}
diff --git a/ include/hemplate/common.hpp b/ include/hemplate/common.hpp
@@ -2,8 +2,7 @@
#include <array>
#include <based/string.hpp>
#include <based/type_traits.hpp>
#include <based/concepts/procedure/procedure.hpp>
#include "hemplate/element.hpp"
#include "hemplate/hemplate_export.hpp"
diff --git a/ include/hemplate/element.hpp b/ include/hemplate/element.hpp
@@ -3,10 +3,11 @@
#include <span>
#include <sstream>
#include <string>
#include <variant>
#include <vector>
#include <based/string.hpp>
#include <based/template.hpp>
#include <based/functional/overload.hpp>
#include <based/string/literal.hpp>
#include "hemplate/attribute.hpp"
#include "hemplate/hemplate_export.hpp"
@@ -14,19 +15,19 @@
namespace hemplate
{
template<std::size_t N>
using string_literal = based::string_literal<N>;
template<std::size_t n>
using string_literal = based::string_literal<n>;
class HEMPLATE_EXPORT element_base
{
friend class element;
template<string_literal Tag>
requires(Tag.size() > 0)
template<string_literal tag>
requires(tag.size() > 0)
friend class element_boolean;
template<string_literal Tag>
requires(Tag.size() > 0)
template<string_literal tag>
requires(tag.size() > 0)
friend class element_atomic;
std::string m_otag;
@@ -71,7 +72,9 @@
class HEMPLATE_EXPORT element_base
template<typename... Args>
explicit element_base(
std::string_view open_tag, std::string_view close_tag, Args&&... args
std::string_view open_tag, // NOLINT(*swappable*)
std::string_view close_tag,
Args&&... args
)
: m_otag(open_tag)
, m_ctag(close_tag)
@@ -123,9 +126,9 @@
class HEMPLATE_EXPORT element_base
public:
explicit operator std::string() const
{
std::stringstream ss;
ss << *this;
return ss.str();
std::stringstream sstr;
sstr << *this;
return sstr.str();
}
friend std::ostream& operator<<(
@@ -148,15 +151,15 @@
public:
}
};
template<string_literal Tag>
requires(Tag.size() > 0)
template<string_literal tag>
requires(tag.size() > 0)
class HEMPLATE_EXPORT element_boolean : public element_base
{
static auto close() { return std::format("</{}>", Tag.data()); }
static auto close() { return std::format("</{}>", tag.data()); }
static auto open(const attribute_list& attrs = {})
{
return attrs.empty() ? std::format("<{}>", Tag.data())
: std::format("<{} {}>", Tag.data(), attrs);
return attrs.empty() ? std::format("<{}>", tag.data())
: std::format("<{} {}>", tag.data(), attrs);
}
public:
@@ -174,14 +177,14 @@
public:
}
};
template<string_literal Tag>
requires(Tag.size() > 0)
template<string_literal tag>
requires(tag.size() > 0)
class HEMPLATE_EXPORT element_atomic : public element_base
{
static auto open(const attribute_list& attrs = {})
{
return attrs.empty() ? std::format("<{} />", Tag.data())
: std::format("<{} {} />", Tag.data(), attrs);
return attrs.empty() ? std::format("<{} />", tag.data())
: std::format("<{} {} />", tag.data(), attrs);
}
public:
diff --git a/ test/source/common_test.cpp b/ test/source/common_test.cpp
@@ -45,31 +45,28 @@
TEST_CASE("transform", "[common/transform]")
SECTION("direct")
{
const std::vector<std::string> vec = {"1", "2"};
const auto t = tag {hemplate::transform(
const auto tmp = tag {hemplate::transform(
vec,
[](const auto& e)
[](const auto& elem)
{
return child {e};
return child {elem};
}
)};
REQUIRE(
std::string(t)
== "<t>\n <c>1</c>\n <c>2</c>\n</t>\n"
);
REQUIRE(std::string(tmp) == "<t>\n <c>1</c>\n <c>2</c>\n</t>\n");
}
SECTION("indirect")
{
const std::vector<std::string> vec = {"1", "2"};
const auto t = tag {hemplate::transform(
const auto tmp = tag {hemplate::transform(
vec,
[](const auto& e)
[](const auto& elem)
{
return hemplate::element {e};
return hemplate::element {elem};
}
)};
REQUIRE(std::string(t) == "<t>\n 1\n 2\n</t>\n");
REQUIRE(std::string(tmp) == "<t>\n 1\n 2\n</t>\n");
}
}
diff --git a/ test/source/element_test.cpp b/ test/source/element_test.cpp
@@ -11,86 +11,88 @@
TEST_CASE("boolean", "[element]")
SECTION("empty")
{
const auto t = tag {};
const auto tmp = tag {};
REQUIRE(std::string(t) == "<tag>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n</tag>\n");
}
SECTION("attribute")
{
const auto t = tag {{{"attr", "val"}}};
const auto tmp = tag {{{"attr", "val"}}};
REQUIRE(std::string(t) == "<tag attr=\"val\">\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag attr=\"val\">\n</tag>\n");
}
SECTION("data")
{
const auto t = tag {"text"};
const auto tmp = tag {"text"};
REQUIRE(std::string(t) == "<tag>text</tag>\n");
REQUIRE(std::string(tmp) == "<tag>text</tag>\n");
}
SECTION("attribute data")
{
const auto t = tag {{{"attr", "val"}}, "text"};
const auto tmp = tag {{{"attr", "val"}}, "text"};
REQUIRE(std::string(t) == "<tag attr=\"val\">text</tag>\n");
REQUIRE(std::string(tmp) == "<tag attr=\"val\">text</tag>\n");
}
SECTION("child")
{
const auto t = tag {
const auto tmp = tag {
child {},
};
REQUIRE(std::string(t) == "<tag>\n <child>\n </child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>\n </child>\n</tag>\n");
}
SECTION("attribute child")
{
const auto t = tag {
const auto tmp = tag {
{{"attr", "val"}},
child {},
};
REQUIRE(
std::string(t) == "<tag attr=\"val\">\n <child>\n </child>\n</tag>\n"
std::string(tmp)
== "<tag attr=\"val\">\n <child>\n </child>\n</tag>\n"
);
}
SECTION("child data")
{
const auto t = tag {
const auto tmp = tag {
child {"text"},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("attribute child data")
{
const auto t = tag {
const auto tmp = tag {
{{"attr", "val"}},
child {"text"},
};
REQUIRE(
std::string(t) == "<tag attr=\"val\">\n <child>text</child>\n</tag>\n"
std::string(tmp)
== "<tag attr=\"val\">\n <child>text</child>\n</tag>\n"
);
}
SECTION("range")
{
const std::vector<std::string> vec = {"hello", "world"};
const auto t = tag {vec};
const auto tmp = tag {vec};
REQUIRE(std::string(t) == "<tag>\n hello\n world\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n hello\n world\n</tag>\n");
}
SECTION("tag element elemetn tag")
{
const auto t = tag {element {element {tag {}}}};
REQUIRE(std::string(t) == "<tag>\n <tag>\n </tag>\n</tag>\n");
const auto tmp = tag {element {element {tag {}}}};
REQUIRE(std::string(tmp) == "<tag>\n <tag>\n </tag>\n</tag>\n");
}
}
@@ -100,16 +102,16 @@
TEST_CASE("atomic", "[element]")
SECTION("empty")
{
const auto t = tag {};
const auto tmp = tag {};
REQUIRE(std::string(t) == "<tag />\n");
REQUIRE(std::string(tmp) == "<tag />\n");
}
SECTION("attribute")
{
const auto t = tag {{{"attr", "val"}}};
const auto tmp = tag {{{"attr", "val"}}};
REQUIRE(std::string(t) == "<tag attr=\"val\" />\n");
REQUIRE(std::string(tmp) == "<tag attr=\"val\" />\n");
}
}
@@ -120,75 +122,75 @@
TEST_CASE("element", "[element]")
SECTION("empty")
{
const auto t = element {};
const auto tmp = element {};
REQUIRE(std::string(t) == ""); // NOLINT
REQUIRE(std::string(tmp) == ""); // NOLINT
}
SECTION("data")
{
const auto t = element {"text"};
const auto tmp = element {"text"};
REQUIRE(std::string(t) == "text\n");
REQUIRE(std::string(tmp) == "text\n");
}
SECTION("tag")
{
const auto t = element {
const auto tmp = element {
tag {},
};
REQUIRE(std::string(t) == "<tag>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n</tag>\n");
}
SECTION("tag element")
{
const auto t = tag {
const auto tmp = tag {
element {},
};
REQUIRE(std::string(t) == "<tag>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n</tag>\n");
}
SECTION("element tag")
{
const auto t = element {
const auto tmp = element {
tag {},
};
REQUIRE(std::string(t) == "<tag>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n</tag>\n");
}
SECTION("element tag data")
{
const auto t = element {
const auto tmp = element {
tag {"text"},
};
REQUIRE(std::string(t) == "<tag>text</tag>\n");
REQUIRE(std::string(tmp) == "<tag>text</tag>\n");
}
SECTION("tag element data")
{
const auto t = tag {
const auto tmp = tag {
element {"text"},
};
REQUIRE(std::string(t) == "<tag>text</tag>\n");
REQUIRE(std::string(tmp) == "<tag>text</tag>\n");
}
SECTION("tag element data")
{
const auto t = tag {
const auto tmp = tag {
element {"text"},
};
REQUIRE(std::string(t) == "<tag>text</tag>\n");
REQUIRE(std::string(tmp) == "<tag>text</tag>\n");
}
SECTION("element tag child data")
{
const auto t = element {
const auto tmp = element {
tag {
child {
"text",
@@ -196,12 +198,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("element tag element child data")
{
const auto t = element {
const auto tmp = element {
tag {
element {
child {
@@ -211,12 +213,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("element tag child element data")
{
const auto t = element {
const auto tmp = element {
tag {
child {
element {
@@ -226,12 +228,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("element tag element child element data")
{
const auto t = element {
const auto tmp = element {
tag {
element {
child {
@@ -243,12 +245,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("tag element child data")
{
const auto t = tag {
const auto tmp = tag {
element {
child {
"text",
@@ -256,12 +258,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("tag child element data")
{
const auto t = tag {
const auto tmp = tag {
child {
element {
"text",
@@ -269,12 +271,12 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
SECTION("tag element child element data")
{
const auto t = tag {
const auto tmp = tag {
element {
child {
element {
@@ -284,6 +286,6 @@
TEST_CASE("element", "[element]")
},
};
REQUIRE(std::string(t) == "<tag>\n <child>text</child>\n</tag>\n");
REQUIRE(std::string(tmp) == "<tag>\n <child>text</child>\n</tag>\n");
}
}
diff --git a/ vcpkg.json b/ vcpkg.json
@@ -1,10 +1,10 @@
{
"name": "hemplate",
"version-semver": "0.4.0",
"version-semver": "0.4.1",
"dependencies": [
{
"name": "based",
"version>=": "0.1.1"
"version>=": "0.2.0"
}
],
"default-features": [],