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 5dbbc906fba4ffb4aac3d9d2ca4b28529573ec48
parent a536f3b212a1990caa284a84ddc005384f9b4d1b
author Dimitrije Dobrota <mail@dimitrijedobrota.com>
date Wed, 23 Apr 2025 11:20:09 +0200

Streamline attribute and everything using it

Diffstat:
M .clang-tidy | +
M example/html.cpp | ++++ ---
M include/hemplate/atom.hpp | ++++++++++++ ------
M include/hemplate/attribute.hpp | +++++++++++++++++++ -----------------------------
M include/hemplate/classes.hpp | ++ --
M include/hemplate/element.hpp | ++++++++++++++++++++ ---------------------------------------
M include/hemplate/rss.hpp | +++++++++++++++++++++++++++++++++++++++++++ -----------------------------------
M include/hemplate/sitemap.hpp | ++++++++++++++++++ ------
M source/attribute.cpp | ++++++++++++++++++++++++++++ ----------------------------------------------
M source/element.cpp | ++++++++++++++++++++++ ------------

10 files changed, 169 insertions(+), 178 deletions(-)


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

@@ -12,6 +12,7 @@ Checks: "*,\ -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,\

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

@@ -1,8 +1,9 @@ #include <iostream>
#include "hemplate/attribute.hpp"
#include "hemplate/html.hpp"
#include "hemplate/attribute.hpp"
int main() { using namespace hemplate; // NOLINT

@@ -19,11 +20,11 @@ int main() html::ul { ul_attrs, html::li {
li_attrs.add("class", "item1"),
li_attrs.add({"class", "item1"}),
"Item 1", }, html::li {
li_attrs.add("class", "item2"),
li_attrs.add({"class", "item2"}),
"Item 2", }, },

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

@@ -12,21 +12,27 @@ std::string format_time_now(); class HEMPLATE_EXPORT feed : public element_builder<"feed", element::Type::Boolean> {
static auto attributes(std::string_view xmlns)
{
return attribute_list {
{"xmlns", xmlns},
};
}
public: static constexpr const auto default_xmlns = "http://www.w3.org/2005/Atom";
explicit feed(std::string xmlns,
const std::derived_from<element> auto&... children)
: element_builder({{"xmlns", std::move(xmlns)}}, children...)
explicit feed(std::string_view xmlns, const is_element auto&... children)
: element_builder(attributes(xmlns), children...)
{ }
explicit feed(std::string xmlns, std::span<const element> children)
: element_builder({{"xmlns", std::move(xmlns)}}, children)
explicit feed(std::string_view xmlns, std::span<const element> children)
: element_builder(attributes(xmlns), children)
{ }
explicit feed(const std::derived_from<element> auto&... children)
explicit feed(const is_element auto&... children)
: feed(default_xmlns, children...) { }

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

@@ -2,7 +2,6 @@ #include <format> #include <string>
#include <utility>
#include <vector> #include "hemplate/hemplate_export.hpp"

@@ -10,39 +9,32 @@ namespace hemplate {
class HEMPLATE_EXPORT attribute
struct HEMPLATE_EXPORT attribute
{
public:
attribute(std::string name) // NOLINT
: m_name(std::move(name))
std::string name;
std::string value;
explicit attribute(std::string_view namee)
: name(namee)
{ }
attribute(std::string name, std::string value) // NOLINT
: m_name(std::move(name))
, m_value(std::move(value))
attribute(std::string_view namee, std::string_view val)
: name(namee)
, value(val)
{ }
bool operator==(const attribute& rhs) const = default;
const std::string& get_name() const { return m_name; }
const std::string& get_value() const { return m_value; }
void set_name(const std::string& name) { m_name = name; }
void set_value(const std::string& value) { m_value = value; }
bool empty() const { return get_value().empty(); }
// NOLINTNEXTLINE *-explicit-constructor
operator std::string() const
operator std::string() const // NOLINT *-explicit-constructor
{
return get_name() + "=\"" + get_value() + "\"";
return name + "=\"" + value + "\"";
}
private:
std::string m_name;
std::string m_value;
bool operator==(const attribute& rhs) const = default;
bool empty() const { return value.empty(); }
attribute& append(std::string_view delim, const std::string& val);
}; class HEMPLATE_EXPORT attribute_list

@@ -51,15 +43,13 @@ public: attribute_list() = default; attribute_list(std::initializer_list<attribute> list);
attribute_list(attribute attr); // NOLINT
attribute_list(attribute attr); // NOLINT *-explicit-constructor
attribute_list& set(const attribute_list& list);
attribute_list& set(const std::string& name);
attribute_list& set(const std::string& name, const std::string& value);
attribute_list& set(attribute attr);
attribute_list add(const attribute_list& list) const;
attribute_list add(const std::string& name) const;
attribute_list add(const std::string& name, const std::string& value) const;
attribute_list add(attribute attr) const;
bool empty() const;

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

@@ -22,8 +22,8 @@ public: explicit xml(std::string version = default_version, std::string encoding = default_encoding)
: element_builder({{"version", std::move(version)},
{"encoding", std::move(encoding)}})
: element_builder(attribute_list {{"version", std::move(version)},
{"encoding", std::move(encoding)}})
{ } };

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

@@ -1,5 +1,4 @@ #pragma once
#include <span> #include <string> #include <vector>

@@ -12,6 +11,11 @@ namespace hemplate {
class element;
template<typename T>
concept is_element = std::derived_from<T, element>;
class HEMPLATE_EXPORT element { public:

@@ -36,7 +40,7 @@ private: explicit element(bool& state, Type type, std::string_view name,
const std::derived_from<element> auto&... children)
const is_element auto&... children)
: m_state(&state) , m_type(type) , m_name(name)

@@ -44,11 +48,10 @@ private: { }
explicit element(
bool& state,
Type type,
std::string_view name, // NOLINT *-easily-swappable-parameters
std::string_view data)
explicit element(bool& state,
Type type,
std::string_view name,
std::string_view data)
: m_state(&state) , m_type(type) , m_name(name)

@@ -71,7 +74,7 @@ private: Type type, std::string_view name, attribute_list attributes,
const std::derived_from<element> auto&... children)
const is_element auto&... children)
: m_state(&state) , m_type(type) , m_name(name)

@@ -125,8 +128,11 @@ public: element& add(const element& elem);
element& set(const std::string& name);
element& set(const std::string& name, const std::string& value);
element& set(const attribute_list& list);
element& set(attribute attr);
element add(const attribute_list& list) const;
element add(attribute attr) const;
bool get_state() const { return *m_state; } bool tgl_state() const { return *m_state = !*m_state; }

@@ -138,35 +144,10 @@ class HEMPLATE_EXPORT element_builder : public element static bool m_state; // NOLINT public:
explicit element_builder(std::string_view data)
: element(m_state, MyType, Tag.data(), data)
{
}
explicit element_builder(const std::derived_from<element> auto&... children)
: element(m_state, MyType, Tag.data(), children...)
{
}
explicit element_builder(std::span<const element> children)
: element(m_state, MyType, Tag.data(), children)
{
}
explicit element_builder(attribute_list attributes, std::string_view data)
: element(m_state, MyType, Tag.data(), std::move(attributes), data)
{
}
explicit element_builder(attribute_list attributes,
const std::derived_from<element> auto&... children)
: element(m_state, MyType, Tag.data(), std::move(attributes), children...)
{
}
explicit element_builder(attribute_list attributes,
std::span<const element> children)
: element(m_state, MyType, Tag.data(), std::move(attributes), children)
template<typename... Args>
explicit element_builder(Args&&... args)
// NOLINTNEXTLINE *-no-array-decay
: element(m_state, MyType, Tag.data(), std::forward<Args>(args)...)
{ } };

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

