stamd

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

indexer.cpp (4602B)


      1 #include <algorithm>
      2 #include <ctime>
      3 #include <format>
      4 #include <iomanip>
      5 #include <iterator>
      6 #include <ostream>
      7 #include <sstream>
      8 #include <string>
      9 
     10 #include "indexer.hpp"
     11 
     12 #include <hemplate/attribute.hpp>
     13 #include <hemplate/classes.hpp>
     14 
     15 #include "article.hpp"
     16 
     17 namespace stamd {
     18 
     19 void Indexer::add(const article_s& article)
     20 {
     21   m_articles.emplace_back(article);
     22 }
     23 
     24 void Indexer::add(categories_t categories)
     25 {
     26   m_categories.merge(categories);
     27 }
     28 
     29 void Indexer::sort()
     30 {
     31   std::sort(begin(m_articles),
     32             end(m_articles),
     33             [](const auto& lft, const auto& rht)
     34             { return lft->get_date() > rht->get_date(); });
     35 }
     36 
     37 int64_t parse_time(const std::string& date)
     38 {
     39   std::tm tms = {};
     40   std::stringstream stream(date);
     41   stream >> std::get_time(&tms, "%Y-%m-%d");
     42   return std::mktime(&tms);
     43 }
     44 
     45 void Indexer::create_index(std::ostream& ost, const std::string& name)
     46 {
     47   using namespace hemplate;  // NOLINT
     48 
     49   const Article index(name, m_options, m_categories);
     50 
     51   index.write_header(ost);
     52   ost << html::h1(name);
     53   ost << html::ul().set("class", "index");
     54   for (const auto& article : m_articles)
     55   {
     56     if (article->is_hidden()) continue;
     57 
     58     const auto& filename = article->get_filename();
     59     const auto& title    = article->get_title();
     60     const auto& date     = article->get_date();
     61 
     62     ost << html::li()
     63                .add(html::span(date + " -&nbsp"))
     64                .add(html::a(title).set("href", filename));
     65   };
     66   ost << html::ul();
     67   index.write_footer(ost);
     68 }
     69 
     70 void Indexer::create_atom(std::ostream& ost, const std::string& name) const
     71 {
     72   using namespace hemplate;  // NOLINT
     73 
     74   const std::string& base_url = m_options.base_url;
     75 
     76   ost << xml();
     77   ost << atom::feed();
     78   ost << atom::title(name);
     79   ost << atom::id(base_url);
     80   ost << atom::updated(atom::format_time_now());
     81   ost << atom::author().add(atom::name(name));
     82   ost << atom::link(" ",
     83                     {{"rel", "self"}, {"href", base_url + "blog/atom.xml"}});
     84   ost << atom::link(
     85       " ", {{"href", base_url}, {"rel", "alternate"}, {"type", "text/html"}});
     86 
     87   for (const auto& article : m_articles)
     88   {
     89     const auto filename = article->get_filename();
     90     const auto title    = article->get_title();
     91     const auto date     = article->get_date();
     92     const auto summary  = article->get("summary").value_or(m_options.summary);
     93 
     94     ost << atom::entry()
     95                .add(atom::title(title))
     96                .add(atom::id(base_url + filename))
     97                .add(atom::link(" ").set("href", base_url + filename))
     98                .add(atom::updated(atom::format_time(parse_time((date)))))
     99                .add(atom::summary(summary));
    100   }
    101 
    102   ost << atom::feed();
    103 }
    104 
    105 void Indexer::create_rss(std::ostream& ost, const std::string& name) const
    106 {
    107   using namespace hemplate;  // NOLINT
    108 
    109   const std::string& base_url    = m_options.base_url;
    110   const std::string& description = m_options.description;
    111 
    112   ost << xml();
    113   ost << rss::rss();
    114   ost << rss::channel();
    115 
    116   ost << rss::title(name);
    117   ost << rss::link(base_url);
    118   ost << rss::description(description);
    119   ost << rss::generator("stamd");
    120   ost << rss::language("en-us");
    121   ost << rss::atomLink().set("href", base_url + "blog/rss.xml");
    122 
    123   for (const auto& article : m_articles)
    124   {
    125     const auto filename = article->get_filename();
    126     const auto date     = article->get_date();
    127     const auto author   = article->get("author").value_or(m_options.author);
    128     const auto email    = article->get("email").value_or(m_options.email);
    129 
    130     ost << rss::item()
    131                .add(rss::title(filename))
    132                .add(rss::link(base_url + filename))
    133                .add(rss::guid(base_url + filename))
    134                .add(rss::pubDate(rss::format_time(parse_time(date))))
    135                .add(rss::author(std::format("{} ({})", email, author)));
    136   }
    137 
    138   ost << rss::channel();
    139   ost << rss::rss();
    140 }
    141 
    142 void Indexer::create_sitemap(std::ostream& ost) const
    143 {
    144   using namespace hemplate;  // NOLINT
    145 
    146   static const std::string& base_url = m_options.base_url;
    147 
    148   ost << xml();
    149   ost << sitemap::urlset();
    150   for (const auto& article : m_articles)
    151   {
    152     const auto& filename = article->get_filename();
    153     const auto& date     = article->get_date();
    154 
    155     ost << sitemap::url()
    156                .add(sitemap::loc(base_url + filename))
    157                .add(sitemap::lastmod(date));
    158   }
    159   ost << sitemap::urlset();
    160 }
    161 
    162 void Indexer::create_robots(std::ostream& ost) const
    163 {
    164   static const std::string& base_url = m_options.base_url;
    165 
    166   ost << "User-agent: *";
    167   ost << std::format("Sitemap: {}/sitemap.xml", base_url);
    168 }
    169 
    170 }  // namespace stamd