1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef THRIFT_FATAL_PRETTY_PRINT_INL_POST_H_
18 #define THRIFT_FATAL_PRETTY_PRINT_INL_POST_H_ 1
19 
20 #include <fatal/type/enum.h>
21 #include <fatal/type/search.h>
22 #include <fatal/type/variant_traits.h>
23 #include <folly/String.h>
24 
25 namespace apache {
26 namespace thrift {
27 namespace detail {
28 
29 /**
30  * Pretty print specialization for enumerations.
31  *
32  * @author: Marcelo Juchem <marcelo@fb.com>
33  */
34 template <>
35 struct pretty_print_impl<type_class::enumeration> {
36   template <typename OutputStream, typename T>
37   static void print(OutputStream& out, T const& what) {
38     out << fatal::enum_to_string(what, nullptr);
39   }
40 };
41 
42 /**
43  * Pretty print specialization for lists.
44  *
45  * @author: Marcelo Juchem <marcelo@fb.com>
46  */
47 template <typename ValueTypeClass>
48 struct pretty_print_impl<type_class::list<ValueTypeClass>> {
49   template <typename OutputStream, typename T>
50   static void print(OutputStream& out, T const& what) {
51     out << "<list>[";
52     if (!what.empty()) {
53       out.newline();
54       auto const size = what.size();
55       std::size_t index = 0;
56       for (auto const& i : what) {
57         auto scope = out.start_scope();
58         scope << index << ": ";
59         pretty_print_impl<ValueTypeClass>::print(scope, i);
60         if (++index < size) {
61           scope << ',';
62         }
63         scope.newline();
64       }
65     }
66     out << ']';
67   }
68 };
69 
70 /**
71  * Pretty print specialization for maps.
72  *
73  * @author: Marcelo Juchem <marcelo@fb.com>
74  */
75 template <typename KeyTypeClass, typename MappedTypeClass>
76 struct pretty_print_impl<type_class::map<KeyTypeClass, MappedTypeClass>> {
77   template <typename OutputStream, typename T>
78   static void print(OutputStream& out, T const& what) {
79     out << "<map>{";
80     if (!what.empty()) {
81       out.newline();
82       auto const size = what.size();
83       std::size_t index = 0;
84       for (auto const& i : what) {
85         auto scope = out.start_scope();
86         pretty_print_impl<KeyTypeClass>::print(scope, i.first);
87         scope << ": ";
88         pretty_print_impl<MappedTypeClass>::print(scope, i.second);
89         if (++index < size) {
90           scope << ',';
91         }
92         scope.newline();
93       }
94     }
95     out << '}';
96   }
97 };
98 
99 /**
100  * Pretty print specialization for sets.
101  *
102  * @author: Marcelo Juchem <marcelo@fb.com>
103  */
104 template <typename ValueTypeClass>
105 struct pretty_print_impl<type_class::set<ValueTypeClass>> {
106   template <typename OutputStream, typename T>
107   static void print(OutputStream& out, T const& what) {
108     out << "<set>{";
109     if (!what.empty()) {
110       out.newline();
111       auto const size = what.size();
112       std::size_t index = 0;
113       for (auto const& i : what) {
114         auto scope = out.start_scope();
115         pretty_print_impl<ValueTypeClass>::print(scope, i);
116         if (++index < size) {
117           scope << ',';
118         }
119         scope.newline();
120       }
121     }
122     out << '}';
123   }
124 };
125 
126 /**
127  * Thrift structures and unions may contain members that are wrapped in smart
128  * pointers. This class helps decode those.
129  */
130 struct pretty_print_structure_with_pointers {
131  protected:
132   template <typename TypeClass, typename OutputStream, typename T>
133   static void recurse_into(OutputStream& out, T const& member) {
134     pretty_print_impl<TypeClass>::print(out, member);
135   }
136 
137   template <typename TypeClass, typename OutputStream, typename T>
138   static void recurse_into_ptr(OutputStream& out, T const* pMember) {
139     if (!pMember) {
140       out << "null";
141       return;
142     }
143     recurse_into<TypeClass>(out, *pMember);
144   }
145 
146   template <typename TypeClass, typename OutputStream, typename T>
147   static void recurse_into(
148       OutputStream& out,
149       optional_boxed_field_ref<boxed_value_ptr<T> const&> pMember) {
150     return recurse_into_ptr<TypeClass>(out, pMember ? &*pMember : nullptr);
151   }
152 
153   template <typename TypeClass, typename OutputStream, typename T>
154   static void recurse_into(
155       OutputStream& out, std::shared_ptr<T> const& pMember) {
156     return recurse_into_ptr<TypeClass>(out, pMember.get());
157   }
158 
159   template <typename TypeClass, typename OutputStream, typename T>
160   static void recurse_into(
161       OutputStream& out, std::unique_ptr<T> const& pMember) {
162     return recurse_into_ptr<TypeClass>(out, pMember.get());
163   }
164 };
165 
166 /**
167  * Pretty print specialization for variants (Thrift unions).
168  *
169  * @author: Marcelo Juchem <marcelo@fb.com>
170  */
171 template <>
172 struct pretty_print_impl<type_class::variant>
173     : pretty_print_structure_with_pointers {
174   template <typename OutputStream, typename T>
175   static void print(OutputStream& out, T const& what) {
176     using descriptors = typename fatal::variant_traits<T>::descriptors;
177     out << "<variant>{";
178     using key = fatal::get_type::id;
179     fatal::scalar_search<descriptors, key>(what.getType(), [&](auto indexed) {
180       using descriptor = decltype(fatal::tag_type(indexed));
181       auto scope = out.start_scope();
182       scope.newline();
183       scope << fatal::enum_to_string(what.getType(), nullptr) << ": ";
184       recurse_into<typename descriptor::metadata::type_class>(
185           scope, typename descriptor::getter()(what));
186       scope.newline();
187     });
188     out << '}';
189   }
190 };
191 
192 /*
193  * Pretty print specialization for structures.
194  *
195  * @author: Marcelo Juchem <marcelo@fb.com>
196  */
197 template <>
198 struct pretty_print_impl<type_class::structure>
199     : pretty_print_structure_with_pointers {
200   template <typename OutputStream, typename T>
201   static void print(OutputStream& out, T const& what) {
202     out << "<struct>{";
203     out.newline();
204     using info = reflect_struct<T>;
205     fatal::foreach<typename info::members>([&](auto indexed) {
206       constexpr auto size = fatal::size<typename info::members>::value;
207       using member = decltype(fatal::tag_type(indexed));
208       if (member::optional::value == optionality::optional &&
209           !member::is_set(what)) {
210         return;
211       }
212       auto const index = fatal::tag_index(indexed);
213       auto scope = out.start_scope();
214       scope << fatal::z_data<typename member::name>() << ": ";
215       recurse_into<typename member::type_class>(
216           scope, typename member::getter{}(what));
217       if (index + 1 < size) {
218         scope << ',';
219       }
220       scope.newline();
221     });
222     out << '}';
223   }
224 };
225 
226 /**
227  * Pretty print specialization for strings.
228  *
229  * @author: Marcelo Juchem <marcelo@fb.com>
230  */
231 template <>
232 struct pretty_print_impl<type_class::string> {
233   template <typename OutputStream, typename T>
234   static void print(OutputStream& out, T const& what) {
235     out << '"' << folly::cEscape<T>(what) << '"';
236   }
237 };
238 
239 /**
240  * Pretty print fallback specialization.
241  *
242  * @author: Marcelo Juchem <marcelo@fb.com>
243  */
244 template <typename>
245 struct pretty_print_impl {
246   template <typename OutputStream, typename T>
247   static void print(OutputStream& out, T const& what) {
248     out << what;
249   }
250 
251   template <typename OutputStream>
252   static void print(OutputStream& out, bool const what) {
253     out << (what ? "true" : "false");
254   }
255 };
256 
257 } // namespace detail
258 } // namespace thrift
259 } // namespace apache
260 
261 #endif // THRIFT_FATAL_PRETTY_PRINT_INL_POST_H_
262