@@ -12,29 +12,33 @@ std::string format_time_now(); class HEMPLATE_EXPORT rss : public element_builder<"rss", element::Type::Boolean> {
static auto attributes(std::string_view version, std::string_view xmlns)
{
return attribute_list {
{"version", version},
{"xmlns", xmlns},
};
}
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 version,
std::string xmlns,
const std::derived_from<element> auto&... children)
: element_builder(
{{"version", std::move(version)}, {"xmlns:atom", std::move(xmlns)}},
children...)
explicit rss(std::string_view version,
std::string_view xmlns,
const is_element auto&... children)
: element_builder(attributes(version, xmlns), children...)
{ }
explicit rss(std::string version,
std::string xmlns,
explicit rss(std::string_view version,
std::string_view xmlns,
std::span<const element> children)
: element_builder(
{{"version", std::move(version)}, {"xmlns:atom", std::move(xmlns)}},
children)
: element_builder(attributes(version, xmlns), children)
{ }
explicit rss(const std::derived_from<element> auto&... children)
explicit rss(const is_element auto&... children)
: rss(default_version, default_xmlns, children...) { }

@@ -48,49 +52,53 @@ public: class HEMPLATE_EXPORT atomLink // NOLINT *-identifier-naming : public element_builder<"atom:link", element::Type::Boolean> {
static auto attributes(attribute_list& list,
std::string_view rel,
std::string_view type)
{
return list.set({
{"rel", rel},
{"type", type},
});
}
public: static constexpr const auto default_rel = "self"; static constexpr const auto default_type = "application/rss+xml";
explicit atomLink(std::string rel,
std::string type,
const attribute_list& attributes,
const std::derived_from<element> auto&... children)
: element_builder(attributes.add({{"rel", std::move(rel)},
{"type", std::move(type)}}),
children...)
explicit atomLink(std::string_view rel,
std::string_view type,
attribute_list attrs,
const is_element auto&... children)
: element_builder(attributes(attrs, rel, type), children...)
{ }
explicit atomLink(std::string rel,
std::string type,
const attribute_list& attributes,
explicit atomLink(std::string_view rel,
std::string_view type,
attribute_list attrs,
std::span<const element> children)
: element_builder(attributes.add({{"rel", std::move(rel)},
{"type", std::move(type)}}),
children)
: element_builder(attributes(attrs, rel, type), children)
{ }
explicit atomLink(const std::derived_from<element> auto&... children)
: atomLink(default_rel, default_type, {}, children...)
explicit atomLink(attribute_list attrs, const is_element auto&... children)
: atomLink(default_rel, default_type, std::move(attrs), children...)
{ }
explicit atomLink(std::span<const element> children)
: atomLink(default_rel, default_type, {}, children)
explicit atomLink(attribute_list attrs, std::span<const element> children)
: atomLink(default_rel, default_type, std::move(attrs), children)
{ }
explicit atomLink(const attribute_list& attributes,
const std::derived_from<element> auto&... children)
: atomLink(default_rel, default_type, attributes, children...)
explicit atomLink(const is_element auto&... children)
: atomLink({}, children...)
{ }
explicit atomLink(const attribute_list& attributes,
std::span<const element> children)
: atomLink(default_rel, default_type, attributes, children)
explicit atomLink(std::span<const element> children)
: atomLink({}, children)
{ } };

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

@@ -9,22 +9,28 @@ namespace hemplate::sitemap class HEMPLATE_EXPORT urlset : public element_builder<"urlset", element::Type::Boolean> {
static auto attributes(std::string_view xmlns)
{
return attribute_list {
{"xmlns", xmlns},
};
}
public: static constexpr const auto default_xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
explicit urlset(std::string xmlns,
const std::derived_from<element> auto&... children)
: element_builder({{"xmlns", std::move(xmlns)}}, children...)
explicit urlset(std::string_view xmlns, const is_element auto&... children)
: element_builder(attributes(xmlns), children...)
{ }
explicit urlset(std::string xmlns, std::span<const element> children)
: element_builder({{"xmlns", std::move(xmlns)}}, children)
explicit urlset(std::string_view xmlns, std::span<const element> children)
: element_builder(attributes(xmlns), children)
{ }
explicit urlset(const std::derived_from<element> auto&... children)
explicit urlset(const is_element auto&... children)
: urlset(default_xmlns, children...) { }

@@ -35,6 +41,12 @@ public: } };
using hemplate::comment;
using hemplate::element;
using hemplate::transform;
using hemplate::transparent;
using hemplate::xml;
// clang-format off // NOLINTBEGIN *-identifier-naming using changefreq = element_builder<"changefreq", element::Type::Boolean>;

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

