stamd

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

commit 17873aadd3a230164ca22881e7bfb3c669d83f72
parent 4b021f8ceab0565d655f5afecf4286cd18b63f42
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Wed, 26 Jun 2024 22:20:19 +0200

General document improvements

Diffstat:
M.clang-tidy | 10++++++----
MCMakeLists.txt | 13+++----------
Msource/article.cpp | 11++++++++---
Msource/article.hpp | 4+++-
Msource/index.cpp | 52++++++++++++++++++++++++++++------------------------
Msource/index.hpp | 3++-
Msource/main.cpp | 43++++++++++++++++++++++++++++++++-----------
7 files changed, 82 insertions(+), 54 deletions(-)

diff --git a/.clang-tidy b/.clang-tidy @@ -9,17 +9,19 @@ Checks: "*,\ -*-braces-around-statements,\ -bugprone-argument-comment,\ -bugprone-easily-swappable-parameters,\ + -concurrency-mt-unsafe,\ -cppcoreguidelines-avoid-magic-numbers,\ + -fuchsia-multiple-inheritance,\ -hicpp-signed-bitwise,\ -llvm-header-guard,\ -llvm-include-order,\ + -misc-no-recursion,\ + -misc-non-private-member-variables-in-classes,\ -modernize-use-nodiscard,\ -modernize-use-trailing-return-type,\ -readability-function-cognitive-complexity,\ - -readability-magic-numbers,\ - fuchsia-multiple-inheritance,\ - -misc-no-recursion,\ - -misc-non-private-member-variables-in-classes" + -readability-magic-numbers +" WarningsAsErrors: '' CheckOptions: - key: 'bugprone-argument-comment.StrictMode' diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -4,7 +4,7 @@ include(cmake/prelude.cmake) project( stamd - VERSION 0.2.2 + VERSION 0.2.3 DESCRIPTION "Static Markdown Page Generator" HOMEPAGE_URL "https://git.dimitrijedobrota.com/stamd.git" LANGUAGES CXX @@ -15,17 +15,10 @@ include(cmake/variables.cmake) # ---- Declare dependencies ---- +find_package(md4c CONFIG REQUIRED) find_package(poafloc 1 CONFIG REQUIRED) find_package(hemplate 0.1 CONFIG REQUIRED) -include(FetchContent) - -FetchContent_Declare( - maddy - URL https://github.com/progsource/maddy/releases/download/1.3.0/maddy-src.zip -) -FetchContent_MakeAvailable(maddy) - # ---- Declare library ---- @@ -54,7 +47,7 @@ set_property(TARGET stamd_exe PROPERTY OUTPUT_NAME stamd) target_compile_features(stamd_exe PRIVATE cxx_std_20) -target_link_libraries(stamd_exe PRIVATE poafloc maddy stamd_lib) +target_link_libraries(stamd_exe PRIVATE poafloc md4c::md4c-html stamd_lib) # ---- Install rules ---- diff --git a/source/article.cpp b/source/article.cpp @@ -69,7 +69,7 @@ void article::print_categories(std::ostream& ost, ost << html::nav(); } -void article::write(const std::string& data, std::ostream& ost) +void article::write_header(std::ostream& ost) const { using namespace hemplate; // NOLINT @@ -128,8 +128,11 @@ void article::write(const std::string& data, std::ostream& ost) .set("class", "switch_label"); if (!m_categories.empty()) print_categories(ost, m_categories); +} - ost << data; +void article::write_footer(std::ostream& ost) const +{ + using namespace hemplate; // NOLINT ost << html::main(); @@ -142,7 +145,9 @@ void article::write(const std::string& data, std::ostream& ost) } ost << html::div(); - ost << html::script(" ").set("source", "/scripts/main.js"); + ost << html::script(" ") + .set("type", "text/javascript") + .set("source", "/scripts/main.js"); ost << html::body(); ost << html::html(); } diff --git a/source/article.hpp b/source/article.hpp @@ -18,7 +18,9 @@ public: { } - void write(const std::string& data, std::ostream& ost); + void write_header(std::ostream& ost) const; + void write_footer(std::ostream& ost) const; + void insert(const std::string& category) { m_categories.emplace(category); } void insert(const std::string& key, const std::string& value) { diff --git a/source/index.cpp b/source/index.cpp @@ -1,4 +1,5 @@ #include <algorithm> +#include <chrono> #include <format> #include <fstream> #include <numeric> @@ -11,33 +12,32 @@ namespace stamd { -void create_index(const std::string& name, +void create_index(std::ostream& ost, + const std::string& name, const article_list& articles, const categories_t& categories) { using namespace hemplate; // NOLINT - std::ofstream ost(name + ".html"); - std::stringstream strs; + const article index(name, categories); - strs << html::h1(name); - strs << html::ul().set("class", "index"); + index.write_header(ost); + ost << html::h1(name); + ost << html::ul().set("class", "index"); for (const auto& article : articles) { if (article->is_hidden()) continue; const auto& filename = article->get_filename(); - const auto& title = article->get_title(); + 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", filename))); + ost << html::li() + .add(html::span(std::format("{} -&nbsp", date))) + .add(html::a(title).set("href", filename)); }; - strs << html::ul(); - - article index(name, categories); - index.write(strs.str(), ost); + ost << html::ul(); + index.write_footer(ost); } void create_atom(std::ostream& ost, @@ -48,9 +48,11 @@ void create_atom(std::ostream& ost, 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..."; + auto const time = + std::chrono::current_zone()->to_local(std::chrono::system_clock::now()); + const elementList content = std::accumulate( begin(articles), end(articles), @@ -58,11 +60,13 @@ void create_atom(std::ostream& ost, [](elementList&& list, const auto& article) { const auto filename = article->get_filename(); + const auto date = article->get_date(); + list.add(atom::entry() .add(atom::title(filename)) - .add(atom::link().set( + .add(atom::link(" ").set( "href", std::format("{}/{}", base, filename))) - .add(atom::updated(updated)) + .add(atom::updated(date)) .add(atom::summary(summary))); return std::move(list); }); @@ -70,14 +74,13 @@ void create_atom(std::ostream& ost, ost << xml(); ost << atom::feed(); ost << atom::title(name); - ost << atom::link().set("href", base); - ost << atom::link({{"rel", "self"}, {"href", loc}}); + ost << atom::link(" ").set("href", base); + ost << atom::link(" ", {{"rel", "self"}, {"href", loc}}); ost << atom::id(base); - ost << atom::updated(updated); + ost << atom::updated(std::format("{:%Y-%m-%d %X}", time)); ost << atom::author().add(atom::name(name)); - ost << atom::feed(); ost << content; - ost << atom::feed(content); + ost << atom::feed(); } void create_rss(std::ostream& ost, @@ -90,8 +93,7 @@ void create_rss(std::ostream& ost, 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"; + static const char* loc = "https://dimitrijedobrota.com/blog/index.rss"; const elementList content = std::accumulate( begin(articles), @@ -100,11 +102,13 @@ void create_rss(std::ostream& ost, [](elementList&& list, const auto& article) { const auto filename = article->get_filename(); + const auto date = article->get_date(); + list.add(rss::item() .add(rss::title(filename)) .add(rss::link(std::format("{}/{}", base, filename))) .add(rss::guid(std::format("{}/{}", base, filename))) - .add(rss::pubDate(updated)) + .add(rss::pubDate(date)) .add(rss::author(std::format("{} ({})", email, author)))); return std::move(list); }); diff --git a/source/index.hpp b/source/index.hpp @@ -22,7 +22,8 @@ void create_rss(std::ostream& ost, const std::string& name, const article_list& articles); -void create_index(const std::string& name, +void create_index(std::ostream& ost, + const std::string& name, const article_list& articles, const categories_t& categories); diff --git a/source/main.cpp b/source/main.cpp @@ -3,11 +3,11 @@ #include <iostream> #include <sstream> +#include <md4c-html.h> #include <poafloc/poafloc.hpp> #include "article.hpp" #include "index.hpp" -#include "maddy/parser.h" #include "utility.hpp" void preprocess(article& article, std::istream& ist) @@ -43,7 +43,7 @@ void preprocess(article& article, std::istream& ist) struct arguments_t { - std::string output_dir = "."; + std::filesystem::path output_dir = "."; std::vector<std::filesystem::path> files; bool index = false; @@ -89,6 +89,12 @@ static const poafloc::arg_t arg { }; // NOLINTEND +void process_output(const MD_CHAR* str, MD_SIZE size, void* data) +{ + std::ofstream& ofs = *static_cast<std::ofstream*>(data); + ofs << std::string(str, size); +} + int main(int argc, char* argv[]) { using namespace stamd; // NOLINT @@ -106,7 +112,6 @@ int main(int argc, char* argv[]) category_map_t category_map; categories_t all_categories; article_list all_articles; - maddy::Parser parser; for (const auto& path : args.files) { @@ -119,8 +124,22 @@ int main(int argc, char* argv[]) preprocess(*article, ifs); // filename can change in preprocessing phase - std::ofstream ofs(article->get_filename()); - article->write(parser.Parse(ifs), ofs); + std::filesystem::path out = args.output_dir / article->get_filename(); + std::ofstream ofs(out); + std::stringstream sst; + + std::cerr << out.string() << std::endl; + + sst << ifs.rdbuf(); + + article->write_header(ofs); + md_html(sst.str().c_str(), + static_cast<MD_SIZE>(sst.str().size()), + process_output, + &ofs, + MD_DIALECT_GITHUB, + 0); + article->write_footer(ofs); if (!article->is_hidden()) { @@ -137,10 +156,11 @@ int main(int argc, char* argv[]) [](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"); - std::ofstream robots("robots.txt"); + std::ofstream atom(args.output_dir / "atom.xml"); + std::ofstream rss(args.output_dir / "rss.xml"); + std::ofstream sitemap(args.output_dir / "sitemap.xml"); + std::ofstream robots(args.output_dir / "robots.txt"); + std::ofstream index(args.output_dir / "index.html"); create_sitemap(sitemap, all_articles); create_robots(robots); @@ -148,11 +168,12 @@ int main(int argc, char* argv[]) create_rss(rss, "index", all_articles); create_atom(atom, "index", all_articles); - create_index("index", all_articles, all_categories); + create_index(index, "index", all_articles, all_categories); for (const auto& [category, articles] : category_map) { auto ctgry = category; - create_index(normalize(ctgry), articles, {}); + std::ofstream ost(args.output_dir / (normalize(ctgry) + ".html")); + create_index(ost, category, articles, {}); } return 0;