Start adding markdown conversion to site generator.

This commit is contained in:
James Grogan 2022-12-05 13:16:10 +00:00
parent f0091f9e04
commit f44c79dc1f
31 changed files with 692 additions and 461 deletions

View file

@ -2,19 +2,24 @@
list(APPEND website_generator_LIB_INCLUDES list(APPEND website_generator_LIB_INCLUDES
ContentFile.h ContentFile.h
ContentFile.cpp ContentFile.cpp
ContentPage.h
ContentPage.cpp
MarkdownContentParser.h MarkdownContentParser.h
MarkdownContentParser.cpp MarkdownContentParser.cpp
SiteGeneratorConfig.h
SiteGeneratorConfig.cpp
WebsiteGenerator.h WebsiteGenerator.h
WebsiteGenerator.cpp) WebsiteGenerator.cpp
)
add_executable(website_generator main.cpp ${website_generator_LIB_INCLUDES}) add_executable(website_generator main.cpp ${website_generator_LIB_INCLUDES})
target_include_directories(website_generator PUBLIC target_include_directories(website_generator PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_CURRENT_SOURCE_DIR}
) )
target_link_libraries(website_generator PUBLIC core web) target_link_libraries(website_generator PUBLIC core web compiler)
set_property(TARGET website_generator PROPERTY FOLDER apps/website-generator) set_property(TARGET website_generator PROPERTY FOLDER apps/website-generator)

View file

@ -0,0 +1,54 @@
#include "ContentFile.h"
#include "PathUtils.h"
#include "MarkdownDocument.h"
#include "MarkdownElement.h"
ContentFile::ContentFile(const Path& filename)
: mFilename(filename)
{
}
ContentFile::~ContentFile()
{
}
Path ContentFile::getFilename() const
{
return mFilename;
}
std::string ContentFile::getOutputLocation() const
{
const auto metadata_item = getMetadataItem("save_as");
return metadata_item.empty() ? PathUtils::getBaseFilename(mFilename) : metadata_item;
}
void ContentFile::load()
{
MarkdownContentParser parser;
auto result = parser.run(mFilename);
mMetadata = result.first;
mContentBody = std::move(result.second);
}
std::string ContentFile::getMetadataItem(const std::string& key) const
{
const auto check = mMetadata.find(key);
if (check == mMetadata.end())
{
return {};
}
else
{
return check->second;
}
}
void ContentFile::write(const Path& path)
{
}

View file

@ -7,73 +7,37 @@
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
class MarkdownDocument;
class ContentFile class ContentFile
{ {
public: public:
using FileMetadata = std::unordered_map<std::string, std::string>; using FileMetadata = std::unordered_map<std::string, std::string>;
using FileContentBody = std::vector<std::string>; using FileContentBody = std::vector<std::string>;
ContentFile(const Path& filename) ContentFile(const Path& filename);
: mFilename(filename)
{
virtual ~ContentFile();
Path getFilename() const;
virtual void load();
std::string getMetadataItem(const std::string& key) const;
virtual std::string getOutputLocation() const;
MarkdownDocument* getContentBody() const
{
return mContentBody.get();
} }
virtual ~ContentFile() = default; void write(const Path& path);
Path getFilename() const
{
return mFilename;
}
virtual void load()
{
MarkdownContentParser parser;
parser.run(mFilename);
mMetadata = parser.getFileMetadata();
mContentBody = parser.getContentBody();
}
std::string getMetadataItem(const std::string& key) const
{
const auto check = mMetadata.find(key);
if (check == mMetadata.end())
{
return {};
}
else
{
return check->second;
}
}
protected: protected:
Path mFilename; Path mFilename;
FileMetadata mMetadata; FileMetadata mMetadata;
FileContentBody mContentBody; std::unique_ptr<MarkdownDocument> mContentBody;
}; std::string mProcessedOutput;
class ContentPage : public ContentFile
{
public:
ContentPage(const Path& filename)
: ContentFile(filename)
{
}
void load() override
{
ContentFile::load();
}
std::string getOutputLocation() const
{
const auto metadata_item = getMetadataItem("save_as");
return metadata_item.empty() ? File::getBaseFilename(mFilename) : metadata_item;
}
}; };
class ContentArticle : public ContentFile class ContentArticle : public ContentFile

View file

