stamd

Static Markdown Page Generator
git clone git://git.dimitrijedobrota.com/stamd.git
Log | Files | Refs | README | LICENSE

commit 0683d0e43044ef897c4d7dbc1cffbda2010193a9
parent 99bcf0d96824df5e573ba76a2ac7c261133c596d
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Tue, 25 Jun 2024 21:22:35 +0200

Generate sitemap.xml and robots.txt

Diffstat:
MCMakeLists.txt | 3++-
Msource/article.cpp | 1+
Asource/index.cpp | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/index.hpp | 29+++++++++++++++++++++++++++++
Msource/main.cpp | 140+++++++++----------------------------------------------------------------------
5 files changed, 198 insertions(+), 126 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -4,7 +4,7 @@ include(cmake/prelude.cmake) project( stamd - VERSION 0.2.0 + VERSION 0.2.1 DESCRIPTION "Static Markdown Page Generator" HOMEPAGE_URL "https://git.dimitrijedobrota.com/stamd.git" LANGUAGES CXX @@ -32,6 +32,7 @@ FetchContent_MakeAvailable(maddy) add_library( stamd_lib OBJECT source/article.cpp + source/index.cpp ) target_link_libraries(stamd_lib PUBLIC hemplate::hemplate) diff --git a/source/article.cpp b/source/article.cpp @@ -62,6 +62,7 @@ void article::write(const std::string& data, std::ostream& ost) static const attributeList description = {{"name", "description"}, {"content", description_s}}; + ost << html::doctype(); ost << html::html().set("lang", get_language()); ost << html::head() .add(html::title(get_title())) diff --git a/source/index.cpp b/source/index.cpp @@ -0,0 +1,151 @@ +#include <algorithm> +#include <format> +#include <fstream> +#include <numeric> +#include <sstream> + +#include "index.hpp" + +#include <hemplate/attribute.hpp> +#include <hemplate/classes.hpp> + +namespace stamd { + +void create_index(const std::string& name, + const article_list& articles, + const categories_t& categories) +{ + using namespace hemplate; // NOLINT + + std::ofstream ost(name + ".html"); + std::stringstream strs; + + strs << html::h1(name); + strs << html::ul().set("class", "index"); + for (const auto& article : articles) + { + if (article->is_hidden()) continue; + const auto& title = article->get_title(); + const auto& date = article->get_date(); + + strs << html::li() + .add(html::div(std::format("{} - ", date))) + .add(html::div().add(html::a(title).set("href", title))); + }; + strs << html::ul(); + + article index(name, categories); + index.write(strs.str(), ost); +} + +void create_atom(std::ostream& ost, + const std::string& name, + const article_list& articles) +{ + using namespace hemplate; // NOLINT + + static const char* base = "https://dimitrijedobrota.com/blog"; + static const char* loc = "https://dimitrijedobrota.com/blog/atom.feed"; + static const char* updated = "2003-12-13T18:30:02Z"; + static const char* summary = "Click on the article link to read..."; + + const elementList content = std::accumulate( + begin(articles), + end(articles), + elementList(), + [](elementList&& list, const auto& article) + { + const auto title = article->get_title(); + list.add(atom::entry() + .add(atom::title(title)) + .add(atom::link().set( + "href", std::format("{}/{}.html", base, title))) + .add(atom::updated(updated)) + .add(atom::summary(summary))); + return std::move(list); + }); + + ost << xml(); + ost << atom::feed(); + ost << atom::title(name); + ost << atom::link().set("href", base); + ost << atom::link({{"rel", "self"}, {"href", loc}}); + ost << atom::id(base); + ost << atom::updated(updated); + ost << atom::author().add(atom::name(name)); + ost << atom::feed(); + ost << content; + ost << atom::feed(content); +} + +void create_rss(std::ostream& ost, + const std::string& name, + const article_list& articles) +{ + using namespace hemplate; // NOLINT + static const char* author = "Dimitrije Dobrota"; + static const char* email = "mail@dimitrijedobrota.com"; + static const char* base = "https://dimitrijedobrota.com/blog"; + static const char* description = "Contents of Dimitrije Dobrota's webpage"; + static const char* loc = "https://dimitrijedobrota.com/blog/index.rss"; + static const char* updated = "2003-12-13T18:30:02Z"; + + const elementList content = std::accumulate( + begin(articles), + end(articles), + elementList(), + [](elementList&& list, const auto& article) + { + const auto title = article->get_title(); + list.add(rss::item() + .add(rss::title(title)) + .add(rss::link(std::format("{}/{}.html", base, title))) + .add(rss::guid(std::format("{}/{}.html", base, title))) + .add(rss::pubDate(updated)) + .add(rss::author(std::format("{} ({})", email, author)))); + return std::move(list); + }); + + ost << xml(); + ost << rss::rss(); + ost << rss::channel(); + ost << rss::title(name); + ost << rss::link(base); + ost << rss::description(description); + ost << rss::generator("stamd"); + ost << rss::language("en-us"); + ost << rss::atomLink().set("href", loc); + ost << content; + ost << rss::channel(); + ost << rss::rss(); +} + +void create_sitemap(std::ostream& ost, const article_list& articles) +{ + using namespace hemplate; // NOLINT + + static const char* base = "https://dimitrijedobrota.com/blog"; + + ost << xml(); + ost << sitemap::urlset(); + for (const auto& article : articles) + { + const auto& title = article->get_title(); + const auto& date = article->get_date(); + + ost << sitemap::url() + .add(sitemap::loc(std::format("{}/{}", base, title))) + .add(sitemap::lastmod(date)); + } + ost << sitemap::urlset(); +} + +void create_robots(std::ostream& ost) +{ + static const char* base = "https://dimitrijedobrota.com/blog"; + + ost << "User-agent: *"; + ost << std::format("Sitemap: {}/sitemap.xml", base); +} + +} // namespace stamd diff --git a/source/index.hpp b/source/index.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <memory> +#include <string> + +#include "article.hpp" + +namespace stamd { + +using article_list = std::vector<std::shared_ptr<article>>; +using categories_t = article::categories_t; + +void create_robots(std::ostream& ost); + +void create_sitemap(std::ostream& ost, const article_list& articles); + +void create_atom(std::ostream& ost, + const std::string& name, + const article_list& articles); + +void create_rss(std::ostream& ost, + const std::string& name, + const article_list& articles); + +void create_index(const std::string& name, + const article_list& articles, + const categories_t& categories); + +} // namespace stamd diff --git a/source/main.cpp b/source/main.cpp @@ -1,22 +1,14 @@ -#include <algorithm> -#include <format> #include <fstream> #include <iostream> -#include <memory> -#include <numeric> #include <sstream> -#include <hemplate/attribute.hpp> -#include <hemplate/classes.hpp> #include <poafloc/poafloc.hpp> #include "article.hpp" +#include "index.hpp" #include "maddy/parser.h" #include "utility.hpp" -using article_list = std::vector<std::shared_ptr<article>>; -using categories_t = article::categories_t; - void preprocess(article& article, std::istream& ist) { std::string line; @@ -48,118 +40,6 @@ void preprocess(article& article, std::istream& ist) } } -void create_index(const std::string& name, - const article_list& articles, - const categories_t& categories) -{ - using namespace hemplate; // NOLINT - - std::ofstream ost(name + ".html"); - std::stringstream strs; - - strs << html::h1(name); - strs << html::ul().set("class", "index"); - for (const auto& article : articles) - { - if (article->is_hidden()) continue; - const auto& title = article->get_title(); - const auto& date = article->get_date(); - - strs << html::li() - .add(html::div(std::format("{} - ", date))) - .add(html::div().add(html::a(title).set("href", title))); - }; - strs << html::ul(); - - article index(name, categories); - index.write(strs.str(), ost); -} - -void create_atom(const std::string& name, const article_list& articles) -{ - using namespace hemplate; // NOLINT - - static const char* base = "https://dimitrijedobrota.com/blog"; - static const char* loc = "https://dimitrijedobrota.com/blog/atom.feed"; - static const char* updated = "2003-12-13T18:30:02Z"; - static const char* summary = "Click on the article link to read..."; - - std::ofstream ost(name + ".atom"); - - elementList content = std::accumulate( - begin(articles), - end(articles), - elementList(), - [](elementList&& list, const auto& article) - { - const auto title = article->get_title(); - list.add(atom::entry() - .add(atom::title(title)) - .add(atom::link().set( - "href", std::format("{}/{}.html", base, title))) - .add(atom::updated(updated)) - .add(atom::summary(summary))); - return std::move(list); - }); - - ost << atom::xml({{"version", "1.0"}, {"encoding", "utf-8"}}); - ost << atom::feed(); - ost << atom::title(name); - ost << atom::link().set("href", base); - ost << atom::link({{"rel", "self"}, {"href", loc}}); - ost << atom::id(base); - ost << atom::updated(updated); - ost << atom::author().add(atom::name(name)); - ost << atom::feed(); - ost << content; - ost << atom::feed(content); -} - -void create_rss(const std::string& name, const article_list& articles) -{ - using namespace hemplate; // NOLINT - - std::ofstream ost(name + ".rss"); - - static const char* author = "Dimitrije Dobrota"; - static const char* email = "mail@dimitrijedobrota.com"; - static const char* base = "https://dimitrijedobrota.com/blog"; - static const char* description = "Contents of Dimitrije Dobrota's webpage"; - static const char* loc = "https://dimitrijedobrota.com/blog/index.rss"; - static const char* updated = "2003-12-13T18:30:02Z"; - - elementList content = std::accumulate( - begin(articles), - end(articles), - elementList(), - [](elementList&& list, const auto& article) - { - const auto title = article->get_title(); - list.add(rss::item() - .add(rss::title(title)) - .add(rss::link(std::format("{}/{}.html", base, title))) - .add(rss::guid(std::format("{}/{}.html", base, title))) - .add(rss::pubDate(updated)) - .add(rss::author(std::format("{} ({})", email, author)))); - return std::move(list); - }); - - ost << rss::xml({{"version", "1.0"}, {"encoding", "utf-8"}}); - ost << rss::rss( - {{"version", "2.0"}, {"xmlns:atom", "http://www.w3.org/2005/Atom"}}); - ost << rss::channel(); - ost << rss::title(name); - ost << rss::link(base); - ost << rss::description(description); - ost << rss::generator("stamd"); - ost << rss::language("en-us"); - ost << rss::atomLink( - {{"href", loc}, {"rel", "self"}, {"type", "application/rss+xml"}}); - ost << content; - ost << rss::channel(); - ost << rss::rss(); -} - struct arguments_t { std::string output_dir = "."; @@ -177,8 +57,7 @@ int parse_opt(int key, const char* arg, poafloc::Parser* parser) case poafloc::ARG: args->articles.emplace_back(arg); break; - defaut: - poafloc::help(parser, stderr, poafloc::STD_USAGE); + default: break; } return 0; @@ -200,6 +79,8 @@ static const poafloc::arg_t arg { int main(int argc, char* argv[]) { + using namespace stamd; // NOLINT + using category_map_t = std::unordered_map<std::string, article_list>; arguments_t args; @@ -234,8 +115,17 @@ int main(int argc, char* argv[]) } } - create_rss("index", all_articles); - create_atom("index", all_articles); + std::ofstream atom("atom.xml"); + std::ofstream rss("rss.xml"); + std::ofstream sitemap("sitemap.xml"); + std::ofstream robots("robots.txt"); + + create_sitemap(sitemap, all_articles); + create_robots(robots); + + create_rss(rss, "index", all_articles); + create_atom(atom, "index", all_articles); + create_index("index", all_articles, all_categories); for (const auto& [category, articles] : category_map) {