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