stamd

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

article.cpp (5273B)


      1 #include <format>
      2 #include <iostream>
      3 #include <iterator>
      4 #include <numeric>
      5 #include <optional>
      6 #include <string>
      7 
      8 #include "article.hpp"
      9 
     10 #include <hemplate/attribute.hpp>
     11 #include <hemplate/classes.hpp>
     12 
     13 #include "utility.hpp"
     14 
     15 namespace stamd {
     16 
     17 std::optional<std::string> Article::get(const std::string& key) const
     18 {
     19   const auto itr = m_symbols.find(key);
     20   if (itr == end(m_symbols))
     21   {
     22     // std::cerr << "Warning: getting invalid value for: " << key << std::endl;
     23     return {};
     24   }
     25   return itr->second;
     26 }
     27 
     28 std::string Article::get_filename() const
     29 {
     30   return m_filename;
     31 }
     32 
     33 std::string Article::get_date() const
     34 {
     35   return get("date").value_or("0000-00-00");
     36 }
     37 
     38 std::string Article::get_title() const
     39 {
     40   return get("title").value_or(get_filename());
     41 }
     42 
     43 std::string Article::get_language() const
     44 {
     45   return get("language").value_or("en");
     46 }
     47 
     48 std::string Article::get_desciprtion() const
     49 {
     50   return get("description").value_or(m_options.description);
     51 }
     52 
     53 std::string Article::get_author() const
     54 {
     55   return get("author").value_or(m_options.author);
     56 }
     57 
     58 std::string Article::get_keywords() const
     59 {
     60   static const auto concat = [](const categories_t& categories)
     61   {
     62     if (categories.empty()) return std::string();
     63     return std::accumulate(std::next(std::begin(categories)),
     64                            std::end(categories),
     65                            *categories.begin(),
     66                            [](const auto& acc, const auto& str)
     67                            { return acc + ", " + str; });
     68   };
     69 
     70   return get("keywords").value_or(concat(m_categories));
     71 }
     72 
     73 void Article::print_nav(std::ostream& ost, const std::string& base)
     74 {
     75   using namespace hemplate;  // NOLINT
     76 
     77   ost << html::nav()
     78              .add(html::a("&lt;-- back", {{"class", "back"}}))
     79              .add(html::a("index", {{"href", base}}))
     80              .add(html::a("home --&gt;", {{"href", "/"}}));
     81 }
     82 
     83 void Article::print_categories(std::ostream& ost,
     84                                const categories_t& categories)
     85 {
     86   using namespace hemplate;  // NOLINT
     87 
     88   ost << html::nav().set("class", "categories");
     89   ost << html::h3("Categories: ");
     90   ost << html::p();
     91   for (const auto& category : categories)
     92   {
     93     auto ctgry = category;
     94     normalize(ctgry);
     95     ost << html::a(category, {{"href", std::format("./{}.html", ctgry)}});
     96   }
     97   ost << html::p();
     98   ost << html::nav();
     99 }
    100 
    101 void Article::write_header(std::ostream& ost) const
    102 {
    103   using namespace hemplate;  // NOLINT
    104 
    105   ost << html::doctype();
    106   ost << html::html().set("lang", get_language());
    107   ost << html::head()
    108              .add(html::title(get_title()))
    109              // Meta tags
    110              .add(html::meta({{"charset", "UTF-8"}}))
    111              .add(html::meta({{"name", "author"}, {"content", get_author()}}))
    112              .add(html::meta(
    113                  {{"name", "description"}, {"content", get_desciprtion()}}))
    114              .add(html::meta(
    115                  {{"name", "keywords"}, {"content", get_keywords()}}))
    116              .add(html::meta(
    117                  {{"content", "width=device-width, initial-scale=1"},
    118                   {"name", "viewport"}}))
    119              // Stylesheets
    120              .add(html::link({{"rel", "stylesheet"}, {"type", "text/css"}})
    121                       .set("href", "/css/index.css"))
    122              .add(html::link({{"rel", "stylesheet"}, {"type", "text/css"}})
    123                       .set("href", "/css/colors.css"))
    124              // Rss feed
    125              .add(html::link({{"rel", "alternate"},
    126                               {"type", "application/atom+xml"},
    127                               {"title", "RSS feed"},
    128                               {"href", "/blog/rss.xml"}}))
    129              // Atom feed
    130              .add(html::link({{"rel", "alternate"},
    131                               {"type", "application/atom+xml"},
    132                               {"title", "Atom feed"},
    133                               {"href", "/blog/atom.xml"}}))
    134              // Icons
    135              .add(html::link({{"rel", "icon"}, {"type", "image/png"}})
    136                       .set("sizes", "32x32")
    137                       .set("href", "/img/favicon-32x32.png"))
    138              .add(html::link({{"rel", "icon"}, {"type", "image/png"}})
    139                       .set("sizes", "16x16")
    140                       .set("href", "/img/favicon-16x16.png"));
    141 
    142   ost << html::body();
    143   ost << html::input()
    144              .set("type", "checkbox")
    145              .set("id", "theme_switch")
    146              .set("class", "theme_switch");
    147 
    148   ost << html::div().set("id", "content");
    149 
    150   if (!m_nonav)
    151   {
    152     ost << html::header();
    153     print_nav(ost, m_options.base_url + "blog");
    154     ost << html::hr();
    155     ost << html::header();
    156   }
    157 
    158   ost << html::main();
    159   ost << html::label(" ")
    160              .set("for", "theme_switch")
    161              .set("class", "switch_label");
    162 
    163   if (!m_categories.empty()) print_categories(ost, m_categories);
    164 }
    165 
    166 void Article::write_footer(std::ostream& ost) const
    167 {
    168   using namespace hemplate;  // NOLINT
    169 
    170   ost << html::main();
    171 
    172   if (!m_nonav)
    173   {
    174     ost << html::footer();
    175     ost << html::hr();
    176     print_nav(ost, m_options.base_url + "blog");
    177     ost << html::footer();
    178   }
    179 
    180   ost << html::div();
    181   ost << html::script(" ").set("src", "/scripts/main.js");
    182   ost << html::body();
    183   ost << html::html();
    184 }
    185 
    186 }  // namespace stamd