From 19d86e8dc7d057481055e3d98d1e9fa0b322f0d0 Mon Sep 17 00:00:00 2001 From: Szymon P Date: Mon, 12 Jan 2026 12:52:19 +0100 Subject: [PATCH 1/4] made formatting files work --- src/format/format.cpp | 99 +++++++++++++++++-------------------------- src/format/format.hpp | 8 ++-- src/tests.cpp | 58 +++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 62 deletions(-) diff --git a/src/format/format.cpp b/src/format/format.cpp index afb99f8..1be57dc 100644 --- a/src/format/format.cpp +++ b/src/format/format.cpp @@ -1,31 +1,7 @@ #include "format/format.hpp" #include "markdown/markdown.hpp" -#include #include -// Replace TEMPLATE with REPLACMENT in-place -static bool replace_once_global(std::string &STR, const std::string &TEMPLATE, - const std::string &REPLACMENT) { - size_t start_pos = STR.find(TEMPLATE); - if (start_pos == std::string::npos) { - return false; - } - STR.replace(start_pos, TEMPLATE.length(), REPLACMENT); - return true; -} - -// Replace TEMPLATE with REPLACMENT between starting from `start` -static bool replace_once_between(std::string &STR, const std::string &TEMPLATE, - const std::string &REPLACMENT, - const size_t &START) { - size_t start_pos = STR.find(TEMPLATE, START); - if (start_pos == std::string::npos) { - return false; - } - STR.replace(start_pos, TEMPLATE.length(), REPLACMENT); - return true; -} - // Remove everything from START to STOP static bool remove_from_to(std::string &STR, const std::string &START, const std::string &STOP) { @@ -45,46 +21,51 @@ static bool remove_from_to(std::string &STR, const std::string &START, std::string format_file(const std::string &TEMPLATE_FILE, const std::string &TEMPLATE, const std::string &INPUT) { - bool is_template = TEMPLATE_FILE.find(TEMPLATE) != std::string::npos; std::string result = TEMPLATE_FILE; + size_t current_pos = 0; - if (!is_template) { - std::clog << "There is no template in given file. Is this correct?" - << std::endl; - return result; - } + while (true) { + size_t found_pos = result.find(TEMPLATE, current_pos); - while (is_template) { - is_template = replace_once_global(result, TEMPLATE, INPUT); - } - return result; -} - -std::string format_file_condtionally(const std::string &TEMPLATE_FILE, - const std::string &TEMPLATE, - const std::string &INPUT) { - - size_t template_pos = TEMPLATE_FILE.find(TEMPLATE); - bool is_template = template_pos != std::string::npos; - - std::string result = TEMPLATE_FILE; - - if (!is_template) { - std::clog << "There is no template in given file. Is this correct?" - << std::endl; - return result; - } - - // if template is empty then we remove everything in between - if (TEMPLATE == "") { - while (is_template) { - is_template = remove_from_to(result, TEMPLATE, "{{endif}"); + if (found_pos == std::string::npos) { + break; } - } - while (is_template) { - is_template = - replace_once_between(result, TEMPLATE, INPUT, result.find(TEMPLATE)); + result.replace(found_pos, TEMPLATE.length(), INPUT); + current_pos = found_pos + INPUT.length(); } return result; } + +std::string format_file_conditionally(const std::string &TEMPLATE_FILE, + const std::string &START_TAG, + const std::string &END_TAG, + const std::string &TEMPLATE, + const std::string &INPUT) { + std::string result = TEMPLATE_FILE; + + if (INPUT.empty()) { + while (true) { + if (!remove_from_to(result, START_TAG, END_TAG)) { + break; + } + } + } else { + // remove START tag + size_t pos = 0; + while ((pos = result.find(START_TAG, pos)) != std::string::npos) { + result.replace(pos, START_TAG.length(), ""); + } + + // remove END tag + pos = 0; + while ((pos = result.find(END_TAG, pos)) != std::string::npos) { + result.replace(pos, END_TAG.length(), ""); + } + + // replace + result = format_file(result, TEMPLATE, INPUT); + } + + return result; +} diff --git a/src/format/format.hpp b/src/format/format.hpp index 8f04037..78bf316 100644 --- a/src/format/format.hpp +++ b/src/format/format.hpp @@ -14,6 +14,8 @@ std::string format_file(const std::string &TEMPLATE_FILE, // @param TEMPLATE_FILE File that you want to format in string. // @param TEMPLATE Full name of the if statement. For example `{{if title}}` // @return Formatted file in std::string -std::string format_file_condtionally(const std::string &TEMPLATE_FILE, - const std::string &TEMPLATE, - const std::string &INPUT); +std::string format_file_conditionally(const std::string &TEMPLATE_FILE, + const std::string &START_TAG, + const std::string &END_TAG, + const std::string &TEMPLATE, + const std::string &INPUT); diff --git a/src/tests.cpp b/src/tests.cpp index 8f82fde..c2bc76d 100644 --- a/src/tests.cpp +++ b/src/tests.cpp @@ -43,6 +43,46 @@ constexpr const char *formated_html = R"( )"; +constexpr const char *formatter_html_conditionally = R"( + + + + + Document + + +{{if tag}} +