@ -0,0 +1,14 @@
#include "ContentPage.h"
ContentPage::ContentPage(const Path& filename)
: ContentFile(filename)
{
}
void ContentPage::load()
{
ContentFile::load();
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "ContentFile.h"
class ContentPage : public ContentFile
{
public:
ContentPage(const Path& filename);
void load() override;
};

View file

@ -1,11 +1,20 @@
#include "MarkdownContentParser.h" #include "MarkdownContentParser.h"
void MarkdownContentParser::run(const Path& path) #include "MarkdownParser.h"
#include "MarkdownDocument.h"
#include "MarkdownElement.h"
#include "File.h"
std::pair<MarkdownContentParser::FileMetadata, std::unique_ptr<MarkdownDocument>> MarkdownContentParser::run(const Path& path)
{ {
FileMetadata metadata; FileMetadata metadata;
FileMetadata output_metadata;
const auto lines = File(path).readLines(); const auto lines = File(path).readLines();
bool metadata_finished = false; bool metadata_finished = false;
std::string content_body;
for (const auto& line : lines) for (const auto& line : lines)
{ {
if (!metadata_finished) if (!metadata_finished)
@ -14,28 +23,23 @@ void MarkdownContentParser::run(const Path& path)
if (!metadata) if (!metadata)
{ {
metadata_finished = true; metadata_finished = true;
mContentBody.push_back(line); content_body += line + '\n';
} }
else else
{ {
mMetadata[metadata->first] = metadata->second; output_metadata[metadata->first] = metadata->second;
} }
} }
else else
{ {
mContentBody.push_back(line); content_body += line + '\n';
} }
} }
}
MarkdownContentParser::FileContentBody MarkdownContentParser::getContentBody() const MarkdownParser md_parser;
{ auto content = md_parser.run(content_body);
return mContentBody;
}
MarkdownContentParser::FileMetadata MarkdownContentParser::getFileMetadata() const return {output_metadata, std::move(content)};
{
return mMetadata;
} }
std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::checkForMetadataItem(const std::string& line) const std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::checkForMetadataItem(const std::string& line) const

View file

@ -1,27 +1,22 @@
#pragma once #pragma once
#include "File.h"
#include <string> #include <string>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <filesystem>
#include <vector>
using Path = std::filesystem::path;
class MarkdownDocument;
class MarkdownContentParser class MarkdownContentParser
{ {
public: public:
using FileMetadataItem = std::pair<std::string, std::string>; using FileMetadataItem = std::pair<std::string, std::string>;
using FileMetadata = std::unordered_map<std::string, std::string>; using FileMetadata = std::unordered_map<std::string, std::string>;
using FileContentBody = std::vector<std::string>;
void run(const Path& path);
FileContentBody getContentBody() const;
FileMetadata getFileMetadata() const;
std::pair<FileMetadata, std::unique_ptr<MarkdownDocument>> run(const Path& path);
private: private:
std::optional<FileMetadataItem> checkForMetadataItem(const std::string& line) const; std::optional<FileMetadataItem> checkForMetadataItem(const std::string& line) const;
FileMetadata mMetadata;
FileContentBody mContentBody;
}; };

View file

@ -0,0 +1,21 @@
#include "SiteGeneratorConfig.h"
Path SiteGeneratorConfig::getThemePath() const
{
return mThemesPath;
}
std::string SiteGeneratorConfig::getActiveTheme() const
{
return mActiveTheme;
}
void SiteGeneratorConfig::setThemePath(const Path& path)
{
mThemesPath = path;
}
void SiteGeneratorConfig::setActiveTheme(const std::string& theme)
{
mActiveTheme = theme;
}

View file

@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <filesystem>
using Path = std::filesystem::path;
class SiteGeneratorConfig
{
public:
Path getThemePath() const;
std::string getActiveTheme() const;
void setThemePath(const Path& path);
void setActiveTheme(const std::string& theme);
private:
Path mThemesPath;
std::string mActiveTheme;
};

View file

@ -0,0 +1,18 @@
#pragma once
#include <filesystem>
using Path = std::filesystem::path;
class SiteTemplateFile
{
public:
SiteTemplateFile(const Path& path)
: mPath(path)
{
}
private:
Path mPath;
};

View file

