| // 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); |
| } |