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