@ -1,6 +1,27 @@
#include "WebsiteGenerator.h" #include "WebsiteGenerator.h"
#include "TomlReader.h" #include "TomlReader.h"
#include "Directory.h"
#include "ContentFile.h"
#include "ContentPage.h"
#include "TemplateFile.h"
#include "TemplatingEngine.h"
#include "SiteGeneratorConfig.h"
#include "PathUtils.h"
#include "FileLogger.h"
WebsiteGenerator::WebsiteGenerator()
: mConfig(std::make_unique<SiteGeneratorConfig>()),
mTemplateEngine()
{
}
WebsiteGenerator::~WebsiteGenerator()
{
}
void WebsiteGenerator::findProject(const std::string& searchPath) void WebsiteGenerator::findProject(const std::string& searchPath)
{ {
@ -8,10 +29,42 @@ void WebsiteGenerator::findProject(const std::string& searchPath)
if (std::filesystem::exists(config_path)) if (std::filesystem::exists(config_path))
{ {
mProjectPath = searchPath; mProjectPath = searchPath;
std::cout << "Found config.toml in " << searchPath << std::endl; MLOG_INFO("Found project config in: " << mProjectPath);
} }
} }
void WebsiteGenerator::findContentFiles()
{
const bool is_recursive{true};
const auto pages_files = Directory::getFilesWithExtension(getPagesPath(), ".md", is_recursive);
for (const auto& path : pages_files)
{
mPages.push_back(std::make_unique<ContentPage>(path));
}
const auto article_files = Directory::getFilesWithExtension(getArticlesPath(), ".md", is_recursive);
for (const auto& path : article_files)
{
mArticles.push_back(std::make_unique<ContentArticle>(path));
}
MLOG_INFO("Found: " << mArticles.size() << " articles");
}
Path WebsiteGenerator::getContentPath() const
{
return mProjectPath / "content";
}
Path WebsiteGenerator::getPagesPath() const
{
return getContentPath() / "pages";
}
Path WebsiteGenerator::getArticlesPath() const
{
return getContentPath() / "articles";
}
Path WebsiteGenerator::getConfigPath() const Path WebsiteGenerator::getConfigPath() const
{ {
return mProjectPath / "config.toml"; return mProjectPath / "config.toml";
@ -28,72 +81,54 @@ void WebsiteGenerator::readConfig()
auto location = items["location"]; auto location = items["location"];
auto active_theme = items["active_theme"]; auto active_theme = items["active_theme"];
mConfig.setThemePath(location); mConfig->setThemePath(location);
mConfig.setActiveTheme(active_theme); mConfig->setActiveTheme(active_theme);
} }
} }
void WebsiteGenerator::parseContentFiles() void WebsiteGenerator::parseContentFiles()
{ {
findContentFiles(); findContentFiles();
for (auto& page : mPages) for (auto& page : mPages)
{ {
page.load(); page->load();
}
for (auto& article : mArticles)
{
article->load();
} }
} }
void WebsiteGenerator::parseTemplateFiles() void WebsiteGenerator::parseTemplateFiles()
{ {
const auto template_path = mProjectPath / mConfig.getThemePath() / mConfig.getActiveTheme(); const auto template_path = mProjectPath / mConfig->getThemePath() / mConfig->getActiveTheme();
mTemplateEngine = std::make_unique<TemplatingEngine>(template_path);
const auto template_files = Directory::getFilesWithExtension(template_path, ".html"); mTemplateEngine->loadTemplateFiles();
for (const auto& path : template_files)
{
mTemplateFiles[path.stem().string()] = std::make_unique<TemplateFile>(path);
}
} }
void WebsiteGenerator::doSubstitutions() void WebsiteGenerator::doSubstitutions()
{ {
auto article_template = mTemplateEngine->processTemplate("article");
for (auto& article : mArticles)
{
}
} }
void WebsiteGenerator::write() void WebsiteGenerator::write()
{ {
// Setup output dir // Setup output dir
const auto output_dir = mProjectPath / "output_custom"; const auto output_dir = mProjectPath / "output";
std::filesystem::create_directory(output_dir); std::filesystem::create_directory(output_dir);
// Write each page // Write each page
} for (auto& article : mArticles)
void WebsiteGenerator::findContentFiles()
{
const auto pages_files = Directory::getFilesWithExtension(getPagesPath(), ".md");
for (const auto& path : pages_files)
{ {
mPages.push_back(ContentPage(path)); auto article_path = article->getFilename();
} auto relative_path = PathUtils::getRelativePath(article_path, getArticlesPath());
auto updated_filename = PathUtils::getPathDelimited(relative_path);
const auto article_files = Directory::getFilesWithExtension(getArticlesPath(), ".md"); auto final_path = output_dir / Path(updated_filename).replace_extension(".html");
for (const auto& path : article_files)
{
mArticles.push_back(ContentArticle(path));
} }
} }
Path WebsiteGenerator::getContentPath() const
{
return mProjectPath / "content";
}
Path WebsiteGenerator::getPagesPath() const
{
return getContentPath() / "pages";
}
Path WebsiteGenerator::getArticlesPath() const
{
return getContentPath() / "articles";
}

