hemplateSimple XML template engine |
git clone git://git.dimitrijedobrota.com/hemplate.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 6317bad529beb61dcd1fefeac8a4e5278449672b |
parent | a4013908e40f037e1f775cac2697165670954199 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Fri, 25 Apr 2025 19:40:56 +0200 |
Finally figure it out!
M | example/html.cpp | | | +++ |
M | include/hemplate/atom.hpp | | | ++++++ ---- |
M | include/hemplate/element.hpp | | | ++++++++++++++++++++++++++++++++++++++ -------------------------------------------- |
M | include/hemplate/rss.hpp | | | +++++++++++++++++++++++++++++++ --------------------------- |
M | include/hemplate/sitemap.hpp | | | ++++++ ---- |
M | source/element.cpp | | | +++++++++++++ ---------- |
6 files changed, 113 insertions(+), 107 deletions(-)
diff --git a/ example/html.cpp b/ example/html.cpp
@@ -1,4 +1,5 @@
#include <iostream>
#include <vector>
#include "hemplate/html.hpp"
@@ -15,6 +16,8 @@
int main()
{"style", "margin-bottom: 1em"},
};
const std::vector<html::b> vec = {html::b("1"), html::b("2"), html::b("3")};
std::cout << html::html {
comment {"Hello this is a comment"},
html::ul {
diff --git a/ include/hemplate/atom.hpp b/ include/hemplate/atom.hpp
@@ -21,13 +21,15 @@
class HEMPLATE_EXPORT feed : public element_boolean<"feed">
public:
static constexpr const auto default_xmlns = "http://www.w3.org/2005/Atom";
explicit feed(std::string_view xmlns, const is_element auto&... children)
: element_builder(attributes(xmlns), children...)
template<typename... Args>
explicit feed(std::string_view xmlns, Args&&... args)
: element_builder(attributes(xmlns), std::forward<Args>(args)...)
{
}
explicit feed(const is_element auto&... children)
: feed(default_xmlns, children...)
template<typename... Args>
explicit feed(Args&&... args)
: element_builder(attributes(default_xmlns), std::forward<Args>(args)...)
{
}
};
diff --git a/ include/hemplate/element.hpp b/ include/hemplate/element.hpp
@@ -6,6 +6,7 @@
#include <vector>
#include <based/string.hpp>
#include <based/template.hpp>
#include "hemplate/attribute.hpp"
#include "hemplate/hemplate_export.hpp"
@@ -13,16 +14,17 @@
namespace hemplate
{
class element;
class element_base;
template<typename T>
concept is_element = std::derived_from<T, element>;
concept is_element = std::derived_from<T, element_base>;
class HEMPLATE_EXPORT element
class HEMPLATE_EXPORT element_base
{
public:
enum class Type : uint8_t
{
Blank,
Atomic,
Boolean,
};
@@ -34,52 +36,35 @@
private:
std::string m_otag;
std::string m_ctag;
std::vector<element> m_children;
std::string m_data;
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;
explicit element(
std::string_view open_tag,
std::string_view close_tag,
const is_element auto&... children
)
: m_otag(open_tag)
, m_ctag(close_tag)
, m_children(std::initializer_list<element> {children...})
{
}
explicit element(
std::string_view open_tag,
std::string_view close_tag,
std::span<const element> children
template<typename... Args>
explicit element_base(
std::string_view open_tag, std::string_view close_tag, Args&&... args
)
: m_otag(open_tag)
, m_ctag(close_tag)
, m_children(std::begin(children), std::end(children))
{
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)
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)); },
};
(add(std::forward<Args>(args)), ...);
}
public:
// NOLINTBEGIN *-explicit-constructor
element(std::string_view data)
: m_data(data)
{
}
element(const is_element auto&... children)
: m_children(std::initializer_list<element> {children...})
{
}
element(std::span<const element> children)
: m_children(std::begin(children), std::end(children))
{
}
// NOLINTEND *-explicit-constructor
explicit operator std::string() const
{
std::stringstream ss;
@@ -87,19 +72,36 @@
public:
return ss.str();
}
friend std::ostream& operator<<(std::ostream& out, const element& element)
friend std::ostream& operator<<(
std::ostream& out, const element_base& element
)
{
element.render(out, 0);
return out;
}
};
template<based::string_literal Tag, element::Type MyType>
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
{
public:
template<typename... Args>
requires(!std::same_as<attribute_list, std::remove_cvref_t<Args>> && ...)
explicit element_builder(Args&&... args)
: 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::Type::Boolean>
: public element
class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Boolean>
: public element_base
{
static auto close() { return std::format("</{}>", Tag.data()); }
static auto open(const attribute_list& attrs = {})
@@ -110,35 +112,25 @@
class HEMPLATE_EXPORT element_builder<Tag, element::Type::Boolean>
public:
template<typename... Args>
explicit element_builder(Args&&... args)
: element(open(), close(), element(std::forward<Args>(args))...)
{
}
explicit element_builder(std::span<const element> children)
: element(open(), close(), children)
{
}
template<typename... Args>
explicit element_builder(const attribute_list& attrs, Args&&... args)
: element(open(attrs), close(), element(std::forward<Args>(args))...)
: element_base(open(attrs), close(), std::forward<Args>(args)...)
{
}
explicit element_builder(
const attribute_list& attrs, std::span<const element> children
)
: element(open(attrs), close(), children)
template<typename... Args>
requires(!std::same_as<attribute_list, std::remove_cvref_t<Args>> && ...)
explicit element_builder(Args&&... args)
: element_base(open(), close(), std::forward<Args>(args)...)
{
}
};
template<based::string_literal Tag>
using element_boolean = element_builder<Tag, element::Type::Boolean>;
using element_boolean = element_builder<Tag, element_base::Type::Boolean>;
template<based::string_literal Tag>
class HEMPLATE_EXPORT element_builder<Tag, element::Type::Atomic>
: public element
class HEMPLATE_EXPORT element_builder<Tag, element_base::Type::Atomic>
: public element_base
{
static auto open(const attribute_list& attrs = {})
{
@@ -148,12 +140,12 @@
class HEMPLATE_EXPORT element_builder<Tag, element::Type::Atomic>
public:
explicit element_builder(const attribute_list& list = {})
: element(open(list))
: element_base("", "", open(list))
{
}
};
template<based::string_literal Tag>
using element_atomic = element_builder<Tag, element::Type::Atomic>;
using element_atomic = element_builder<Tag, element_base::Type::Atomic>;
} // namespace hemplate
diff --git a/ include/hemplate/rss.hpp b/ include/hemplate/rss.hpp
@@ -23,17 +23,18 @@
public:
static constexpr const auto default_version = "2.0";
static constexpr const auto default_xmlns = "http://www.w3.org/2005/Atom";
explicit rss(
std::string_view version,
std::string_view xmlns,
const is_element auto&... children
)
: element_builder(attributes(version, xmlns), children...)
template<typename... Args>
explicit rss(std::string_view version, std::string_view xmlns, Args&&... args)
: element_builder(attributes(version, xmlns), std::forward<Args>(args)...)
{
}
explicit rss(const is_element auto&... children)
: rss(default_version, default_xmlns, children...)
template<typename... Args>
explicit rss(Args&&... args)
: element_builder(
attributes(default_version, default_xmlns),
std::forward<Args>(args)...
)
{
}
};
@@ -42,46 +43,49 @@
class HEMPLATE_EXPORT atomLink // NOLINT *-identifier-naming
: public element_boolean<"atom:link">
{
static auto attributes(
attribute_list& list, std::string_view rel, std::string_view type
const attribute_list& list, std::string_view rel, std::string_view type
)
{
list.set({
{"rel", rel},
{"type", type},
});
return list;
return attribute_list {list, {{"rel", rel}, {"type", type}}};
}
public:
static constexpr const auto default_rel = "self";
static constexpr const auto default_type = "application/rss+xml";
template<typename... Args>
explicit atomLink(
std::string_view rel,
std::string_view type,
attribute_list attrs,
const is_element auto&... children
const attribute_list& attrs,
Args&&... args
)
: element_builder(attributes(attrs, rel, type), children...)
: element_builder(
attributes(attrs, rel, type), std::forward<Args>(args)...
)
{
}
explicit atomLink(
std::string_view rel,
std::string_view type,
const is_element auto&... children
)
: atomLink(rel, type, {}, children...)
template<typename... Args>
explicit atomLink(std::string_view rel, std::string_view type, Args&&... args)
: element_builder(
attributes({}, rel, type), {}, std::forward<Args>(args)...
)
{
}
explicit atomLink(attribute_list attrs, const is_element auto&... children)
: atomLink(default_rel, default_type, std::move(attrs), children...)
template<typename... Args>
explicit atomLink(const attribute_list& attrs, Args&&... args)
: element_builder(
attributes(attrs, default_rel, default_type),
std::forward<Args>(args)...
)
{
}
explicit atomLink(const is_element auto&... children)
: atomLink({}, children...)
template<typename... Args>
explicit atomLink(Args&&... args)
: element_builder(std::forward<Args>(args)...)
{
}
};
diff --git a/ include/hemplate/sitemap.hpp b/ include/hemplate/sitemap.hpp
@@ -19,13 +19,15 @@
public:
static constexpr const auto default_xmlns =
"http://www.sitemaps.org/schemas/sitemap/0.9";
explicit urlset(std::string_view xmlns, const is_element auto&... children)
: element_builder(attributes(xmlns), children...)
template<typename... Args>
explicit urlset(std::string_view xmlns, Args&&... args)
: element_builder(attributes(xmlns), std::forward<Args>(args)...)
{
}
explicit urlset(const is_element auto&... children)
: urlset(default_xmlns, children...)
template<typename... Args>
explicit urlset(Args&&... args)
: urlset(default_xmlns, std::forward<Args>(args)...)
{
}
};
diff --git a/ source/element.cpp b/ source/element.cpp
@@ -6,27 +6,30 @@
namespace hemplate
{
void element::render_children(std::ostream& out, std::size_t indent_value) const
void element_base::render_children(std::ostream& out, std::size_t indent_value)
const
{
for (const auto& child : m_children) {
child.render(out, indent_value);
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::render(std::ostream& out, std::size_t indent_value) const
void element_base::render(std::ostream& out, std::size_t indent_value) const
{
const std::string indent(indent_value, ' ');
if (m_otag.empty()) {
if (!m_data.empty()) {
out << indent << m_data << '\n';
} else {
render_children(out, indent_value);
}
render_children(out, indent_value);
return;
}
if (!m_children.empty()) {
if (!m_cdn.empty()) {
out << indent << m_otag << '\n';
render_children(out, indent_value + 2);
out << indent << m_ctag << '\n';