hemplate

Simple XML template engine
git clone git://git.dimitrijedobrota.com/hemplate.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commit c3b07dea92d12ab3b39a94e84e2505e83311c3a8
parent 6317bad529beb61dcd1fefeac8a4e5278449672b
author Dimitrije Dobrota <mail@dimitrijedobrota.com>
date Sat, 26 Apr 2025 09:25:49 +0200

Big cleanup, naming improvements

Diffstat:
M .clang-format | + -
M CMakeLists.txt | -
M example/html.cpp | +
M include/hemplate/atom.hpp | +++ ---
M include/hemplate/classes.hpp | ++++ ----------
M include/hemplate/element.hpp | +++++++++++++++++++++++++++++++++++++++++++++++ -----------------------------------
M include/hemplate/rss.hpp | ++++++++++++ --------------
M include/hemplate/sitemap.hpp | +++ ---
D source/element.cpp | ------------------------------------------

9 files changed, 92 insertions(+), 124 deletions(-)


diff --git a/ .clang-format b/ .clang-format

@@ -17,7 +17,7 @@ AllowShortEnumsOnASingleLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortLambdasOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None

diff --git a/ CMakeLists.txt b/ CMakeLists.txt

@@ -20,7 +20,6 @@ find_package(based 0.1 CONFIG REQUIRED) add_library( hemplate_hemplate source/classes.cpp
source/element.cpp
source/attribute.cpp ) target_link_libraries(hemplate_hemplate PUBLIC based::based)

diff --git a/ example/html.cpp b/ example/html.cpp

@@ -32,6 +32,7 @@ int main() "Item 2", "some text", },
transform(vec, [](const auto& e) { return e; }),
}, html::hr {}, };

diff --git a/ include/hemplate/atom.hpp b/ include/hemplate/atom.hpp

@@ -19,17 +19,17 @@ class HEMPLATE_EXPORT feed : public element_boolean<"feed"> } public:
static constexpr const auto default_xmlns = "http://www.w3.org/2005/Atom";
static constexpr const auto def_xmlns = "http://www.w3.org/2005/Atom";
template<typename... Args> explicit feed(std::string_view xmlns, Args&&... args)
: element_builder(attributes(xmlns), std::forward<Args>(args)...)
: element_boolean(attributes(xmlns), std::forward<Args>(args)...)
{ } template<typename... Args> explicit feed(Args&&... args)
: element_builder(attributes(default_xmlns), std::forward<Args>(args)...)
: element_boolean(attributes(def_xmlns), std::forward<Args>(args)...)
{ } };

diff --git a/ include/hemplate/classes.hpp b/ include/hemplate/classes.hpp