{{tag}}

+{{endif}} + +)"; + +constexpr const char *formated_html_conditionally = R"( + + + + + Document + + + +

tag

+ + +)"; + +constexpr const char *formated_html_conditionally_empty = R"( + + + + + Document + + + + +)"; + constexpr const char *no_metadata_post_md = R"(# This markdown has no metadata. That's bad. Don't do it. It's for tests only.)"; @@ -183,3 +223,21 @@ TEST_CASE("Test format_file function with correct data") { "{{replace_me}}", "I'm formatted!")); } + +TEST_CASE("Test format_file function with no templates") { + REQUIRE(TestFiles::formated_html == + format_file(TestFiles::formated_html, "{{replace_me}}", "nothing")); +} + +TEST_CASE("Test format_file_conditionally with tag") { + REQUIRE(TestFiles::formated_html_conditionally == + format_file_conditionally(TestFiles::formatter_html_conditionally, + "{{if tag}}", "{{endif}}", "{{tag}}", + "tag")); +} + +TEST_CASE("Test format_file_conditionally with empty tag") { + REQUIRE(TestFiles::formated_html_conditionally_empty == + format_file_conditionally(TestFiles::formatter_html_conditionally, + "{{if tag}}", "{{endif}}", "{{tag}}", "")); +} From 3b676a8c49cdec3464012bd86242b27398a2e21b Mon Sep 17 00:00:00 2001 From: Szymon P Date: Mon, 12 Jan 2026 12:58:57 +0100 Subject: [PATCH 2/4] update makefile --- src/makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/makefile b/src/makefile index fa8f076..0d73592 100644 --- a/src/makefile +++ b/src/makefile @@ -31,6 +31,7 @@ all: directories $(TARGET) directories: @mkdir -p $(BUILD_DIR)/config @mkdir -p $(BUILD_DIR)/format + @mkdir -p $(BUILD_DIR)/io @mkdir -p $(BUILD_DIR)/markdown $(TARGET): $(OBJECTS) From e542115cb827293fc8f8e897d2c7195b440f157a Mon Sep 17 00:00:00 2001 From: Szymon P Date: Mon, 12 Jan 2026 22:24:50 +0100 Subject: [PATCH 3/4] created builder files --- src/builder/builder.cpp | 32 ++++++++++++++++++++++++++++++++ src/builder/builder.hpp | 7 +++++++ src/makefile | 7 +++++-- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/builder/builder.cpp create mode 100644 src/builder/builder.hpp diff --git a/src/builder/builder.cpp b/src/builder/builder.cpp new file mode 100644 index 0000000..b089c72 --- /dev/null +++ b/src/builder/builder.cpp @@ -0,0 +1,32 @@ +#include "builder/builder.hpp" +#include "config/config.hpp" +#include "format/format.hpp" + +std::string build_header(const std::string &TEMPLATE, + const Information &INFORMATION) { + std::string result = + format_file(TEMPLATE, "{{title}}", INFORMATION.metadata.title); + ; + + return result; +} + +std::string build_head(const std::string &TEMPLATE, + const Information &INFORMATION) { + std::string result = + format_file(TEMPLATE, "{{title}}", INFORMATION.metadata.title); + return result; +} + +std::string build_footer(const std::string &TEMPLATE, + const Information &INFORMATION) { + std::string result = TEMPLATE; + result = + format_file_conditionally(TEMPLATE, "{{if email}}", "{{endif}}", + "{{email}}", INFORMATION.config.contact.email); + + result = format_file_conditionally(TEMPLATE, "{{if signal}}", "{{endif}}", + "{{signal}}", + INFORMATION.config.contact.signal); + return result; +} diff --git a/src/builder/builder.hpp b/src/builder/builder.hpp new file mode 100644 index 0000000..6cf27f6 --- /dev/null +++ b/src/builder/builder.hpp @@ -0,0 +1,7 @@ +#include "config/config.hpp" +#include "markdown/markdown.hpp" + +struct Information { + Config config; + Metadata metadata; +}; diff --git a/src/makefile b/src/makefile index 0d73592..5d76770 100644 --- a/src/makefile +++ b/src/makefile @@ -9,7 +9,8 @@ SOURCES = main.cpp \ config/config.cpp \ format/format.cpp \ markdown/markdown.cpp \ - io/io.cpp + io/io.cpp \ + builder/builder.cpp OBJECTS = $(SOURCES:%.cpp=$(BUILD_DIR)/%.o) @@ -17,7 +18,8 @@ TEST_SOURCES = tests.cpp \ config/config.cpp \ format/format.cpp \ markdown/markdown.cpp \ - io/io.cpp + io/io.cpp \ + builder/builder.cpp TEST_OBJECTS = $(TEST_SOURCES:%.cpp=$(BUILD_DIR)/%.o) @@ -33,6 +35,7 @@ directories: @mkdir -p $(BUILD_DIR)/format @mkdir -p $(BUILD_DIR)/io @mkdir -p $(BUILD_DIR)/markdown + @mkdir -p $(BUILD_DIR)/builder $(TARGET): $(OBJECTS) $(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS) From a89e4bb60ffd16b4d5068a83393751a40f25d2a9 Mon Sep 17 00:00:00 2001 From: Szymon P Date: Mon, 12 Jan 2026 23:04:37 +0100 Subject: [PATCH 4/4] added build_index() function --- README.md | 4 ++- src/builder/builder.cpp | 15 +++++++++-- src/builder/builder.hpp | 15 +++++++++++ src/markdown/markdown.hpp | 1 + src/tests.cpp | 52 ++++++++++++++++++++++++++++++++++++++- templates/index.html | 13 +++++++--- 6 files changed, 92 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5200a3e..15a31ee 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,12 @@ As this is still a WIP, here is a short list of what works and what still needs - [x] metadata parsing - [x] basic tests - [x] parsing markdown -- [ ] generating html documents +- [x] generating html documents - [ ] some nicer css styles - [ ] more sensible templates - [x] change yaml style metadata to toml +- [ ] cli +- [ ] perhaps someday gui? # What is this? This is a small blog app. Basically a static site generator. diff --git a/src/builder/builder.cpp b/src/builder/builder.cpp index b089c72..8216505 100644 --- a/src/builder/builder.cpp +++ b/src/builder/builder.cpp @@ -2,6 +2,17 @@ #include "config/config.hpp" #include "format/format.hpp" +std::string build_index(const std::string &TEMPLATE, const Page &PAGE, + const Information &INFORMATION) { + std::string result = TEMPLATE; + result = format_file(result, "{{lang}}", INFORMATION.config.general.lang); + result = format_file(result, "{{head}}", PAGE.head); + result = format_file(result, "{{header}}", PAGE.header); + result = format_file(result, "{{main}}", PAGE.main); + result = format_file(result, "{{footer}}", PAGE.footer); + return result; +} + std::string build_header(const std::string &TEMPLATE, const Information &INFORMATION) { std::string result = @@ -22,10 +33,10 @@ std::string build_footer(const std::string &TEMPLATE, const Information &INFORMATION) { std::string result = TEMPLATE; result = - format_file_conditionally(TEMPLATE, "{{if email}}", "{{endif}}", + format_file_conditionally(result, "{{if email}}", "{{endif}}", "{{email}}", INFORMATION.config.contact.email); - result = format_file_conditionally(TEMPLATE, "{{if signal}}", "{{endif}}", + result = format_file_conditionally(result, "{{if signal}}", "{{endif}}", "{{signal}}", INFORMATION.config.contact.signal); return result; diff --git a/src/builder/builder.hpp b/src/builder/builder.hpp index 6cf27f6..08c2373 100644 --- a/src/builder/builder.hpp +++ b/src/builder/builder.hpp @@ -1,7 +1,22 @@ #include "config/config.hpp" #include "markdown/markdown.hpp" +struct Page { + std::string head; + std::string header; + std::string main; + std::string footer; +}; + struct Information { Config config; Metadata metadata; }; + +// @brief Function that builds index.html. It takes all the information from +// `Page` struct that should be built before calling it. +// @param TEMPLATE Template file that will be used to build index +// @param PAGE Page struct with information on html tags +// @return Built index.html in string +std::string build_index(const std::string &TEMPLATE, const Page &PAGE, + const Information &INFORMATION); diff --git a/src/markdown/markdown.hpp b/src/markdown/markdown.hpp index cff04b1..d55cbe1 100644 --- a/src/markdown/markdown.hpp +++ b/src/markdown/markdown.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/src/tests.cpp b/src/tests.cpp index c2bc76d..4d61b81 100644 --- a/src/tests.cpp +++ b/src/tests.cpp @@ -1,3 +1,4 @@ +#include "builder/builder.hpp" #include "config/config.hpp" #include "format/format.hpp" #include "io/io.hpp" @@ -128,6 +129,21 @@ This [should be clickable](https://example.com) - nine - six - nine)"; + +constexpr const char *template_index = R"( + + + {{head}} + + +
{{header}}
+
+ {{main}} +
+ {{footer}} + +)"; + } // namespace TestFiles // IO tests @@ -158,7 +174,7 @@ TEST_CASE("get_metadata function data correctness test") { Metadata result = get_metadata(TestFiles::post_md).value(); std::string expected_author = "joseph"; - struct tm datetime = {0}; + struct tm datetime = {}; datetime.tm_year = 2026 - 1900; datetime.tm_mon = 1 - 1; datetime.tm_mday = 1; @@ -241,3 +257,37 @@ TEST_CASE("Test format_file_conditionally with empty tag") { format_file_conditionally(TestFiles::formatter_html_conditionally, "{{if tag}}", "{{endif}}", "{{tag}}", "")); } + +// Builder tests +TEST_CASE("build_index correctly assembles the page", "[builder]") { + Config config; + config.general.lang = "pl"; + Metadata metadata; + + Information info; + info.config = config; + info.metadata = metadata; + + Page page; + page.head = "Test"; + page.header = ""; + page.main = "
Treść wpisu
"; + page.footer = "
Stopka
"; + + std::string expected_output = R"( + + + Test + + +
+
+
Treść wpisu
+
+
Stopka
+ +)"; + + std::string result = build_index(TestFiles::template_index, page, info); + REQUIRE(result == expected_output); +} diff --git a/templates/index.html b/templates/index.html index aed8988..ae4a9bd 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,13 +1,18 @@ + {{head}} + +
{{header}} - +
-
    - {{posts}} -
+ {{main}}
+ +