1// Copyright 2016 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#ifndef MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
6#define MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
7
8#include <map>
9#include <string>
10#include <type_traits>
11#include <vector>
12
13#include "mojo/public/cpp/bindings/array.h"
14#include "mojo/public/cpp/bindings/map.h"
15#include "mojo/public/cpp/bindings/string.h"
16
17// Two functions are defined to facilitate conversion between
18// mojo::Array/Map/String and std::vector/map/string: mojo::UnwrapToSTLType()
19// recursively convert mojo types to STL types; mojo::WrapSTLType() does the
20// opposite. For example:
21//   mojo::Array<mojo::Map<mojo::String, mojo::Array<int32_t>>> mojo_obj;
22//
23//   std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
24//       mojo::UnwrapToSTLType(std::move(mojo_obj));
25//
26//   mojo_obj = mojo::WrapSTLType(std::move(stl_obj));
27//
28// Notes:
29//   - The conversion moves as much contents as possible. The two functions both
30//     take an rvalue ref as input in order to avoid accidental copies.
31//   - Because std::vector/map/string cannot express null, UnwrapToSTLType()
32//     converts null mojo::Array/Map/String to empty.
33//   - The recursive conversion stops at any types that are not the types listed
34//     above. For example, unwrapping mojo::Array<StructContainingMojoMap> will
35//     result in std::vector<StructContainingMojoMap>. It won't convert
36//     mojo::Map inside the struct.
37
38namespace mojo {
39namespace internal {
40
41template <typename T>
42struct UnwrapTraits;
43
44template <typename T>
45struct UnwrapShouldGoDeeper {
46 public:
47  static const bool value =
48      !std::is_same<T, typename UnwrapTraits<T>::Type>::value;
49};
50
51template <typename T>
52struct UnwrapTraits {
53 public:
54  using Type = T;
55  static Type Unwrap(T input) { return input; }
56};
57
58template <typename T>
59struct UnwrapTraits<Array<T>> {
60 public:
61  using Type = std::vector<typename UnwrapTraits<T>::Type>;
62
63  static Type Unwrap(Array<T> input) {
64    return Helper<T>::Run(std::move(input));
65  }
66
67 private:
68  template <typename U, bool should_go_deeper = UnwrapShouldGoDeeper<U>::value>
69  struct Helper {};
70
71  template <typename U>
72  struct Helper<U, true> {
73   public:
74    static Type Run(Array<T> input) {
75      Type output;
76      output.reserve(input.size());
77      for (size_t i = 0; i < input.size(); ++i)
78        output.push_back(UnwrapTraits<T>::Unwrap(std::move(input[i])));
79      return output;
80    }
81  };
82
83  template <typename U>
84  struct Helper<U, false> {
85   public:
86    static Type Run(Array<T> input) { return input.PassStorage(); }
87  };
88};
89
90template <typename K, typename V>
91struct UnwrapTraits<Map<K, V>> {
92 public:
93  using Type =
94      std::map<typename UnwrapTraits<K>::Type, typename UnwrapTraits<V>::Type>;
95
96  static Type Unwrap(Map<K, V> input) {
97    return Helper<K, V>::Run(std::move(input));
98  }
99
100 private:
101  template <typename X,
102            typename Y,
103            bool should_go_deeper = UnwrapShouldGoDeeper<X>::value ||
104                                    UnwrapShouldGoDeeper<Y>::value>
105  struct Helper {};
106
107  template <typename X, typename Y>
108  struct Helper<X, Y, true> {
109   public:
110    static Type Run(Map<K, V> input) {
111      std::map<K, V> input_storage = input.PassStorage();
112      Type output;
113      for (auto& pair : input_storage) {
114        output.insert(
115            std::make_pair(UnwrapTraits<K>::Unwrap(pair.first),
116                           UnwrapTraits<V>::Unwrap(std::move(pair.second))));
117      }
118      return output;
119    }
120  };
121
122  template <typename X, typename Y>
123  struct Helper<X, Y, false> {
124   public:
125    static Type Run(Map<K, V> input) { return input.PassStorage(); }
126  };
127};
128
129template <>
130struct UnwrapTraits<String> {
131 public:
132  using Type = std::string;
133
134  static std::string Unwrap(const String& input) { return input; }
135};
136
137template <typename T>
138struct WrapTraits;
139
140template <typename T>
141struct WrapShouldGoDeeper {
142 public:
143  static const bool value =
144      !std::is_same<T, typename WrapTraits<T>::Type>::value;
145};
146
147template <typename T>
148struct WrapTraits {
149 public:
150  using Type = T;
151
152  static T Wrap(T input) { return input; }
153};
154
155template <typename T>
156struct WrapTraits<std::vector<T>> {
157 public:
158  using Type = Array<typename WrapTraits<T>::Type>;
159
160  static Type Wrap(std::vector<T> input) {
161    return Helper<T>::Run(std::move(input));
162  }
163
164 private:
165  template <typename U, bool should_go_deeper = WrapShouldGoDeeper<U>::value>
166  struct Helper {};
167
168  template <typename U>
169  struct Helper<U, true> {
170   public:
171    static Type Run(std::vector<T> input) {
172      std::vector<typename WrapTraits<T>::Type> output_storage;
173      output_storage.reserve(input.size());
174      for (auto& element : input)
175        output_storage.push_back(WrapTraits<T>::Wrap(std::move(element)));
176      return Type(std::move(output_storage));
177    }
178  };
179
180  template <typename U>
181  struct Helper<U, false> {
182   public:
183    static Type Run(std::vector<T> input) { return Type(std::move(input)); }
184  };
185};
186
187template <typename K, typename V>
188struct WrapTraits<std::map<K, V>> {
189 public:
190  using Type = Map<typename WrapTraits<K>::Type, typename WrapTraits<V>::Type>;
191
192  static Type Wrap(std::map<K, V> input) {
193    return Helper<K, V>::Run(std::move(input));
194  }
195
196 private:
197  template <typename X,
198            typename Y,
199            bool should_go_deeper =
200                WrapShouldGoDeeper<X>::value || WrapShouldGoDeeper<Y>::value>
201  struct Helper {};
202
203  template <typename X, typename Y>
204  struct Helper<X, Y, true> {
205   public:
206    static Type Run(std::map<K, V> input) {
207      Type output;
208      for (auto& pair : input) {
209        output.insert(WrapTraits<K>::Wrap(pair.first),
210                      WrapTraits<V>::Wrap(std::move(pair.second)));
211      }
212      return output;
213    }
214  };
215
216  template <typename X, typename Y>
217  struct Helper<X, Y, false> {
218   public:
219    static Type Run(std::map<K, V> input) { return Type(std::move(input)); }
220  };
221};
222
223template <>
224struct WrapTraits<std::string> {
225 public:
226  using Type = String;
227
228  static String Wrap(const std::string& input) { return input; }
229};
230
231}  // namespace internal
232
233template <typename T>
234typename internal::UnwrapTraits<T>::Type UnwrapToSTLType(T&& input) {
235  return internal::UnwrapTraits<T>::Unwrap(std::move(input));
236}
237
238template <typename T>
239typename internal::WrapTraits<T>::Type WrapSTLType(T&& input) {
240  return internal::WrapTraits<T>::Wrap(std::move(input));
241}
242
243}  // namespace mojo
244
245#endif  // MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
246