Skip to content

Commit

Permalink
Parser now supports replacing custom variables
Browse files Browse the repository at this point in the history
  • Loading branch information
jlblancoc committed Dec 17, 2024
1 parent fff8405 commit ea23b22
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 23 deletions.
7 changes: 5 additions & 2 deletions mola_yaml/include/mola_yaml/yaml_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <mola_yaml/macro_helpers.h>
#include <mrpt/containers/yaml.h>

#include <sstream>
#include <map>
#include <string>

namespace mola
Expand All @@ -27,7 +27,10 @@ struct YAMLParseOptions
{
bool doIncludes{true}; //!< "$include{}"s
bool doCmdRuns{true}; //!< "$()"s
bool doEnvVars{true}; //!< "${}"s
bool doEnvVars{true}; //!< "${}"s (from env vars and field "variables")

/** Custom variables for replacements like `${name}` => `value` */
std::map<std::string, std::string> variables;

/** If not empty, base reference path which respect to "$include{}"s are
* specified. Automatically filled in by load_yaml_file() */
Expand Down
54 changes: 37 additions & 17 deletions mola_yaml/src/yaml_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ namespace fs = std::filesystem;

using mrpt::containers::yaml;

static std::string::size_type findClosing(
namespace
{
std::string::size_type findClosing(
size_t pos, const std::string& s, const char searchEndChar,
const char otherStartChar)
{
Expand All @@ -59,31 +61,34 @@ static std::string::size_type findClosing(
}

// "foo|bar" -> {"foo","bar"}
static std::tuple<std::string, std::string> splitVerticalBar(
const std::string& s)
std::tuple<std::string, std::string> splitVerticalBar(const std::string& s)
{
const auto posBar = s.find("|");
if (posBar == std::string::npos) return {s, {}};

return {s.substr(0, posBar), s.substr(posBar + 1)};
}

static std::string trimWSNL(const std::string& s)
std::string trimWSNL(const std::string& s)
{
std::string str = s;
mrpt::system::trim(str);
str.erase(std::remove(str.begin(), str.end(), '\r'), str.end());
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
return str;
}
} // namespace

std::string mola::yaml_to_string(const mrpt::containers::yaml& cfg)
{
std::stringstream ss;
ss << cfg;
return ss.str();
}

static std::string parseEnvVars(
namespace
{
std::string parseVars(
const std::string& text, const mola::YAMLParseOptions& opts)
{
MRPT_TRY_START
Expand All @@ -106,30 +111,43 @@ static std::string parseEnvVars(

const auto [varname, defaultValue] = splitVerticalBar(varnameOrg);

// 1st try: match to env vars
std::string varvalue;
const char* v = ::getenv(varname.c_str());
if (v != nullptr)
{ // match:
varvalue = std::string(v);
}
else
{
// Handle special variable names:
// 2nd try: handle special variable names:
// ${CURRENT_YAML_FILE_PATH}
if (varname == "CURRENT_YAML_FILE_PATH")
varvalue = opts.includesBasePath;
else if (!defaultValue.empty()) { varvalue = defaultValue; }
else
{
THROW_EXCEPTION_FMT(
"YAML parseEnvVars(): Undefined environment variable: ${%s}",
varname.c_str());
}
// 3rd try: custom user variables
if (auto it = opts.variables.find(varname);
it != opts.variables.end())
{
varvalue = it->second;
}
else
// 4th: default:
if (!defaultValue.empty()) { varvalue = defaultValue; }
else
{
THROW_EXCEPTION_FMT(
"YAML parseEnvVars(): Undefined environment variable: "
"${%s}",
varname.c_str());
}
}

return parseEnvVars(pre + varvalue + post.substr(post_end + 1), opts);
return parseVars(pre + varvalue + post.substr(post_end + 1), opts);
MRPT_TRY_END
}

static std::string parseCmdRuns(
std::string parseCmdRuns(
const std::string& text, const mola::YAMLParseOptions& opts)
{
MRPT_TRY_START
Expand Down Expand Up @@ -167,7 +185,7 @@ static std::string parseCmdRuns(
MRPT_TRY_END
}

static void recursiveParseNodeForIncludes(
void recursiveParseNodeForIncludes(
yaml::node_t& n, const mola::YAMLParseOptions& opts)
{
if (n.isScalar())
Expand Down Expand Up @@ -241,7 +259,7 @@ static void recursiveParseNodeForIncludes(
}
}

static std::string parseIncludes(
std::string parseIncludes(
const std::string& text, const mola::YAMLParseOptions& opts)
{
MRPT_TRY_START
Expand All @@ -255,6 +273,8 @@ static std::string parseIncludes(
MRPT_TRY_END
}

} // namespace

mrpt::containers::yaml mola::parse_yaml(
const mrpt::containers::yaml& input, const mola::YAMLParseOptions& opts)
{
Expand All @@ -274,7 +294,7 @@ std::string mola::parse_yaml(
if (opts.doCmdRuns) s = parseCmdRuns(s, opts);

// 3) Parse "${}"s
if (opts.doEnvVars) s = parseEnvVars(s, opts);
if (opts.doEnvVars) s = parseVars(s, opts);

return s;
}
Expand Down
41 changes: 37 additions & 4 deletions mola_yaml/tests/test-yaml-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

using namespace std::string_literals;

static void test_yaml2string()
namespace
{
void test_yaml2string()
{
{
const auto data = mrpt::containers::yaml::Map({{"A", 1.0}, {"B", 3}});
Expand All @@ -43,9 +45,10 @@ b: "foo"
- c
d:
va: 'z'
e: '${foo|default1}'
)###";

static void test_parseSimple()
void test_parseSimple()
{
{
const auto y = mrpt::containers::yaml::FromText(txt1);
Expand All @@ -57,7 +60,35 @@ static void test_parseSimple()
}
}

static void test_parseIncludes()
void test_parseCustomVars()
{
{
const auto y = mola::parse_yaml(mrpt::containers::yaml::FromText(txt1));
ASSERT_(y.isMap());
ASSERT_EQUAL_(y["e"].as<std::string>(), "default1");
}
{
mola::YAMLParseOptions opts;
opts.doEnvVars = false;

const auto y =
mola::parse_yaml(mrpt::containers::yaml::FromText(txt1), opts);
ASSERT_(y.isMap());
ASSERT_EQUAL_(y["e"].as<std::string>(), "${foo|default1}");
}
{
mola::YAMLParseOptions opts;
const std::string se = "Something Else";
opts.variables["foo"] = se;

const auto y =
mola::parse_yaml(mrpt::containers::yaml::FromText(txt1), opts);
ASSERT_(y.isMap());
ASSERT_EQUAL_(y["e"].as<std::string>(), se);
}
}

void test_parseIncludes()
{
{
const auto file = MOLA_MODULE_SOURCE_DIR + "/test_include1.yaml"s;
Expand Down Expand Up @@ -97,6 +128,8 @@ static void test_parseIncludes()
}
}

} // namespace

MRPT_TODO("Possible bug: #$include{} shouldn't be parsed")
MRPT_TODO("bug: #${var} shouldn't be parsed")

Expand All @@ -107,7 +140,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
test_yaml2string();
test_parseSimple();
test_parseIncludes();
// test_parseEnvSimple();
test_parseCustomVars();

std::cout << "Test successful." << std::endl;
}
Expand Down

0 comments on commit ea23b22

Please sign in to comment.