@@ -3,60 +3,54 @@ namespace hemplate {
attribute& attribute::append(std::string_view delim, const std::string& val)
{
if (!value.empty()) {
if (!empty()) {
value += std::string(delim);
}
value += val;
}
return *this;
}
attribute_list::attribute_list(std::initializer_list<attribute> list) { for (const auto& attr : list) {
set(attr.get_name(), attr.get_value());
set(attr);
} }
attribute_list::attribute_list(attribute attr) // NOLINT
attribute_list::attribute_list(attribute attr)
{
set(attr.get_name(), attr.get_value());
set(std::move(attr));
} bool attribute_list::empty() const {
return m_attributes.empty() && m_class.get_value().empty()
&& m_style.get_value().empty();
return m_attributes.empty() && m_class.value.empty() && m_style.value.empty();
} attribute_list& attribute_list::set(const attribute_list& list) { for (const auto& attr : list.m_attributes) {
set(attr.get_name(), attr.get_value());
set(attr);
}
set("class", list.m_class);
set("style", list.m_style);
return (*this);
}
set(m_class);
set(m_style);
attribute_list& attribute_list::set(const std::string& name)
{
if (name != "class" && name != "style") {
m_attributes.emplace_back(name);
}
return *this;
return (*this);
}
attribute_list& attribute_list::set(const std::string& name,
const std::string& value)
attribute_list& attribute_list::set(attribute attr)
{
if (name == "class") {
if (m_class.get_value().empty()) {
m_class.set_value(value);
} else {
m_class.set_value(m_class.get_value() + " " + value);
}
} else if (name == "style") {
if (m_style.get_value().empty()) {
m_style.set_value(value);
} else {
m_style.set_value(m_style.get_value() + "; " + value);
}
if (attr.name == "class") {
m_class.append(" ", attr.value);
} else if (attr.name == "style") {
m_class.append("; ", attr.value);
} else {
m_attributes.emplace_back(name, value);
m_attributes.emplace_back(std::move(attr));
} return *this;

@@ -64,24 +58,12 @@ attribute_list& attribute_list::set(const std::string& name, attribute_list attribute_list::add(const attribute_list& list) const {
attribute_list res = *this;
res.set(list);
return res;
}
attribute_list attribute_list::add(const std::string& name) const
{
attribute_list res = *this;
res.set(name);
return res;
return attribute_list(*this).set(list);
}
attribute_list attribute_list::add(const std::string& name,
const std::string& value) const
attribute_list attribute_list::add(attribute attr) const
{
attribute_list res = *this;
res.set(name, value);
return res;
return attribute_list(*this).set(std::move(attr));
} } // namespace hemplate

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

@@ -13,18 +13,6 @@ element& element::add(const element& elem) return *this; }
element& element::set(const std::string& name)
{
m_attributes.set(name);
return *this;
}
element& element::set(const std::string& name, const std::string& value)
{
m_attributes.set(name, value);
return *this;
}
void element::render_comment(std::ostream& out, std::size_t indent_value) const { const std::string indent(indent_value, ' ');

@@ -104,4 +92,26 @@ void element::render(std::ostream& out, std::size_t indent_value) const } }
element& element::set(const attribute_list& list)
{
m_attributes.set(list);
return *this;
}
element& element::set(attribute attr)
{
m_attributes.set(std::move(attr));
return *this;
}
element element::add(const attribute_list& list) const
{
return element(*this).set(list);
}
element element::add(attribute attr) const
{
return element(*this).set(std::move(attr));
}
} // namespace hemplate