hemplateSimple 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
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