Skip to content

Commit

Permalink
Add yaml templates
Browse files Browse the repository at this point in the history
  • Loading branch information
psiberx committed Jul 31, 2023
1 parent e62bc9b commit 247e636
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/App/Project.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ constexpr auto Author = "psiberx";
constexpr auto NameW = L"TweakXL";
constexpr auto AuthorW = L"psiberx";

constexpr auto Version = semver::from_string_noexcept("1.1.7").value();
constexpr auto Version = semver::from_string_noexcept("1.2.0").value();
}
204 changes: 204 additions & 0 deletions src/App/Tweaks/Declarative/Yaml/YamlReader.Template.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#include "YamlReader.hpp"

namespace
{
constexpr auto TemplateAttrKey = "$data";

constexpr auto AttrMark = '$';
constexpr auto AttrOpen = '{';
constexpr auto AttrClose = '}';

uint64_t MakeKey(const std::string& aName)
{
return Red::FNV1a64(aName.c_str());
}

uint64_t MakeKey(const char* aName, uint32_t aSize)
{
return Red::FNV1a64(reinterpret_cast<const uint8_t*>(aName), aSize);
}

std::string FormatString(const std::string& aInput, const Core::Map<uint64_t, std::string>& aData)
{
constexpr auto MaxLength = 512;

char buffer[MaxLength + 1];
char* out = buffer;
const char* max = buffer + MaxLength;
const char* str = aInput.c_str();

while (str && *str)
{
auto* attrOpen = strchr(str, AttrMark);

if (!attrOpen)
{
if (out == buffer)
{
return aInput;
}

while (*str && out < max)
{
*out = *str;
++out;
++str;
}
break;
}

if (*(attrOpen + 1) != AttrOpen)
{
return aInput;
}

auto* attrClose = strchr(str, AttrClose);

if (!attrClose)
{
return aInput;
}

while (str != attrOpen && out < max)
{
*out = *str;
++out;
++str;
}

if (out == max)
{
break;
}

str = attrClose + 1;

const auto attr = MakeKey(attrOpen + 2, attrClose - attrOpen - 2);
const char* value = nullptr;

const auto it = aData.find(attr);
if (it != aData.end())
{
value = it.value().c_str();
}

if (value)
{
while (value && *value && out < max)
{
*out = *value;
++out;
++value;
}

if (out == max)
{
break;
}
}
}

*out = '\0';

return buffer;
}

YAML::Node FormatNode(const YAML::Node& aNode, const Core::Map<uint64_t, std::string>& aData)
{
switch (aNode.Type())
{
case YAML::NodeType::Scalar:
{
const auto& value = aNode.Scalar();
if (value.find(AttrMark) != std::string::npos)
{
return YAML::Node(FormatString(value, aData));
}
else
{
return aNode;
}
}
case YAML::NodeType::Map:
{
YAML::Node node;
for (auto& nodeIt : aNode)
{
node[nodeIt.first] = FormatNode(nodeIt.second, aData);
}
return node;
}
case YAML::NodeType::Sequence:
{
YAML::Node node;
for (std::size_t i = 0; i < aNode.size(); ++i)
{
node[i] = FormatNode(aNode[i], aData);
}
return node;
}
default:
return aNode;
}
}
}

void App::YamlReader::ExpandTemplates(YAML::Node& aRootNode)
{
Core::Vector<std::pair<const std::string&, YAML::Node>> templates;

for (const auto& nodeIt : aRootNode)
{
const auto& subKey = nodeIt.first.Scalar();
const auto& subNode = nodeIt.second;

if (!subNode.IsMap() || !subNode[TemplateAttrKey].IsDefined())
continue;

templates.emplace_back(subKey, subNode);
}

for (auto& [templateName, templateNode] : templates)
{
aRootNode.remove(templateName);

const auto& dataNode = templateNode[TemplateAttrKey];

if (!dataNode.IsSequence())
{
LogError("{}: Template data must be an array of structs.", templateName);
continue;
}

templateNode.remove(TemplateAttrKey);

for (std::size_t i = 0; i < dataNode.size(); ++i)
{
Core::Map<uint64_t, std::string> instanceData;

for (const auto& propIt : dataNode[i])
{
const auto propKey = MakeKey(propIt.first.Scalar());
const auto& propValue = propIt.second;

if (!propValue.IsScalar())
continue;

instanceData[propKey] = propValue.Scalar();
}

auto instanceName = FormatString(templateName, instanceData);

if (aRootNode[instanceName].IsDefined())
{
LogError("{}: Cannot instantiate {}, because it already exists.",
templateName, instanceName);
continue;
}

auto instanceNode = FormatNode(templateNode, instanceData);

aRootNode[instanceName] = instanceNode;
}
}
}
1 change: 1 addition & 0 deletions src/App/Tweaks/Declarative/Yaml/YamlReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ void App::YamlReader::Read(App::TweakChangeset& aChangeset)
}

ConvertLegacyNodes();
ExpandTemplates(m_data);

auto propMode = ResolvePropertyMode(m_data);

Expand Down
1 change: 1 addition & 0 deletions src/App/Tweaks/Declarative/Yaml/YamlReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class YamlReader
Red::InstancePtr<> MakeValue(const Red::CBaseRTTIType* aType, const YAML::Node& aNode);
std::pair<Red::CName, Red::InstancePtr<>> TryMakeValue(const YAML::Node& aNode);

void ExpandTemplates(YAML::Node& aRootNode);
void ConvertLegacyNodes();

std::filesystem::path m_path;
Expand Down
8 changes: 4 additions & 4 deletions src/App/Version.rc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#define VER_PRODUCTVERSION 1,1,7,0
#define VER_FILEVERSION 1,1,7,2306261621
#define VER_PRODUCTVERSION 1,2,0,0
#define VER_FILEVERSION 1,2,0,2308010056

#define VER_PRODUCTNAME_STR "TweakXL\0"
#define VER_PRODUCTVERSION_STR "1.1.7\0"
#define VER_FILEVERSION_STR "1.1.7.2306261621\0"
#define VER_PRODUCTVERSION_STR "1.2.0\0"
#define VER_FILEVERSION_STR "1.2.0.2308010056\0"

1 VERSIONINFO
FILEVERSION VER_FILEVERSION
Expand Down
2 changes: 1 addition & 1 deletion xmake.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
set_xmakever("2.5.9")

set_project("TweakXL")
set_version("1.1.7", {build = "%y%m%d%H%M"})
set_version("1.2.0", {build = "%y%m%d%H%M"})

set_arch("x64")
set_languages("cxx20", "cxx2a")
Expand Down

0 comments on commit 247e636

Please sign in to comment.