View file

@ -1,72 +1,38 @@
#pragma once #pragma once
#include "Directory.h"
#include "ContentFile.h"
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <unordered_map>
#include <vector> #include <vector>
class GeneratorConfig using Path = std::filesystem::path;
{
public:
void setThemePath(const Path& path)
{
mThemesPath = path;
}
void setActiveTheme(const std::string& theme) class SiteGeneratorConfig;
{ class ContentPage;
mActiveTheme = theme; class ContentArticle;
} class TemplatingEngine;
Path getThemePath() const
{
return mThemesPath;
}
std::string getActiveTheme() const
{
return mActiveTheme;
}
private:
Path mThemesPath;
std::string mActiveTheme;
};
class TemplateFile
{
public:
TemplateFile(const Path& path)
: mPath(path)
{
}
private:
Path mPath;
};
class WebsiteGenerator class WebsiteGenerator
{ {
public: public:
WebsiteGenerator();
~WebsiteGenerator();
void doSubstitutions();
void findProject(const std::string& searchPath); void findProject(const std::string& searchPath);
void readConfig();
void parseContentFiles(); void parseContentFiles();
void parseTemplateFiles(); void parseTemplateFiles();
void doSubstitutions(); void readConfig();
void write(); void write();
private: private:
void findContentFiles(); void findContentFiles();
Path getContentPath() const; Path getContentPath() const;
@ -78,8 +44,10 @@ private:
Path getConfigPath() const; Path getConfigPath() const;
std::filesystem::path mProjectPath; std::filesystem::path mProjectPath;
GeneratorConfig mConfig;
std::vector<ContentPage> mPages; std::unique_ptr<SiteGeneratorConfig> mConfig;
std::vector<ContentArticle> mArticles; std::unique_ptr<TemplatingEngine> mTemplateEngine;
std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
std::vector<std::unique_ptr<ContentPage> > mPages;
std::vector<std::unique_ptr<ContentArticle> > mArticles;
}; };

View file

@ -2,7 +2,6 @@
#include "WebsiteGenerator.h" #include "WebsiteGenerator.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
auto args = CommandLineArgs::Create(); auto args = CommandLineArgs::Create();
@ -21,10 +20,10 @@ int main(int argc, char *argv[])
generator.parseTemplateFiles(); generator.parseTemplateFiles();
// Substitute template files // Substitute template files
generator.doSubstitutions();
// Write output // Write output
generator.write(); generator.write();
return 0; return 0;
} }

View file

@ -1,6 +1,7 @@
#include "TemplatingEngine.h" #include "TemplatingEngine.h"
#include "Directory.h" #include "Directory.h"
#include "FileLogger.h"
#include <iostream> #include <iostream>
@ -13,10 +14,11 @@ TemplatingEngine::TemplatingEngine(const Path& workingDirectory)
void TemplatingEngine::loadTemplateFiles() void TemplatingEngine::loadTemplateFiles()
{ {
const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension); const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension);
for (const auto& file : files) for (const auto& path : files)
{ {
mTemplateFiles.push_back(std::make_unique<TemplateFile>(file)); mTemplateFiles[path.stem().string()] = std::make_unique<TemplateFile>(path);
} }
MLOG_INFO("Found: " << mTemplateFiles.size() << " templates in " << mWorkingDirectory);
} }
std::string TemplatingEngine::processTemplate(const std::string& name) std::string TemplatingEngine::processTemplate(const std::string& name)
@ -35,15 +37,7 @@ TemplateFile* TemplatingEngine::getTemplateFile(const Path& path)
TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name) TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name)
{ {
//std::cout << "Looking for template file with name: " << name << std::endl; return mTemplateFiles[name].get();
for (const auto& file : mTemplateFiles)
{
if (file->getName() == name)
{
return file.get();
}
}
return nullptr;
} }
std::string TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent) std::string TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent)

View file

@ -5,6 +5,8 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <memory>
#include <unordered_map>
class TemplatingEngine class TemplatingEngine
{ {
@ -14,7 +16,6 @@ public:
void loadTemplateFiles(); void loadTemplateFiles();
std::string processTemplate(const std::string& name); std::string processTemplate(const std::string& name);
private: private:
TemplateFile* getTemplateFile(const std::string& name); TemplateFile* getTemplateFile(const std::string& name);
TemplateFile* getTemplateFile(const Path& path); TemplateFile* getTemplateFile(const Path& path);
@ -22,7 +23,7 @@ private:
std::string processTemplate(TemplateFile* file, TemplateNode* parent = nullptr); std::string processTemplate(TemplateFile* file, TemplateNode* parent = nullptr);
std::vector<std::unique_ptr<TemplateFile> > mTemplateFiles; std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
Path mWorkingDirectory; Path mWorkingDirectory;
std::string mTemplateExtension{ ".html" }; std::string mTemplateExtension{ ".html" };
}; };

