Skip to content

Commit

Permalink
Implement theme manager
Browse files Browse the repository at this point in the history
  • Loading branch information
MetroWind committed Sep 26, 2024
1 parent 2e665ca commit 10c65e7
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ set(SOURCE_FILES
src/utils.hpp
src/attachment.hpp
src/legacy-migration.hpp
src/theme.cpp
src/theme.hpp
)

set(LIBS
Expand Down
120 changes: 120 additions & 0 deletions src/theme.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include <algorithm>
#include <expected>
#include <filesystem>
#include <fstream>

#include <ryml.hpp>
#include <ryml_std.hpp>

#include "theme.hpp"
#include "error.hpp"
#include "utils.hpp"

namespace fs = std::filesystem;

namespace
{
constexpr char THEME_INFO_FILE_NAME[] = "info.yaml";

E<Theme> readThemeDir(const fs::path& dir)
{
// Read theme info from the info file.
std::ifstream file(dir / THEME_INFO_FILE_NAME);
std::vector<char> buffer(std::istreambuf_iterator<char>{file}, {});
if(file.bad() || file.fail())
{
return std::unexpected(runtimeError("Failed to read theme info"));
}
file.close();

ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(buffer));
Theme theme;
theme.dir = dir;
if(tree["parent"].has_key())
{
tree["parent"] >> theme.parent_name;
}
if(tree["name"].has_key())
{
tree["name"] >> theme.name;
}
else
{
theme.name = dir.filename();
}

// Find all stylesheets.
for(const fs::directory_entry& entry: fs::directory_iterator(dir))
{
fs::path path = entry.path();
std::string ext = path.extension();
if(toLower(ext) != ".css")
{
continue;
}
theme.stylesheets.push_back(fs::relative(path, dir.parent_path()));
}
std::sort(theme.stylesheets.begin(), theme.stylesheets.end());
return theme;
}

} // namespace

E<void> ThemeManager::loadDir(const std::filesystem::path& dir)
{
for(const fs::directory_entry& entry: fs::directory_iterator(dir))
{
// First layer of dirs is themes. Each subdirectory is a theme.
if(entry.is_directory())
{
fs::path path = entry.path();
if(!fs::exists(path / THEME_INFO_FILE_NAME))
{
continue;
}
ASSIGN_OR_RETURN(Theme theme, readThemeDir(path));
themes[theme.name] = std::move(theme);
}
}

// Go though all themes to establish hierarchy.
for(auto& theme_pair: themes)
{
Theme& theme = theme_pair.second;
if(theme.parent_name.empty())
{
continue;
}

const auto it = themes.find(theme.parent_name);
if(it == themes.end())
{
return std::unexpected(runtimeError(
std::format("Couldn’t find theme {}’s parent, {}",
theme.name, theme.parent_name)));
}
theme.parent = &(it->second);
}
return {};
}

std::vector<std::filesystem::path> ThemeManager::stylesheets(
const std::string& theme_name) const
{
std::vector<std::filesystem::path> result;
const Theme* theme = nullptr;
const auto it = themes.find(theme_name);
if(it == themes.end())
{
return result;
}
theme = &(it->second);
while(theme)
{
result.insert(result.end(), theme->stylesheets.rbegin(),
theme->stylesheets.rend());
theme = theme->parent;
}
std::reverse(result.begin(), result.end());
return result;
}
36 changes: 36 additions & 0 deletions src/theme.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <string>
#include <filesystem>
#include <unordered_map>
#include <vector>

#include "error.hpp"

struct Theme
{
Theme* parent = nullptr;
std::string parent_name;
std::string name;
// Full path of the theme dir.
std::filesystem::path dir;
// Paths of stylesheet files, relative to the themes dir (which
// contains all the themes), sorted by filename.
std::vector<std::filesystem::path> stylesheets;
};

class ThemeManager
{
public:
E<void> loadDir(const std::filesystem::path& dir);

// Return all the stylesheets required for the specified theme,
// including the stylesheets from the parents up to a root theme.
// The stylesheets are arranged in an order that is suitable to be
// referenced in an HTML file. If the theme is not found, return
// an empty vector.
std::vector<std::filesystem::path> stylesheets(
const std::string& theme_name) const;
private:
std::unordered_map<std::string, Theme> themes;
};

0 comments on commit 10c65e7

Please sign in to comment.