commit 4b021f8ceab0565d655f5afecf4286cd18b63f42
parent 0683d0e43044ef897c4d7dbc1cffbda2010193a9
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Wed, 26 Jun 2024 18:29:41 +0200
Consistency and stability improvements
Diffstat:
5 files changed, 131 insertions(+), 61 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)
project(
stamd
- VERSION 0.2.1
+ VERSION 0.2.2
DESCRIPTION "Static Markdown Page Generator"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/stamd.git"
LANGUAGES CXX
diff --git a/source/article.cpp b/source/article.cpp
@@ -1,4 +1,5 @@
#include <format>
+#include <iostream>
#include <numeric>
#include "article.hpp"
@@ -8,18 +9,47 @@
#include "utility.hpp"
+std::optional<std::string> article::get(const std::string& key) const
+{
+ const auto itr = m_symbols.find(key);
+ if (itr == end(m_symbols))
+ {
+ std::cerr << "Warning: getting invalid value for: " << key << std::endl;
+ return {};
+ }
+ return itr->second;
+}
+
+std::string article::get_filename() const
+{
+ return m_symbols.find("filename")->second;
+}
+
+std::string article::get_date() const
+{
+ return get("date").value_or("0000-00-00");
+}
+
+std::string article::get_title() const
+{
+ return get("title").value_or(get_filename());
+}
+
+std::string article::get_language() const
+{
+ return get("language").value_or("en");
+}
+
void article::print_nav(std::ostream& ost)
{
using namespace hemplate; // NOLINT
static const char* base = "https://dimitrijedobrota.com/blog";
- ost << html::div()
- .add(html::nav()
- .add(html::a("<-- back", {{"class", "back"}}))
- .add(html::a("index", {{"href", base}}))
- .add(html::a("hime -->", {{"href", "/"}})))
- .add(html::hr());
+ ost << html::nav()
+ .add(html::a("<-- back", {{"class", "back"}}))
+ .add(html::a("index", {{"href", base}}))
+ .add(html::a("home -->", {{"href", "/"}}));
}
void article::print_categories(std::ostream& ost,
@@ -27,20 +57,16 @@ void article::print_categories(std::ostream& ost,
{
using namespace hemplate; // NOLINT
- ost << html::div(
- attributeList({{"class", "categories"}}),
- std::accumulate(
- begin(categories),
- end(categories),
- elementList(html::h3("Categories: "), html::p()),
- [](elementList&& list, std::string ctgry)
- {
- normalize(ctgry);
- list.add(
- html::a(ctgry, {{"href", std::format("./{}.html", ctgry)}}));
- return std::move(list);
- })
- .add(html::p()));
+ ost << html::nav().set("class", "categories");
+ ost << html::h3("Categories: ");
+ ost << html::p();
+ for (auto ctgry : categories)
+ {
+ normalize(ctgry);
+ ost << html::a(ctgry, {{"href", std::format("./{}.html", ctgry)}});
+ }
+ ost << html::p();
+ ost << html::nav();
}
void article::write(const std::string& data, std::ostream& ost)
@@ -48,8 +74,10 @@ void article::write(const std::string& data, std::ostream& ost)
using namespace hemplate; // NOLINT
static const char* description_s =
- "Dimitrije Dobrota's personal site. You can find my daily findings in a "
- "form of articles on my blog as well as various programming projects.";
+ "Dimitrije Dobrota's personal site. You can find my daily "
+ "findings in a "
+ "form of articles on my blog as well as various programming "
+ "projects.";
static const attributeList icon = {{"rel", "icon"}, {"type", "image/png"}};
static const attributeList style = {{"rel", "stylesheet"},
@@ -83,21 +111,37 @@ void article::write(const std::string& data, std::ostream& ost)
.set("type", "checkbox")
.set("id", "theme_switch")
.set("class", "theme_switch");
+
+ ost << html::div().set("id", "content");
+
+ if (!m_nonav)
+ {
+ ost << html::header();
+ print_nav(ost);
+ ost << html::hr();
+ ost << html::header();
+ }
+
ost << html::main();
- ost << html::div().set("class", "content");
ost << html::label(" ")
.set("for", "theme_switch")
.set("class", "switch_label");
- if (!m_nonav) print_nav(ost);
if (!m_categories.empty()) print_categories(ost, m_categories);
ost << data;
- if (!m_nonav) print_nav(ost);
+ ost << html::main();
+
+ if (!m_nonav)
+ {
+ ost << html::footer();
+ ost << html::hr();
+ print_nav(ost);
+ ost << html::footer();
+ }
ost << html::div();
- ost << html::main();
ost << html::script(" ").set("source", "/scripts/main.js");
ost << html::body();
ost << html::html();
diff --git a/source/article.hpp b/source/article.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <optional>
#include <set>
#include <string>
#include <unordered_map>
@@ -11,17 +12,17 @@ public:
using symbols_t = std::unordered_map<std::string, std::string>;
using categories_t = std::set<std::string>;
- explicit article(std::string name, categories_t categories = {})
- : m_name(std::move(name))
- , m_categories(std::move(categories))
+ explicit article(std::string filename, categories_t categories = {})
+ : m_categories(std::move(categories))
+ , m_symbols({{"filename", filename}})
{
}
void write(const std::string& data, std::ostream& ost);
- void emplace(const std::string& category) { m_categories.emplace(category); }
- void emplace(const std::string& key, const std::string& value)
+ void insert(const std::string& category) { m_categories.emplace(category); }
+ void insert(const std::string& key, const std::string& value)
{
- m_symbols.emplace(key, value);
+ m_symbols.insert_or_assign(key, value);
}
auto get_categories() const { return m_categories; }
@@ -31,21 +32,21 @@ public:
bool is_hidden() const { return m_hidden; }
- const std::string& get_language() { return m_symbols.find("lang")->second; }
- const std::string& get_title() { return m_symbols.find("title")->second; }
- const std::string& get_date() { return m_symbols.find("date")->second; }
+ std::optional<std::string> get(const std::string& key) const;
+
+ std::string get_filename() const;
+ std::string get_date() const;
+ std::string get_title() const;
+ std::string get_language() const;
private:
static void print_nav(std::ostream& ost);
static void print_categories(std::ostream& ost,
const categories_t& categories);
- std::string m_name;
-
bool m_hidden = false;
bool m_nonav = false;
categories_t m_categories;
- symbols_t m_symbols = {
- {"title", "test"}, {"lang", "en"}, {"date", "1970-01-01"}};
+ symbols_t m_symbols;
};
diff --git a/source/index.cpp b/source/index.cpp
@@ -25,12 +25,14 @@ void create_index(const std::string& name,
for (const auto& article : articles)
{
if (article->is_hidden()) continue;
+
+ const auto& filename = article->get_filename();
const auto& title = article->get_title();
- const auto& date = article->get_date();
+ 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)));
+ .add(html::div().add(html::a(title).set("href", filename)));
};
strs << html::ul();
@@ -55,11 +57,11 @@ void create_atom(std::ostream& ost,
elementList(),
[](elementList&& list, const auto& article)
{
- const auto title = article->get_title();
+ const auto filename = article->get_filename();
list.add(atom::entry()
- .add(atom::title(title))
+ .add(atom::title(filename))
.add(atom::link().set(
- "href", std::format("{}/{}.html", base, title)))
+ "href", std::format("{}/{}", base, filename)))
.add(atom::updated(updated))
.add(atom::summary(summary)));
return std::move(list);
@@ -83,6 +85,7 @@ void create_rss(std::ostream& ost,
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";
@@ -96,11 +99,11 @@ void create_rss(std::ostream& ost,
elementList(),
[](elementList&& list, const auto& article)
{
- const auto title = article->get_title();
+ const auto filename = article->get_filename();
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::title(filename))
+ .add(rss::link(std::format("{}/{}", base, filename)))
+ .add(rss::guid(std::format("{}/{}", base, filename)))
.add(rss::pubDate(updated))
.add(rss::author(std::format("{} ({})", email, author))));
return std::move(list);
@@ -130,11 +133,11 @@ void create_sitemap(std::ostream& ost, const article_list& articles)
ost << sitemap::urlset();
for (const auto& article : articles)
{
- const auto& title = article->get_title();
- const auto& date = article->get_date();
+ const auto& name = article->get_filename();
+ const auto& date = article->get_date();
ost << sitemap::url()
- .add(sitemap::loc(std::format("{}/{}", base, title)))
+ .add(sitemap::loc(std::format("{}/{}.html", base, name)))
.add(sitemap::lastmod(date));
}
ost << sitemap::urlset();
diff --git a/source/main.cpp b/source/main.cpp
@@ -1,3 +1,4 @@
+#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
@@ -17,7 +18,7 @@ void preprocess(article& article, std::istream& ist)
while (std::getline(ist, line))
{
- if (line.empty()) continue;
+ if (line.empty()) break;
if (line[0] != '@') break;
{
@@ -31,11 +32,11 @@ void preprocess(article& article, std::istream& ist)
if (key == "hidden") article.set_hidden(true);
else if (key == "nonav") article.set_nonav(true);
- else if (key != "categories") article.emplace(key, value);
+ else if (key != "categories") article.insert(key, value);
else
{
std::istringstream iss(value);
- while (std::getline(iss, value, ',')) article.emplace(trim(value));
+ while (std::getline(iss, value, ',')) article.insert(trim(value));
}
}
}
@@ -43,7 +44,10 @@ void preprocess(article& article, std::istream& ist)
struct arguments_t
{
std::string output_dir = ".";
- std::vector<std::string> articles;
+ std::vector<std::filesystem::path> files;
+ bool index = false;
+
+ std::string base = "https://dimitrijedobrota.com/blog";
};
int parse_opt(int key, const char* arg, poafloc::Parser* parser)
@@ -54,8 +58,14 @@ int parse_opt(int key, const char* arg, poafloc::Parser* parser)
case 'o':
args->output_dir = arg;
break;
+ case 'b':
+ args->base = arg;
+ break;
+ case 'i':
+ args->index = true;
+ break;
case poafloc::ARG:
- args->articles.emplace_back(arg);
+ args->files.emplace_back(arg);
break;
default:
break;
@@ -66,6 +76,8 @@ int parse_opt(int key, const char* arg, poafloc::Parser* parser)
// NOLINTBEGIN
static const poafloc::option_t options[] = {
{"output", 'o', "DIR", 0, "Output directory"},
+ {"index", 'i', 0, 0, "Generate all of the indices"},
+ {"base", 'b', "URL", 0, "Base URL for the content"},
{0},
};
@@ -96,15 +108,18 @@ int main(int argc, char* argv[])
article_list all_articles;
maddy::Parser parser;
- for (const auto& name : args.articles)
+ for (const auto& path : args.files)
{
- std::ofstream ofs(name + ".out");
- std::ifstream ifs(name);
+ const std::string filename = path.stem().string() + ".html";
- all_articles.push_back(make_shared<article>(name));
+ std::ifstream ifs(path.string());
+ all_articles.push_back(make_shared<article>(filename));
auto& article = all_articles.back();
preprocess(*article, ifs);
+
+ // filename can change in preprocessing phase
+ std::ofstream ofs(article->get_filename());
article->write(parser.Parse(ifs), ofs);
if (!article->is_hidden())
@@ -115,6 +130,13 @@ int main(int argc, char* argv[])
}
}
+ if (!args.index) return 0;
+
+ sort(begin(all_articles),
+ end(all_articles),
+ [](const auto& lft, const auto& rht)
+ { return lft->get_date() > rht->get_date(); });
+
std::ofstream atom("atom.xml");
std::ofstream rss("rss.xml");
std::ofstream sitemap("sitemap.xml");