View file

@ -1,3 +1,5 @@
set(MODULE_NAME core)
list(APPEND core_HEADERS list(APPEND core_HEADERS
AbstractApp.h AbstractApp.h
Dictionary.h Dictionary.h
@ -8,6 +10,7 @@ list(APPEND core_HEADERS
file_utilities/Directory.h file_utilities/Directory.h
file_utilities/File.h file_utilities/File.h
file_utilities/FileFormats.h file_utilities/FileFormats.h
file_utilities/PathUtils.h
StringUtils.h StringUtils.h
http/HttpResponse.h http/HttpResponse.h
serializers/TomlReader.h serializers/TomlReader.h
@ -25,6 +28,7 @@ list(APPEND core_LIB_INCLUDES
file_utilities/Directory.cpp file_utilities/Directory.cpp
file_utilities/File.cpp file_utilities/File.cpp
file_utilities/FileFormats.cpp file_utilities/FileFormats.cpp
file_utilities/PathUtils.cpp
memory/SharedMemory.cpp memory/SharedMemory.cpp
RandomUtils.cpp RandomUtils.cpp
StringUtils.cpp StringUtils.cpp
@ -38,18 +42,17 @@ list(APPEND core_LIB_INCLUDES
http/HttpRequest.cpp http/HttpRequest.cpp
serializers/TomlReader.cpp) serializers/TomlReader.cpp)
# add the executable add_library(${MODULE_NAME} SHARED ${core_LIB_INCLUDES} ${core_HEADERS})
add_library(core SHARED ${core_LIB_INCLUDES} ${core_HEADERS})
target_include_directories(core PUBLIC target_include_directories(${MODULE_NAME} PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/file_utilities" ${CMAKE_CURRENT_SOURCE_DIR}/file_utilities
"${CMAKE_CURRENT_SOURCE_DIR}/loggers" ${CMAKE_CURRENT_SOURCE_DIR}/loggers
"${CMAKE_CURRENT_SOURCE_DIR}/memory" ${CMAKE_CURRENT_SOURCE_DIR}/memory
"${CMAKE_CURRENT_SOURCE_DIR}/streams" ${CMAKE_CURRENT_SOURCE_DIR}/streams
"${CMAKE_CURRENT_SOURCE_DIR}/http" ${CMAKE_CURRENT_SOURCE_DIR}/http
"${CMAKE_CURRENT_SOURCE_DIR}/data_structures" ${CMAKE_CURRENT_SOURCE_DIR}/data_structures
"${CMAKE_CURRENT_SOURCE_DIR}/serializers" ${CMAKE_CURRENT_SOURCE_DIR}/serializers
) )
set_target_properties( core PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
set_property(TARGET core PROPERTY FOLDER src) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src)

View file

@ -1,6 +1,6 @@
#include "Directory.h" #include "Directory.h"
std::vector<Path> Directory::getFilesWithExtension(const Path& path, const std::string& extension) std::vector<Path> Directory::getFilesWithExtension(const Path& path, const std::string& extension, bool recursive)
{ {
std::vector<Path> paths; std::vector<Path> paths;
if (std::filesystem::is_directory(path)) if (std::filesystem::is_directory(path))
@ -11,6 +11,11 @@ std::vector<Path> Directory::getFilesWithExtension(const Path& path, const std::
{ {
paths.push_back(entry.path()); paths.push_back(entry.path());
} }
else if(recursive && std::filesystem::is_directory(entry))
{
const auto child_paths = getFilesWithExtension(entry, extension, recursive);
paths.insert(paths.end(), child_paths.begin(), child_paths.end());
}
} }
} }
return paths; return paths;

View file

@ -9,5 +9,5 @@ class Directory
{ {
public: public:
static std::vector<Path> getFilesWithExtension(const Path& path, const std::string& extension); static std::vector<Path> getFilesWithExtension(const Path& path, const std::string& extension, bool recursive=false);
}; };

View file

@ -197,11 +197,6 @@ std::string File::read()
return buffer.str(); return buffer.str();
} }
std::string File::getBaseFilename(const Path& path)
{
return path.stem().string();
}
std::string File::readText() std::string File::readText()
{ {
if (!pathExists()) if (!pathExists())

View file

@ -20,7 +20,6 @@ public:
}; };
public: public:
File(std::filesystem::path fullPath); File(std::filesystem::path fullPath);
~File(); ~File();
@ -29,8 +28,6 @@ public:
std::string dumpBinary(); std::string dumpBinary();
static std::string getBaseFilename(const Path& path);
std::string getExtension() const; std::string getExtension() const;
std::ifstream* getInHandle() const; std::ifstream* getInHandle() const;

