2022-09-05 23:25:11 +02:00

355 lines
11 KiB
C++

#include "Templater.h"
#include <unordered_map>
#include <vector>
#include <filesystem>
#include <fstream>
#include <iostream>
#include "Utils/ClassUtils.h"
#include "DirectiveEscapeStreamProxy.h"
#include "TemplatingStreamProxy.h"
#include "Parsing/ParsingException.h"
#include "Parsing/Impl/DefinesStreamProxy.h"
#include "Parsing/Impl/ParserSingleInputStream.h"
using namespace templating;
namespace fs = std::filesystem;
namespace templating
{
class TemplatingPass
{
public:
TemplatingPass()
: m_stream(nullptr)
{
}
TemplatingPass(std::istream& stream, const std::string& fileName, ITemplaterControl* templaterControl)
{
m_base_stream = std::make_unique<ParserSingleInputStream>(stream, fileName);
m_templating_proxy = std::make_unique<TemplatingStreamProxy>(m_base_stream.get(), templaterControl);
m_defines_proxy = std::make_unique<DefinesStreamProxy>(m_templating_proxy.get());
m_directive_escape_proxy = std::make_unique<DirectiveEscapeStreamProxy>(m_defines_proxy.get());
m_templating_proxy->SetDefinesProxy(m_defines_proxy.get());
m_stream = m_directive_escape_proxy.get();
}
std::unique_ptr<IParserLineStream> m_base_stream;
std::unique_ptr<TemplatingStreamProxy> m_templating_proxy;
std::unique_ptr<DefinesStreamProxy> m_defines_proxy;
std::unique_ptr<DirectiveEscapeStreamProxy> m_directive_escape_proxy;
IParserLineStream* m_stream;
};
enum class TemplatingVariationType
{
OPTIONS,
SWITCH
};
class ITemplatingVariation
{
protected:
ITemplatingVariation() = default;
public:
virtual ~ITemplatingVariation() = default;
ITemplatingVariation(const ITemplatingVariation& other) = default;
ITemplatingVariation(ITemplatingVariation&& other) noexcept = default;
ITemplatingVariation& operator=(const ITemplatingVariation& other) = default;
ITemplatingVariation& operator=(ITemplatingVariation&& other) noexcept = default;
virtual const std::string& GetName() = 0;
virtual void Advance() = 0;
virtual void Apply(DefinesStreamProxy* definesProxy) = 0;
_NODISCARD virtual bool IsFinished() const = 0;
_NODISCARD virtual TemplatingVariationType GetVariationType() const = 0;
};
class SwitchVariation final : public ITemplatingVariation
{
public:
explicit SwitchVariation(std::string name)
: m_name(std::move(name)),
m_should_define(true),
m_finished(false)
{
}
const std::string& GetName() override
{
return m_name;
}
void Advance() override
{
if (!m_should_define)
m_finished = true;
else
m_should_define = false;
}
void Apply(DefinesStreamProxy* definesProxy) override
{
if (m_should_define)
definesProxy->AddDefine(DefinesStreamProxy::Define(m_name, "1"));
}
_NODISCARD bool IsFinished() const override
{
return m_finished;
}
_NODISCARD TemplatingVariationType GetVariationType() const override
{
return TemplatingVariationType::SWITCH;
}
std::string m_name;
bool m_should_define;
bool m_finished;
};
class OptionsVariation final : public ITemplatingVariation
{
public:
OptionsVariation(std::string name, std::vector<std::string> values)
: m_name(std::move(name)),
m_values(std::move(values)),
m_value_offset(0u)
{
}
const std::string& GetName() override
{
return m_name;
}
void Advance() override
{
m_value_offset++;
}
void Apply(DefinesStreamProxy* definesProxy) override
{
if (m_value_offset < m_values.size())
definesProxy->AddDefine(DefinesStreamProxy::Define(m_name, m_values[m_value_offset]));
}
_NODISCARD bool IsFinished() const override
{
return m_value_offset >= m_values.size();
}
_NODISCARD TemplatingVariationType GetVariationType() const override
{
return TemplatingVariationType::OPTIONS;
}
std::string m_name;
std::vector<std::string> m_values;
size_t m_value_offset;
};
class TemplaterControlImpl final : ITemplaterControl
{
public:
TemplaterControlImpl(std::istream& stream, std::string filename, const std::string& outputDirectory)
: m_stream(stream),
m_filename(std::move(filename)),
m_output_directory(outputDirectory),
m_first_line(true),
m_write_output_to_file(false)
{
fs::path filenamePath(m_filename);
m_default_output_file = (m_output_directory / filenamePath.replace_extension()).string();
}
bool RunNextPass()
{
m_stream.clear();
m_stream.seekg(0, std::ios::beg);
m_output_file = m_default_output_file;
m_current_pass = TemplatingPass(m_stream, m_filename, this);
for (const auto& activeVariation : m_active_variations)
activeVariation->Apply(m_current_pass.m_defines_proxy.get());
while (!m_current_pass.m_stream->Eof())
{
auto nextLine = m_current_pass.m_stream->NextLine();
if (m_write_output_to_file)
{
if (m_first_line)
m_first_line = false;
else
m_output_stream << '\n';
m_output_stream << nextLine.m_line;
}
else
{
if (m_first_line)
m_first_line = false;
else
m_output_cache << '\n';
m_output_cache << nextLine.m_line;
}
}
if (!m_write_output_to_file)
{
m_output_stream = std::ofstream(m_output_file);
if (!m_output_stream.is_open())
{
std::cerr << "Failed to open output file \"" << m_output_file << "\"\n";
return false;
}
const auto cachedData = m_output_cache.str();
if (!cachedData.empty())
m_output_stream << cachedData;
}
m_first_line = true;
m_write_output_to_file = false;
m_output_cache.clear();
m_output_cache.str(std::string());
m_output_stream.close();
return true;
}
void AdvanceActiveVariations()
{
while (!m_active_variations.empty())
{
const auto& lastVariation = m_active_variations[m_active_variations.size() - 1];
lastVariation->Advance();
if (lastVariation->IsFinished())
{
m_active_variations_by_name.erase(lastVariation->GetName());
m_active_variations.pop_back();
}
else
break;
}
}
_NODISCARD bool HasActiveVariations() const
{
return !m_active_variations.empty();
}
protected:
bool AddSwitch(std::string switchName) override
{
const auto existingVariation = m_active_variations_by_name.find(switchName);
if (existingVariation != m_active_variations_by_name.end())
return existingVariation->second->GetVariationType() == TemplatingVariationType::SWITCH;
auto switchVariation = std::make_unique<SwitchVariation>(std::move(switchName));
if (m_current_pass.m_defines_proxy)
switchVariation->Apply(m_current_pass.m_defines_proxy.get());
m_active_variations_by_name.emplace(switchVariation->m_name, switchVariation.get());
m_active_variations.emplace_back(std::move(switchVariation));
return true;
}
bool AddOptions(std::string optionsName, std::vector<std::string> optionValues) override
{
const auto existingVariation = m_active_variations_by_name.find(optionsName);
if (existingVariation != m_active_variations_by_name.end())
return existingVariation->second->GetVariationType() == TemplatingVariationType::SWITCH;
auto optionsVariation = std::make_unique<OptionsVariation>(std::move(optionsName), std::move(optionValues));
if (m_current_pass.m_defines_proxy)
optionsVariation->Apply(m_current_pass.m_defines_proxy.get());
m_active_variations_by_name.emplace(optionsVariation->m_name, optionsVariation.get());
m_active_variations.emplace_back(std::move(optionsVariation));
return true;
}
bool SetFileName(const std::string& fileName) override
{
if (m_write_output_to_file)
return false;
m_output_file = fileName;
m_output_stream = std::ofstream(m_output_file);
if (!m_output_stream.is_open())
{
std::cerr << "Failed to open output file \"" << m_output_file << "\"\n";
return false;
}
m_write_output_to_file = true;
const auto cachedData = m_output_cache.str();
if (!cachedData.empty())
m_output_stream << cachedData;
m_output_cache.clear();
m_output_cache.str(std::string());
return true;
}
private:
std::vector<std::unique_ptr<ITemplatingVariation>> m_active_variations;
std::unordered_map<std::string, ITemplatingVariation*> m_active_variations_by_name;
TemplatingPass m_current_pass;
std::istream& m_stream;
std::string m_filename;
std::string m_output_file;
std::string m_default_output_file;
const fs::path m_output_directory;
bool m_first_line;
bool m_write_output_to_file;
std::ofstream m_output_stream;
std::ostringstream m_output_cache;
};
}
Templater::Templater(std::istream& stream, std::string fileName)
: m_stream(stream),
m_file_name(std::move(fileName))
{
}
bool Templater::TemplateToDirectory(const std::string& outputDirectory)
{
TemplaterControlImpl control(m_stream, m_file_name, outputDirectory);
try
{
if (!control.RunNextPass())
return false;
control.AdvanceActiveVariations();
while (control.HasActiveVariations())
{
if (!control.RunNextPass())
return false;
control.AdvanceActiveVariations();
}
}
catch (ParsingException& e)
{
std::cerr << "Error: " << e.FullMessage() << std::endl;
return false;
}
return true;
}