1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/output_conversion.h"
6
7 #include "gn/settings.h"
8 #include "gn/value.h"
9
10 namespace {
11
ToString(const Value & output,std::ostream & out)12 void ToString(const Value& output, std::ostream& out) {
13 out << output.ToString(false);
14 }
15
ToStringQuoted(const Value & output,std::ostream & out)16 void ToStringQuoted(const Value& output, std::ostream& out) {
17 out << "\"" << output.ToString(false) << "\"";
18 }
19
Indent(int indent,std::ostream & out)20 void Indent(int indent, std::ostream& out) {
21 for (int i = 0; i < indent; ++i)
22 out << " ";
23 }
24
25 // Forward declare so it can be used recursively.
26 void RenderScopeToJSON(const Value& output, std::ostream& out, int indent);
27
RenderListToJSON(const Value & output,std::ostream & out,int indent)28 void RenderListToJSON(const Value& output, std::ostream& out, int indent) {
29 assert(indent > 0);
30 bool first = true;
31 out << "[\n";
32 for (const auto& value : output.list_value()) {
33 if (!first)
34 out << ",\n";
35 Indent(indent, out);
36 if (value.type() == Value::SCOPE)
37 RenderScopeToJSON(value, out, indent + 1);
38 else if (value.type() == Value::LIST)
39 RenderListToJSON(value, out, indent + 1);
40 else
41 out << value.ToString(true);
42 first = false;
43 }
44 out << "\n";
45 Indent(indent - 1, out);
46 out << "]";
47 }
48
RenderScopeToJSON(const Value & output,std::ostream & out,int indent)49 void RenderScopeToJSON(const Value& output, std::ostream& out, int indent) {
50 assert(indent > 0);
51 Scope::KeyValueMap scope_values;
52 output.scope_value()->GetCurrentScopeValues(&scope_values);
53 bool first = true;
54 out << "{\n";
55 for (const auto& pair : scope_values) {
56 if (!first)
57 out << ",\n";
58 Indent(indent, out);
59 out << "\"" << pair.first << "\": ";
60 if (pair.second.type() == Value::SCOPE)
61 RenderScopeToJSON(pair.second, out, indent + 1);
62 else if (pair.second.type() == Value::LIST)
63 RenderListToJSON(pair.second, out, indent + 1);
64 else
65 out << pair.second.ToString(true);
66 first = false;
67 }
68 out << "\n";
69 Indent(indent - 1, out);
70 out << "}";
71 }
72
OutputListLines(const Value & output,std::ostream & out)73 void OutputListLines(const Value& output, std::ostream& out) {
74 assert(output.type() == Value::LIST);
75 const std::vector<Value>& list = output.list_value();
76 for (const auto& cur : list)
77 out << cur.ToString(false) << "\n";
78 }
79
OutputString(const Value & output,std::ostream & out)80 void OutputString(const Value& output, std::ostream& out) {
81 if (output.type() == Value::NONE)
82 return;
83 if (output.type() == Value::STRING) {
84 ToString(output, out);
85 return;
86 }
87 ToStringQuoted(output, out);
88 }
89
OutputValue(const Value & output,std::ostream & out)90 void OutputValue(const Value& output, std::ostream& out) {
91 if (output.type() == Value::NONE)
92 return;
93 if (output.type() == Value::STRING) {
94 ToStringQuoted(output, out);
95 return;
96 }
97 ToString(output, out);
98 }
99
100 // The direct Value::ToString call wraps the scope in '{}', which we don't want
101 // here for the top-level scope being output.
OutputScope(const Value & output,std::ostream & out)102 void OutputScope(const Value& output, std::ostream& out) {
103 Scope::KeyValueMap scope_values;
104 output.scope_value()->GetCurrentScopeValues(&scope_values);
105 for (const auto& pair : scope_values) {
106 out << " " << pair.first << " = " << pair.second.ToString(true) << "\n";
107 }
108 }
109
OutputDefault(const Value & output,std::ostream & out)110 void OutputDefault(const Value& output, std::ostream& out) {
111 if (output.type() == Value::LIST)
112 OutputListLines(output, out);
113 else
114 ToString(output, out);
115 }
116
OutputJSON(const Value & output,std::ostream & out)117 void OutputJSON(const Value& output, std::ostream& out) {
118 if (output.type() == Value::SCOPE) {
119 RenderScopeToJSON(output, out, /*indent=*/1);
120 return;
121 }
122 if (output.type() == Value::LIST) {
123 RenderListToJSON(output, out, /*indent=*/1);
124 return;
125 }
126 ToStringQuoted(output, out);
127 }
128
DoConvertValueToOutput(const Value & output,const std::string & output_conversion,const Value & original_output_conversion,std::ostream & out,Err * err)129 void DoConvertValueToOutput(const Value& output,
130 const std::string& output_conversion,
131 const Value& original_output_conversion,
132 std::ostream& out,
133 Err* err) {
134 if (output_conversion == "") {
135 OutputDefault(output, out);
136 } else if (output_conversion == "list lines") {
137 if (output.type() != Value::LIST) {
138 *err = Err(original_output_conversion, "Not a valid list.");
139 return;
140 }
141 OutputListLines(output, out);
142 } else if (output_conversion == "string") {
143 OutputString(output, out);
144 } else if (output_conversion == "value") {
145 OutputValue(output, out);
146 } else if (output_conversion == "json") {
147 OutputJSON(output, out);
148 } else if (output_conversion == "scope") {
149 if (output.type() != Value::SCOPE) {
150 *err = Err(original_output_conversion, "Not a valid scope.");
151 return;
152 }
153 OutputScope(output, out);
154 } else {
155 // If we make it here, we didn't match any of the valid options.
156 *err = Err(original_output_conversion, "Not a valid output_conversion.",
157 "Run `gn help io_conversion` to see your options.");
158 }
159 }
160
161 } // namespace
162
ConvertValueToOutput(const Settings * settings,const Value & output,const Value & output_conversion,std::ostream & out,Err * err)163 void ConvertValueToOutput(const Settings* settings,
164 const Value& output,
165 const Value& output_conversion,
166 std::ostream& out,
167 Err* err) {
168 if (output_conversion.type() == Value::NONE) {
169 OutputDefault(output, out);
170 return;
171 }
172 if (!output_conversion.VerifyTypeIs(Value::STRING, err))
173 return;
174
175 DoConvertValueToOutput(output, output_conversion.string_value(),
176 output_conversion, out, err);
177 }
178