View file

@ -0,0 +1,33 @@
#include "PathUtils.h"
std::string PathUtils::getBaseFilename(const Path& path)
{
return path.stem().string();
}
Path PathUtils::getRelativePath(const Path& input, const Path& relativeTo)
{
return std::filesystem::relative(input, relativeTo);
}
std::string PathUtils::getPathDelimited(const Path& path, char delimiter)
{
std::string name;
unsigned count = 0;
for(const auto& element : path)
{
if (count == 0)
{
name += element.stem().string();
}
else
{
name += delimiter + element.stem().string();
}
count++;
}
return name;
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
using Path = std::filesystem::path;
class PathUtils
{
public:
static std::string getBaseFilename(const Path& path);
static Path getRelativePath(const Path& path, const Path& relativeTo);
static std::string getPathDelimited(const Path& path, char delimiter='-');
};

View file

@ -15,6 +15,10 @@ list(APPEND web_LIB_INCLUDES
markdown/MarkdownConverter.cpp markdown/MarkdownConverter.cpp
markdown/MarkdownDocument.h markdown/MarkdownDocument.h
markdown/MarkdownDocument.cpp markdown/MarkdownDocument.cpp
markdown/MarkdownComponents.h
markdown/MarkdownComponents.cpp
markdown/MarkdownElement.h
markdown/MarkdownElement.cpp
html/HtmlWriter.cpp html/HtmlWriter.cpp
html/HtmlDocument.cpp html/HtmlDocument.cpp
html/HtmlElement.cpp html/HtmlElement.cpp

View file

@ -0,0 +1,122 @@
#include "MarkdownComponents.h"
MarkdownTextSpan::Type MarkdownTextSpan::getType() const
{
return Type::TEXT_SPAN;
}
MarkdownParagraph::Type MarkdownParagraph::getType() const
{
return Type::PARAGRAPH;
}
void MarkdownParagraph::addChild(std::unique_ptr<MarkdownInlineElement> child)
{
mChildren.push_back(std::move(child));
}
std::size_t MarkdownParagraph::getNumChildren() const
{
return mChildren.size();
}
MarkdownInlineElement* MarkdownParagraph::getChild(std::size_t idx) const
{
return mChildren[idx].get();
}
MarkdownBulletItem::Type MarkdownBulletItem::getType() const
{
return Type::BULLET_ITEM;
}
MarkdownBulletList::Type MarkdownBulletList::getType() const
{
return Type::BULLET_LIST;
}
void MarkdownBulletList::addChild(std::unique_ptr<MarkdownBulletItem> child)
{
mChildren.push_back(std::move(child));
}
std::size_t MarkdownBulletList::getNumChildren() const
{
return mChildren.size();
}
MarkdownBulletItem* MarkdownBulletList::getChild(std::size_t idx) const
{
return mChildren[idx].get();
}
MarkdownHeading::MarkdownHeading(unsigned level)
: mLevel(level)
{
}
MarkdownHeading::Type MarkdownHeading::getType() const
{
return Type::HEADING;
}
unsigned MarkdownHeading::getLevel() const
{
return mLevel;
}
MarkdownInlineQuote::Type MarkdownInlineQuote::getType() const
{
return Type::INLINE_QUOTE;
}
MarkdownLink::MarkdownLink(const std::string& target)
: mTarget(target)
{
}
const std::string& MarkdownLink::getTarget() const
{
return mTarget;
}
MarkdownLink::Type MarkdownLink::getType() const
{
return Type::LINK;
}
MarkdownImage::MarkdownImage(const std::string& source, const std::string& alt)
: mSource(source),
mAlt(alt)
{
}
MarkdownImage::Type MarkdownImage::getType() const
{
return Type::IMAGE;
}
const std::string& MarkdownImage::getSource() const
{
return mSource;
}
const std::string& MarkdownImage::getAlt() const
{
return mAlt;
}
MarkdownMultilineQuote::MarkdownMultilineQuote(const std::string& tag)
: mTag(tag)
{
}
MarkdownMultilineQuote::Type MarkdownMultilineQuote::getType() const
{
return Type::MULTILINE_QUOTE;
}

View file

@ -0,0 +1,124 @@
#pragma once
#include "MarkdownElement.h"
#include <vector>
#include <memory>
class MarkdownTextSpan : public MarkdownInlineElement
{
public:
virtual ~MarkdownTextSpan() = default;
Type getType() const override;
};
class MarkdownParagraph : public MarkdownElement
{
public:
virtual ~MarkdownParagraph() = default;
Type getType() const override;
void addChild(std::unique_ptr<MarkdownInlineElement> child);
std::size_t getNumChildren() const;
MarkdownInlineElement* getChild(std::size_t idx) const;
private:
std::vector<std::unique_ptr<MarkdownInlineElement> > mChildren;
};
class MarkdownBulletItem : public MarkdownElement
{
public:
virtual ~MarkdownBulletItem() = default;
Type getType() const override;
};
class MarkdownBulletList : public MarkdownElement
{
public:
virtual ~MarkdownBulletList() = default;
Type getType() const override;
void addChild(std::unique_ptr<MarkdownBulletItem> child);
std::size_t getNumChildren() const;
MarkdownBulletItem* getChild(std::size_t idx) const;
private:
std::vector<std::unique_ptr<MarkdownBulletItem> > mChildren;
};
class MarkdownHeading : public MarkdownElement
{
public:
MarkdownHeading(unsigned level);
virtual ~MarkdownHeading() = default;
Type getType() const override;
unsigned getLevel() const;
private:
unsigned mLevel{1};
};
class MarkdownInlineQuote : public MarkdownInlineElement
{
public:
virtual ~MarkdownInlineQuote() = default;
Type getType() const override;
};
class MarkdownLink : public MarkdownInlineElement
{
public:
MarkdownLink(const std::string& target);
virtual ~MarkdownLink() = default;
const std::string& getTarget() const;
Type getType() const override;
private:
std::string mTarget;
};
class MarkdownImage : public MarkdownInlineElement
{
public:
MarkdownImage(const std::string& source, const std::string& alt);
virtual ~MarkdownImage() = default;
Type getType() const override;
const std::string& getSource() const;
const std::string& getAlt() const;
private:
std::string mSource;
std::string mAlt;
};
class MarkdownMultilineQuote : public MarkdownElement
{
public:
MarkdownMultilineQuote(const std::string& tag);
virtual ~MarkdownMultilineQuote() = default;
Type getType() const override;
private:
std::string mTag;
};

View file

@ -4,6 +4,8 @@
#include "HtmlElement.h" #include "HtmlElement.h"
#include "HtmlParagraphElement.h" #include "HtmlParagraphElement.h"
#include "HtmlTextRun.h" #include "HtmlTextRun.h"
#include "MarkdownElement.h"
#include "MarkdownComponents.h"
#include "MarkdownDocument.h" #include "MarkdownDocument.h"

View file

@ -0,0 +1,18 @@
#include "MarkdownDocument.h"
#include "MarkdownElement.h"
void MarkdownDocument::addElement(std::unique_ptr<MarkdownElement> element)
{
mElements.push_back(std::move(element));
}
std::size_t MarkdownDocument::getNumElements() const
{
return mElements.size();
}
MarkdownElement* MarkdownDocument::getElement(std::size_t idx) const
{
return mElements[idx].get();
}

View file

@ -3,262 +3,16 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
class MarkdownElement class MarkdownElement;
{
public:
enum class Type
{
HEADING,
PARAGRAPH,
TEXT_SPAN,
INLINE_CODE,
MULTILINE_CODE,
INLINE_QUOTE,
MULTILINE_QUOTE,
INLINE_SPECIAL,
MULTILINE_SPECIAL,
LINK,
IMAGE,
BULLET_ITEM,
BULLET_LIST
};
virtual ~MarkdownElement() = default;
void appendTextContent(const std::string& content)
{
mTextContent += content;
}
const std::string& getTextContent() const
{
return mTextContent;
}
virtual Type getType() const = 0;
private:
std::string mTextContent;
};
class MarkdownInlineElement : public MarkdownElement
{
public:
virtual ~MarkdownInlineElement() = default;
};
class MarkdownTextSpan : public MarkdownInlineElement
{
public:
virtual ~MarkdownTextSpan() = default;
Type getType() const override
{
return Type::TEXT_SPAN;
}
};
class MarkdownParagraph : public MarkdownElement
{
public:
virtual ~MarkdownParagraph() = default;
Type getType() const override
{
return Type::PARAGRAPH;
}
void addChild(std::unique_ptr<MarkdownInlineElement> child)
{
mChildren.push_back(std::move(child));
}
std::size_t getNumChildren() const
{
return mChildren.size();
}
MarkdownInlineElement* getChild(std::size_t idx) const
{
return mChildren[idx].get();
}
std::vector<std::unique_ptr<MarkdownInlineElement> > mChildren;
};
class MarkdownBulletItem : public MarkdownElement
{
public:
virtual ~MarkdownBulletItem() = default;
Type getType() const override
{
return Type::BULLET_ITEM;
}
};
class MarkdownBulletList : public MarkdownElement
{
public:
virtual ~MarkdownBulletList() = default;
Type getType() const override
{
return Type::BULLET_LIST;
}
void addChild(std::unique_ptr<MarkdownBulletItem> child)
{
mChildren.push_back(std::move(child));
}
std::size_t getNumChildren() const
{
return mChildren.size();
}
MarkdownBulletItem* getChild(std::size_t idx) const
{
return mChildren[idx].get();
}
std::vector<std::unique_ptr<MarkdownBulletItem> > mChildren;
};
class MarkdownHeading : public MarkdownElement
{
public:
MarkdownHeading(unsigned level)
: mLevel(level)
{
}
virtual ~MarkdownHeading() = default;
Type getType() const override
{
return Type::HEADING;
}
unsigned getLevel() const
{
return mLevel;
}
private:
unsigned mLevel{1};
};
class MarkdownInlineQuote : public MarkdownInlineElement
{
public:
virtual ~MarkdownInlineQuote() = default;
Type getType() const override
{
return Type::INLINE_QUOTE;
}
};
class MarkdownLink : public MarkdownInlineElement
{
public:
MarkdownLink(const std::string& target)
: mTarget(target)
{
}
virtual ~MarkdownLink() = default;
const std::string& getTarget() const
{
return mTarget;
}
Type getType() const override
{
return Type::LINK;
}
private:
std::string mTarget;
};
class MarkdownImage : public MarkdownInlineElement
{
public:
MarkdownImage(const std::string& source, const std::string& alt)
: mSource(source),
mAlt(alt)
{
}
virtual ~MarkdownImage() = default;
Type getType() const override
{
return Type::IMAGE;
}
const std::string& getSource() const
{
return mSource;
}
const std::string& getAlt() const
{
return mAlt;
}
private:
std::string mSource;
std::string mAlt;
};
class MarkdownMultilineQuote : public MarkdownElement
{
public:
MarkdownMultilineQuote(const std::string& tag)
: mTag(tag)
{
}
virtual ~MarkdownMultilineQuote() = default;
Type getType() const override
{
return Type::MULTILINE_QUOTE;
}
private:
std::string mTag;
};
class MarkdownDocument class MarkdownDocument
{ {
public: public:
void addElement(std::unique_ptr<MarkdownElement> element) void addElement(std::unique_ptr<MarkdownElement> element);
{
mElements.push_back(std::move(element));
}
std::size_t getNumElements() const std::size_t getNumElements() const;
{
return mElements.size();
}
MarkdownElement* getElement(std::size_t idx) const MarkdownElement* getElement(std::size_t idx) const;
{
return mElements[idx].get();
}
private: private:
std::vector<std::unique_ptr<MarkdownElement> > mElements; std::vector<std::unique_ptr<MarkdownElement> > mElements;

View file

@ -0,0 +1,11 @@
#include "MarkdownElement.h"
void MarkdownElement::appendTextContent(const std::string& content)
{
mTextContent += content;
}
const std::string& MarkdownElement::getTextContent() const
{
return mTextContent;
}

View file

@ -0,0 +1,40 @@
#pragma once
#include <string>
class MarkdownElement
{
public:
enum class Type
{
HEADING,
PARAGRAPH,
TEXT_SPAN,
INLINE_CODE,
MULTILINE_CODE,
INLINE_QUOTE,
MULTILINE_QUOTE,
INLINE_SPECIAL,
MULTILINE_SPECIAL,
LINK,
IMAGE,
BULLET_ITEM,
BULLET_LIST
};
virtual ~MarkdownElement() = default;
void appendTextContent(const std::string& content);
const std::string& getTextContent() const;
virtual Type getType() const = 0;
private:
std::string mTextContent;
};
class MarkdownInlineElement : public MarkdownElement
{
public:
virtual ~MarkdownInlineElement() = default;
};

View file

@ -2,6 +2,7 @@
#include "MarkdownDocument.h" #include "MarkdownDocument.h"
#include "StringUtils.h" #include "StringUtils.h"
#include "MarkdownComponents.h"
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>