add yaml::stringify
This commit is contained in:
parent
2aea19febd
commit
396fab02b3
@ -1,6 +1,8 @@
|
||||
#include "yaml.hpp"
|
||||
#include "BasicParser.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
using namespace yaml;
|
||||
|
||||
namespace {
|
||||
@ -316,3 +318,142 @@ dv::value yaml::parse(std::string_view filename, std::string_view source) {
|
||||
dv::value yaml::parse(std::string_view source) {
|
||||
return parse("[string]", source);
|
||||
}
|
||||
|
||||
static void add_indent(std::stringstream& ss, int indent) {
|
||||
for (int i = 0; i < indent; i++) {
|
||||
ss << " ";
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_string(
|
||||
std::stringstream& ss, const std::string& string, int indent
|
||||
) {
|
||||
bool has_spec_chars = false;
|
||||
bool multiline = false;
|
||||
for (char c : string) {
|
||||
if (c < ' ' || c == '"' || c == '\'') {
|
||||
has_spec_chars = true;
|
||||
if (multiline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == '\n') {
|
||||
multiline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (multiline) {
|
||||
ss << "|-\n";
|
||||
size_t offset = 0;
|
||||
size_t newoffset = 0;
|
||||
|
||||
do {
|
||||
offset = newoffset;
|
||||
if (offset == string.length() - 1 && string[offset] == '\n') {
|
||||
break;
|
||||
}
|
||||
add_indent(ss, indent);
|
||||
newoffset = string.find('\n', offset + 1);
|
||||
if (newoffset == std::string::npos) {
|
||||
ss << string.substr(offset);
|
||||
break;
|
||||
} else {
|
||||
ss << string.substr(offset + 1, newoffset - offset - 1);
|
||||
}
|
||||
ss << '\n';
|
||||
} while (true);
|
||||
} else {
|
||||
if (has_spec_chars || string.empty()) {
|
||||
ss << util::escape(string, false);
|
||||
} else {
|
||||
ss << string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void to_string(
|
||||
std::stringstream& ss,
|
||||
const dv::value& value,
|
||||
int indent,
|
||||
bool eliminateIndent = false
|
||||
) {
|
||||
using dv::value_type;
|
||||
|
||||
switch (value.getType()) {
|
||||
case value_type::string:
|
||||
insert_string(ss, value.asString(), indent);
|
||||
break;
|
||||
case value_type::number:
|
||||
ss << std::setprecision(15) << value.asNumber();
|
||||
break;
|
||||
case value_type::integer:
|
||||
ss << value.asInteger();
|
||||
break;
|
||||
case value_type::boolean:
|
||||
ss << (value.asBoolean() ? "true" : "false");
|
||||
break;
|
||||
case value_type::none:
|
||||
ss << "null";
|
||||
break;
|
||||
case value_type::object: {
|
||||
if (value.empty()) {
|
||||
ss << "{}";
|
||||
break;
|
||||
}
|
||||
bool first = true;
|
||||
for (const auto& [key, elem] : value.asObject()) {
|
||||
if (!first) {
|
||||
ss << '\n';
|
||||
}
|
||||
if (!eliminateIndent) {
|
||||
add_indent(ss, indent);
|
||||
} else {
|
||||
eliminateIndent = false;
|
||||
}
|
||||
ss << key << ": ";
|
||||
if ((elem.isObject() || elem.isList()) && !elem.empty()) {
|
||||
ss << "\n";
|
||||
to_string(ss, elem, indent + 1);
|
||||
} else {
|
||||
to_string(ss, elem, indent + 1);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case value_type::list: {
|
||||
if (value.empty()) {
|
||||
ss << "[]";
|
||||
break;
|
||||
}
|
||||
bool first = true;
|
||||
for (const auto& elem : value) {
|
||||
if (!first) {
|
||||
ss << '\n';
|
||||
}
|
||||
if (!eliminateIndent) {
|
||||
add_indent(ss, indent);
|
||||
} else {
|
||||
eliminateIndent = false;
|
||||
}
|
||||
ss << "- ";
|
||||
to_string(ss, elem, indent + 1, true);
|
||||
first = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case value_type::bytes: {
|
||||
const auto& bytes = value.asBytes();
|
||||
auto b64 = util::base64_encode(bytes.data(), bytes.size());
|
||||
b64 = util::join(util::split_by_n(b64, 64), '\n');
|
||||
insert_string(ss, b64, indent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string yaml::stringify(const dv::value& value) {
|
||||
std::stringstream ss;
|
||||
to_string(ss, value, 0);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -7,4 +7,6 @@
|
||||
namespace yaml {
|
||||
dv::value parse(std::string_view filename, std::string_view source);
|
||||
dv::value parse(std::string_view source);
|
||||
|
||||
std::string stringify(const dv::value& value);
|
||||
}
|
||||
|
||||
@ -115,4 +115,29 @@ namespace util {
|
||||
std::string format_data_size(size_t size);
|
||||
|
||||
std::pair<std::string, std::string> split_at(std::string_view view, char c);
|
||||
|
||||
template <typename CharT>
|
||||
std::vector<std::basic_string<CharT>> split_by_n(
|
||||
const std::basic_string<CharT>& str, size_t n
|
||||
) {
|
||||
std::vector<std::basic_string<CharT>> result;
|
||||
for (size_t i = 0; i < str.length(); i += n) {
|
||||
result.push_back(str.substr(i, n));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
std::basic_string<CharT> join(
|
||||
const std::vector<std::basic_string<CharT>>& strings, CharT delimiter
|
||||
) {
|
||||
std::basic_stringstream<CharT> ss;
|
||||
for (size_t i = 0; i < strings.size(); ++i) {
|
||||
ss << strings[i];
|
||||
if (i != strings.size() - 1) {
|
||||
ss << delimiter;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ TEST(YAML, EncodeDecode) {
|
||||
auto filename = "root:.github/workflows/windows-clang.yml";
|
||||
try {
|
||||
auto value = yaml::parse(io::read_string(filename));
|
||||
std::cout << json::stringify(value, true) << std::endl;
|
||||
std::cout << yaml::stringify(value) << std::endl;
|
||||
} catch (const parsing_error& error) {
|
||||
std::cerr << error.errorLog() << std::endl;
|
||||
throw error;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user