summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorradhitya <alif@radhitya.org>2026-06-25 22:38:51 +0700
committerradhitya <alif@radhitya.org>2026-06-25 22:51:07 +0700
commitff69f0e9e01d7d90773b2d1c1b339a0c8e1fc806 (patch)
treed39d4093bbfff5e5fb07b2e724452dbafc020ae6 /src
parent3e5f9473f963d1c33b4d817105ff8f32e32e521a (diff)
done
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp186
1 files changed, 161 insertions, 25 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 0902b19..88a2f57 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,12 +5,24 @@
#include <fstream>
#include <cassert>
#include <cmark.h>
+#include <cstdlib>
+#include <inja/inja.hpp>
+#include <nlohmann/json.hpp>
+#include <cstring>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+#include <algorithm>
+using json = nlohmann::json;
using namespace std::string_literals;
+
struct MainConfig {
- std::string title;
+ std::string title;
std::string url;
std::string author;
+ int posts_per_page = 5;
};
struct FrontmatterConfig {
@@ -27,6 +39,14 @@ struct Config {
FrontmatterConfig frontmatter;
};
+
+struct Post {
+ std::string slug;
+ std::string title;
+ std::string date;
+ std::string body_html;
+};
+
Config
parse_config(const std::string& filepath)
{
@@ -44,6 +64,7 @@ parse_config(const std::string& filepath)
cfg.main.title = (*main)["title"].value_or("untitled's blog");
cfg.main.url = (*main)["url"].value_or("localhost");
cfg.main.author = (*main)["author"].value_or("linus");
+ cfg.main.posts_per_page = (int)(*main)["posts_per_page"].value_or(5LL);
}
return cfg;
}
@@ -61,18 +82,17 @@ frontmatter(const std::string& filepath)
? std::string::npos
: content.find("+++", first_fence + 3);
- /* TIL: npos == no position */
if (first_fence != std::string::npos && second_fence != std::string::npos) {
std::string fm = content.substr(first_fence + 3,
second_fence - (first_fence+3));
try {
toml::parse_result result = toml::parse(fm);
const toml::table& tbl = result;
- cfg.frontmatter.title = tbl["title"].value_or(""s);
- cfg.frontmatter.date = tbl["date"].value_or(""s);
- cfg.frontmatter.categories = tbl["categories"].value_or(""s);
- cfg.frontmatter.tags = tbl["tags"].value_or(""s);
- cfg.frontmatter.draft = tbl["draft"].value_or(true);
+ cfg.frontmatter.title = tbl["title"].value_or(""s);
+ cfg.frontmatter.date = tbl["date"].value_or(""s);
+ cfg.frontmatter.categories = tbl["categories"].value_or(""s);
+ cfg.frontmatter.tags = tbl["tags"].value_or(""s);
+ cfg.frontmatter.draft = tbl["draft"].value_or(true);
} catch(const toml::parse_error& err) {
std::cerr << "parsing failed: " << err << std::endl;
throw;
@@ -90,25 +110,141 @@ markdown_processing(const std::string& md) {
return html;
}
-int main(int argc, char *argv[]) {
- try {
- Config my_config = parse_config("config.toml");
-
- std::cout << "title name: " << my_config.main.title << std::endl;
- std::cout << "url name: " << my_config.main.url << std::endl;
- std::cout << "author: " << my_config.main.author << std::endl;
-
- Config front = frontmatter(argv[1]);
- std::cout << "title page: " << front.frontmatter.title << std::endl;
- std::cout << "title date: " << front.frontmatter.date << std::endl;
- std::cout << "title categories: " << front.frontmatter.categories << std::endl;
- std::cout << "title tags: " << front.frontmatter.tags << std::endl;
- std::cout << "title draft: " << front.frontmatter.draft << std::endl;
- std::cout << "title body: " << markdown_processing(front.frontmatter.body) << std::endl;
-
- } catch (const std::exception& e) {
- std::cerr << e.what() << std::endl;
+std::string
+strip_html(const std::string& html) {
+ std::string out;
+ bool in_tag = false;
+ for (char c : html) {
+ if (c == '<') in_tag = true;
+ else if (c == '>') in_tag = false;
+ else if (!in_tag) out += c;
+ }
+ return out;
+}
+
+int main() {
+ inja::Environment env;
+ nlohmann::json site;
+ system("rm -rf public");
+ if (mkdir("public", 0755) != 0) {
+ std::cerr << "cant create public" << std::endl;
return 1;
}
+ system("cp -r static/. public/");
+
+ Config my_config = parse_config("config.toml");
+ int ppp = my_config.main.posts_per_page;
+ if (ppp < 1) ppp = 5;
+
+ site["site"]["title"] = my_config.main.title;
+ site["site"]["author"] = my_config.main.author;
+ site["site"]["url"] = my_config.main.url;
+
+ std::vector<Post> posts;
+ std::string intro_html;
+
+ DIR *dir = opendir("content");
+ if (!dir) {
+ std::cerr << "cant open content/" << std::endl;
+ return 1;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(dir)) != nullptr) {
+ if (ent->d_name[0] == '.') continue;
+ const char* ext = strrchr(ent->d_name, '.');
+ if (!ext || strcmp(ext, ".md") != 0) continue;
+
+ std::string fname(ent->d_name);
+ std::string slug(fname.c_str(), ext - ent->d_name);
+ std::string fpath = "content/" + fname;
+
+ Config page = frontmatter(fpath);
+
+ if (slug == "_index") {
+ intro_html = markdown_processing(page.frontmatter.body);
+ continue;
+ }
+
+ if (page.frontmatter.draft) continue;
+
+ posts.push_back({
+ slug,
+ page.frontmatter.title.empty() ? slug : page.frontmatter.title,
+ page.frontmatter.date,
+ markdown_processing(page.frontmatter.body)
+ });
+ }
+ closedir(dir);
+
+ std::sort(posts.begin(), posts.end(),
+ [](const Post& a, const Post& b) { return a.date > b.date; });
+
+ for (const auto& p : posts) {
+ nlohmann::json data = site;
+ data["title"] = p.title;
+ data["date"] = p.date;
+ data["draft"] = false;
+ data["body"] = p.body_html;
+ std::string html = env.render_file("templates/post.html", data);
+ std::string outdir = "public/" + p.slug;
+ mkdir(outdir.c_str(), 0755);
+ std::ofstream out(outdir + "/index.html");
+ out << html;
+ }
+
+ int total = (int)posts.size();
+ int pages = total > 0 ? (total + ppp - 1) / ppp : 1;
+
+ for (int page = 1; page <= pages; page++) {
+ nlohmann::json data = site;
+ data["intro"] = intro_html;
+ json arr = json::array();
+ int start = (page - 1) * ppp;
+ int end = std::min(start + ppp, total);
+ for (int i = start; i < end; i++) {
+ arr.push_back({
+ {"title", posts[i].title},
+ {"slug", posts[i].slug},
+ {"date", posts[i].date},
+ {"excerpt", posts[i].body_html}
+ });
+ }
+ data["posts"] = arr;
+ data["page"] = page;
+ data["total_pages"] = pages;
+ data["has_prev"] = (page > 1);
+ data["has_next"] = (page < pages);
+ data["prev_url"] = (page == 2) ? "/" : ("/page/" + std::to_string(page - 1) + "/");
+ data["next_url"] = "/page/" + std::to_string(page + 1) + "/";
+
+ std::string outdir = (page == 1) ? "public" : ("public/page/" + std::to_string(page));
+ if (page > 1) system(("mkdir -p " + outdir).c_str());
+ std::string html = env.render_file("templates/index.html", data);
+ std::ofstream out(outdir + "/index.html");
+ out << html;
+ }
+
+ json search_idx = json::array();
+ for (const auto& p : posts) {
+ search_idx.push_back({
+ {"title", p.title},
+ {"slug", p.slug},
+ {"date", p.date},
+ {"text", strip_html(p.body_html)}
+ });
+ }
+ std::ofstream sj("public/search.json");
+ sj << search_idx.dump();
+
+ nlohmann::json data = site;
+ data["title"] = "Search";
+ system("mkdir -p public/search");
+ std::string html = env.render_file("templates/search.html", data);
+ std::ofstream so("public/search/index.html");
+ so << html;
+
+ std::cout << "wrote " << posts.size() << " posts, "
+ << pages << " page(s), search" << std::endl;
return 0;
}