@@ -23,18 +23,12 @@ public: class HEMPLATE_EXPORT xml : public element { public:
static constexpr const auto default_version = "1.0";
static constexpr const auto default_encoding = "UTF-8";
static auto data(std::string_view version, std::string_view encoding)
{
const attribute_list attrs {{"version", version}, {"encoding", encoding}};
return "?xml " + std::string(attrs) + "?";
}
static constexpr const auto def_version = "1.0";
static constexpr const auto def_encoding = "UTF-8";
explicit xml(
std::string_view version = default_version,
std::string_view encoding = default_encoding
std::string_view version = def_version,
std::string_view encoding = def_encoding
) : element(std::format("<? {}?>", attribute_list {version, encoding})) {

diff --git a/ include/hemplate/element.hpp b/ include/hemplate/element.hpp

@@ -14,34 +14,27 @@ namespace hemplate {
class element_base;
template<typename T>
concept is_element = std::derived_from<T, element_base>;
template<std::size_t N>
using string_literal = based::string_literal<N>;
class HEMPLATE_EXPORT element_base {
public:
enum class Type : uint8_t
{
Blank,
Atomic,
Boolean,
};
friend class element;
template<string_literal Tag>
requires(Tag.size() > 0)
friend class element_boolean;
template<based::string_literal Tag, Type MyType>
friend class element_builder;
template<string_literal Tag>
requires(Tag.size() > 0)
friend class element_atomic;
private:
std::string m_otag; std::string m_ctag; using child_t = std::variant<element_base, std::string>; std::vector<child_t> m_cdn;
void render_children(std::ostream& out, std::size_t indent_value) const;
void render(std::ostream& out, std::size_t indent_value) const;
template<typename... Args> explicit element_base( std::string_view open_tag, std::string_view close_tag, Args&&... args

@@ -49,21 +42,61 @@ private: : m_otag(open_tag) , m_ctag(close_tag) {
m_cdn.reserve(sizeof...(args));
const auto add = based::overload {
[&](const element_base& elem) { m_cdn.emplace_back(elem); },
[&](const std::ranges::forward_range auto& range)
[this](const std::ranges::forward_range auto& range)
requires(!std::constructible_from<std::string_view, decltype(range)>) { m_cdn.reserve(std::size(m_cdn) + std::size(range)); m_cdn.insert(std::end(m_cdn), std::begin(range), std::end(range)); },
[&](const std::string_view data)
{ m_cdn.emplace_back(std::string(data)); },
};
[this](const std::string_view data)
{
m_cdn.emplace_back(std::string(data));
},
[this](const element_base& elem)
{
m_cdn.emplace_back(elem);
},
};
m_cdn.reserve(sizeof...(args));
(add(std::forward<Args>(args)), ...); }
void render_children(std::ostream& out, std::size_t indent_value) const
{
const std::string indent(indent_value, ' ');
const auto render = based::overload {
[&](const element_base& elem)
{
elem.render(out, indent_value);
},
[&](const std::string& data)
{
out << indent << data << '\n';
},
};
for (const auto& child : m_cdn) {
std::visit(render, child);
}
}
void render(std::ostream& out, std::size_t indent_value) const
{
const std::string indent(indent_value, ' ');
if (m_otag.empty()) {
render_children(out, indent_value);
return;
}
out << indent << m_otag << '\n';
render_children(out, indent_value + 2);
out << indent << m_ctag << '\n';
}
public: explicit operator std::string() const {

@@ -72,36 +105,27 @@ public: return ss.str(); }
friend std::ostream& operator<<(
std::ostream& out, const element_base& element
)
friend std::ostream& operator<<(std::ostream& out, const element_base& element)
{ element.render(out, 0); return out; } };
template<based::string_literal Tag, element_base::Type MyType>
class element_builder;
template<>
class HEMPLATE_EXPORT element_builder<"", element_base::Type::Blank>
: public element_base
class HEMPLATE_EXPORT element : public element_base
{ public: template<typename... Args> requires(!std::same_as<attribute_list, std::remove_cvref_t<Args>> && ...)
explicit element_builder(Args&&... args)
element(Args&&... args) // NOLINT *-explicit
: element_base("", "", std::forward<Args>(args)...) { } };
using element = element_builder<"", element_base::Type::Blank>;
template<based::string_literal Tag>
class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Boolean>
: public element_base
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 open(const attribute_list& attrs = {})

@@ -112,25 +136,22 @@ class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Boolean> public: template<typename... Args>
explicit element_builder(const attribute_list& attrs, Args&&... args)
explicit element_boolean(const attribute_list& attrs, Args&&... args)
: element_base(open(attrs), close(), std::forward<Args>(args)...) { } template<typename... Args> requires(!std::same_as<attribute_list, std::remove_cvref_t<Args>> && ...)
explicit element_builder(Args&&... args)
explicit element_boolean(Args&&... args)
: element_base(open(), close(), std::forward<Args>(args)...) { } };
template<based::string_literal Tag>
using element_boolean = element_builder<Tag, element_base::Type::Boolean>;
template<based::string_literal Tag>
class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Atomic>
: public element_base
template<string_literal Tag>
requires(Tag.size() > 0)
class HEMPLATE_EXPORT element_atomic : public element_base
{ static auto open(const attribute_list& attrs = {}) {

@@ -139,13 +160,10 @@ class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Atomic> } public:
explicit element_builder(const attribute_list& list = {})
explicit element_atomic(const attribute_list& list = {})
: element_base("", "", open(list)) { } };
template<based::string_literal Tag>
using element_atomic = element_builder<Tag, element_base::Type::Atomic>;
} // namespace hemplate

diff --git a/ include/hemplate/rss.hpp b/ include/hemplate/rss.hpp

@@ -20,20 +20,19 @@ class HEMPLATE_EXPORT rss : public element_boolean<"rss"> } public:
static constexpr const auto default_version = "2.0";
static constexpr const auto default_xmlns = "http://www.w3.org/2005/Atom";
static constexpr const auto def_version = "2.0";
static constexpr const auto def_xmlns = "http://www.w3.org/2005/Atom";
template<typename... Args> explicit rss(std::string_view version, std::string_view xmlns, Args&&... args)
: element_builder(attributes(version, xmlns), std::forward<Args>(args)...)
: element_boolean(attributes(version, xmlns), std::forward<Args>(args)...)
{ } template<typename... Args> explicit rss(Args&&... args)
: element_builder(
attributes(default_version, default_xmlns),
std::forward<Args>(args)...
: element_boolean(
attributes(def_version, def_xmlns), std::forward<Args>(args)...
) { }

@@ -50,8 +49,8 @@ class HEMPLATE_EXPORT atomLink // NOLINT *-identifier-naming } public:
static constexpr const auto default_rel = "self";
static constexpr const auto default_type = "application/rss+xml";
static constexpr const auto def_rel = "self";
static constexpr const auto def_type = "application/rss+xml";
template<typename... Args> explicit atomLink(

@@ -60,7 +59,7 @@ public: const attribute_list& attrs, Args&&... args )
: element_builder(
: element_boolean(
attributes(attrs, rel, type), std::forward<Args>(args)... ) {

@@ -68,7 +67,7 @@ public: template<typename... Args> explicit atomLink(std::string_view rel, std::string_view type, Args&&... args)
: element_builder(
: element_boolean(
attributes({}, rel, type), {}, std::forward<Args>(args)... ) {

@@ -76,16 +75,15 @@ public: template<typename... Args> explicit atomLink(const attribute_list& attrs, Args&&... args)
: element_builder(
attributes(attrs, default_rel, default_type),
std::forward<Args>(args)...
: element_boolean(
attributes(attrs, def_rel, def_type), std::forward<Args>(args)...
) { } template<typename... Args> explicit atomLink(Args&&... args)
: element_builder(std::forward<Args>(args)...)
: element_boolean(std::forward<Args>(args)...)
{ } };

diff --git a/ include/hemplate/sitemap.hpp b/ include/hemplate/sitemap.hpp

@@ -16,18 +16,18 @@ class HEMPLATE_EXPORT urlset : public element_boolean<"urlset"> } public:
static constexpr const auto default_xmlns =
static constexpr const auto def_xmlns =
"http://www.sitemaps.org/schemas/sitemap/0.9"; template<typename... Args> explicit urlset(std::string_view xmlns, Args&&... args)
: element_builder(attributes(xmlns), std::forward<Args>(args)...)
: element_boolean(attributes(xmlns), std::forward<Args>(args)...)
{ } template<typename... Args> explicit urlset(Args&&... args)
: urlset(default_xmlns, std::forward<Args>(args)...)
: element_boolean(attribute(def_xmlns), std::forward<Args>(args)...)
{ } };

diff --git a/ source/element.cpp b/ source/element.cpp

@@ -1,42 +0,0 @@
#include <ostream>
#include <string>
#include "hemplate/element.hpp"
namespace hemplate
{
void element_base::render_children(std::ostream& out, std::size_t indent_value)
const
{
const auto render = based::overload {
[&](const element_base& elem) { elem.render(out, indent_value); },
[&](const std::string& data)
{ out << std::string(indent_value, ' ') << data << '\n'; },
};
for (const auto& child : m_cdn) {
std::visit(render, child);
}
}
void element_base::render(std::ostream& out, std::size_t indent_value) const
{
const std::string indent(indent_value, ' ');
if (m_otag.empty()) {
render_children(out, indent_value);
return;
}
if (!m_cdn.empty()) {
out << indent << m_otag << '\n';
render_children(out, indent_value + 2);
out << indent << m_ctag << '\n';
return;
}
out << indent << m_otag << m_ctag << '\n';
}
} // namespace hemplate