blob: 04017dd4e2b6852ade8f4d33fd310439d5ae1b44 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gn/output_conversion.h"
#include "gn/settings.h"
#include "gn/value.h"
namespace {
void ToString(const Value& output, std::ostream& out) {
out << output.ToString(false);
}
void ToStringQuoted(const Value& output, std::ostream& out) {
out << "\"" << output.ToString(false) << "\"";
}
void Indent(int indent, std::ostream& out) {
for (int i = 0; i < indent; ++i)
out << " ";
}
// Forward declare so it can be used recursively.
void RenderScopeToJSON(const Value& output, std::ostream& out, int indent);
void RenderListToJSON(const Value& output, std::ostream& out, int indent) {
assert(indent > 0);
bool first = true;
out << "[\n";
for (const auto& value : output.list_value()) {
if (!first)
out << ",\n";
Indent(indent, out);
if (value.type() == Value::SCOPE)
RenderScopeToJSON(value, out, indent + 1);
else if (value.type() == Value::LIST)
RenderListToJSON(value, out, indent + 1);
else
out << value.ToString(true);
first = false;
}
out << "\n";
Indent(indent - 1, out);
out << "]";
}
void RenderScopeToJSON(const Value& output, std::ostream& out, int indent) {
assert(indent > 0);
Scope::KeyValueMap scope_values;
output.scope_value()->GetCurrentScopeValues(&scope_values);
bool first = true;
out << "{\n";
for (const auto& pair : scope_values) {
if (!first)
out << ",\n";
Indent(indent, out);
out << "\"" << pair.first << "\": ";
if (pair.second.type() == Value::SCOPE)
RenderScopeToJSON(pair.second, out, indent + 1);
else if (pair.second.type() == Value::LIST)
RenderListToJSON(pair.second, out, indent + 1);
else
out << pair.second.ToString(true);
first = false;
}
out << "\n";
Indent(indent - 1, out);
out << "}";
}
void OutputListLines(const Value& output, std::ostream& out) {
assert(output.type() == Value::LIST);
const std::vector<Value>& list = output.list_value();
for (const auto& cur : list)
out << cur.ToString(false) << "\n";
}
void OutputString(const Value& output, std::ostream& out) {
if (output.type() == Value::NONE)
return;
if (output.type() == Value::STRING) {
ToString(output, out);
return;
}
ToStringQuoted(output, out);
}
void OutputValue(const Value& output, std::ostream& out) {
if (output.type() == Value::NONE)
return;
if (output.type() == Value::STRING) {
ToStringQuoted(output, out);
return;
}
ToString(output, out);
}
// The direct Value::ToString call wraps the scope in '{}', which we don't want
// here for the top-level scope being output.
void OutputScope(const Value& output, std::ostream& out) {
Scope::KeyValueMap scope_values;
output.scope_value()->GetCurrentScopeValues(&scope_values);
for (const auto& pair : scope_values) {
out << " " << pair.first << " = " << pair.second.ToString(true) << "\n";
}
}
void OutputDefault(const Value& output, std::ostream& out) {
if (output.type() == Value::LIST)
OutputListLines(output, out);
else
ToString(output, out);
}
void OutputJSON(const Value& output, std::ostream& out) {
if (output.type() == Value::SCOPE) {
RenderScopeToJSON(output, out, /*indent=*/1);
return;
}
if (output.type() == Value::LIST) {
RenderListToJSON(output, out, /*indent=*/1);
return;
}
ToStringQuoted(output, out);
}
void DoConvertValueToOutput(const Value& output,
const std::string& output_conversion,
const Value& original_output_conversion,
std::ostream& out,
Err* err) {
if (output_conversion == "") {
OutputDefault(output, out);
} else if (output_conversion == "list lines") {
if (output.type() != Value::LIST) {
*err = Err(original_output_conversion, "Not a valid list.");
return;
}
OutputListLines(output, out);
} else if (output_conversion == "string") {
OutputString(output, out);
} else if (output_conversion == "value") {
OutputValue(output, out);
} else if (output_conversion == "json") {
OutputJSON(output, out);
} else if (output_conversion == "scope") {
if (output.type() != Value::SCOPE) {
*err = Err(original_output_conversion, "Not a valid scope.");
return;
}
OutputScope(output, out);
} else {
// If we make it here, we didn't match any of the valid options.
*err = Err(original_output_conversion, "Not a valid output_conversion.",
"Run gn help output_conversion to see your options.");
}
}
} // namespace
void ConvertValueToOutput(const Settings* settings,
const Value& output,
const Value& output_conversion,
std::ostream& out,
Err* err) {
if (output_conversion.type() == Value::NONE) {
OutputDefault(output, out);
return;
}
if (!output_conversion.VerifyTypeIs(Value::STRING, err))
return;
DoConvertValueToOutput(output, output_conversion.string_value(),
output_conversion, out, err);
}