DAW JSON Link
Loading...
Searching...
No Matches
daw_json_reflection.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
14
15#if defined( DAW_JSON_HAS_REFLECTION )
18
19#include <daw/daw_bind_args_at.h>
20#include <daw/daw_concepts.h>
21#include <daw/daw_move.h>
22#include <daw/daw_pipelines.h>
23
24#include <cstddef>
25#include <meta>
26#include <string_view>
27#include <tuple>
28#include <type_traits>
29#include <utility>
30#include <vector>
31
32namespace daw::json::inline DAW_JSON_VER::refl_details {
33 template<typename D>
34 struct refl_ignored_value : refl_annotation_base, refl_ignored_base {
35 D default_value;
36
37 explicit constexpr refl_ignored_value( D value )
38 : default_value( value ) {}
39
40 template<typename T>
41 constexpr operator T( ) const {
42 if constexpr( std::is_convertible_v<D, T> ) {
43 return default_value;
44 } else if constexpr( requires( D v ) {
45 { v( ) } -> std::convertible_to<T>;
46 } ) {
47 return default_value( );
48 }
49 }
50 };
51
52 struct refl_ignored : refl_annotation_base, refl_ignored_base {
53 explicit consteval refl_ignored( ) = default;
54
55 template<typename T>
56 static consteval auto operator( )( T &&rhs ) {
57 return refl_ignored_value<T>{ DAW_FWD( rhs ) };
58 }
59
60 template<typename T>
61 consteval operator T( ) const {
62 return T{ };
63 }
64 };
65
66 struct member_reflection_t {};
67
68 template<typename T, std::size_t Idx>
69 using submember_type_t = std::tuple_element_t<Idx, to_tuple_t<T>>;
70
71 template<EnumType E>
72 constexpr E enum_from_string( std::string_view name ) {
73 static constexpr auto enums =
74 reflect_constant_array( enumerators_of( ^^E ) );
75 template for( constexpr auto enumerator : [:enums:] ) {
76 // TODO add name formatting e.g lower/upper/first capital
77 if( name == identifier_of( enumerator ) ) {
78 return [:enumerator:];
79 }
80 }
81 daw_json_error( true, ErrorReason::InvalidString );
82 }
83
84 template<EnumType E>
85 constexpr std::string_view enum_to_string( E value ) {
86 static constexpr auto enums =
87 reflect_constant_array( enumerators_of( ^^E ) );
88 template for( constexpr auto enumerator : [:enums:] ) {
89 if( value == [:enumerator:] ) {
90 return identifier_of( enumerator );
91 }
92 }
93 daw_json_error( true, ErrorReason::CouldNotFindEnumeratorForValue );
94 return std::string_view{ };
95 }
96
97 template<EnumType E>
98 struct reflect_enum_as_string {
99 static constexpr E operator( )( std::string_view name ) {
100 return enum_from_string<E>( name );
101 }
102
103 static constexpr std::string_view operator( )( E value ) {
104 return enum_to_string( value );
105 }
106 };
107
110 template<EnumType E, json_options_t NumberOptions>
111 struct reflect_enum_as_string_or_number {
112 using number_t = std::underlying_type_t<E>;
113 using json_number_t = json_number_no_name<number_t, NumberOptions>;
114
117 static constexpr E operator( )( std::string_view json_value ) {
118 daw_json_ensure( not json_value.empty( ),
119 ErrorReason::UnexpectedEndOfData );
120 if( json_value.front( ) == '"' ) {
121 auto const name_end = json_value.find( '"', 1 );
122 daw_json_ensure( name_end != std::string_view::npos,
123 ErrorReason::InvalidString );
124 auto const name = json_value.substr( 1, name_end - 1 );
125 // Identifiers cannot start with these, allow the number options to
126 // decide if a number in a string is valid
127 if( name.empty( ) or
128 not( name.front( ) == '-' or name.front( ) == '+' or
129 ( name.front( ) >= '0' and name.front( ) <= '9' ) ) ) {
130 return enum_from_string<E>( name );
131 }
132 }
133 return static_cast<E>( from_json<json_number_t>( json_value ) );
134 }
135
136 template<typename WritableType>
137 static constexpr WritableType operator( )( WritableType it, E value ) {
138 static constexpr auto enums =
139 reflect_constant_array( enumerators_of( ^^E ) );
140 template for( constexpr auto enumerator : [:enums:] ) {
141 if( value == [:enumerator:] ) {
142 static constexpr std::string_view name = identifier_of( enumerator );
143 it.put( '"' );
144 it.write( name );
145 it.put( '"' );
146 return it;
147 }
148 }
149 return json_details::member_to_string<json_number_t>(
150 std::move( it ), static_cast<number_t>( value ) );
151 }
152 };
153
154 template<typename T, std::size_t... Idx>
155 consteval std::meta::info
156 get_json_members_list_impl( std::index_sequence<Idx...> ) {
157 return ^^json_member_list<get_member_link_t<T, Idx>...>;
158 }
159
160 template<typename T>
161 consteval std::meta::info get_json_member_list( ) {
162 static constexpr auto sz =
163 get_non_ignored_reflectible_members<T>( ).size( );
164 return get_json_members_list_impl<T>( std::make_index_sequence<sz>{ } );
165 }
166
167 template<EnumType E, json_options_t Options>
168 struct enum_string : json_custom_no_name<E, reflect_enum_as_string<E>,
169 reflect_enum_as_string<E>, Options> {
170 };
171
172 template<EnumType E, json_options_t NumberOptions>
173 struct enum_string_or_number
175 E, reflect_enum_as_string_or_number<E, NumberOptions>,
176 reflect_enum_as_string_or_number<E, NumberOptions>,
177 options::json_custom_opt( options::JsonCustomTypes::Any )> {};
178
179 template<Reflectable T>
180 consteval bool has_reflected_submembers( ) {
181 static constexpr auto members = [:as_stdarray(
182 get_reflectible_members<T>( ) ):];
183 template for( constexpr auto member : members ) {
184 if( not annotations_of_with_base_type( member, ^^refl_annotation_base )
185 .empty( ) ) {
186 return true;
187 }
188 }
189 return false;
190 }
191
192} // namespace daw::json::inline DAW_JSON_VER::refl_details
193namespace daw::json::inline DAW_JSON_VER {
194
198 struct reflect_base_t {
199
202 template<json_name Name>
203 static constexpr auto rename = refl_details::refl_rename{ Name.m_data };
204
205 template<typename JsonMember>
206 static constexpr auto map_as = refl_details::refl_map_as{ ^^JsonMember };
207
211 static constexpr auto ignored = refl_details::refl_ignored{ };
212
214 template<json_options_t Options>
215 static constexpr auto enum_string_with_options =
216 refl_details::refl_enum_string{ Options };
217
218 static constexpr auto enum_string =
219 enum_string_with_options<json_custom_opts_def>;
220
224 template<json_options_t NumberOptions>
225 static constexpr auto enum_string_or_number_with_options =
226 refl_details::refl_enum_string_or_number{ NumberOptions };
227
228 static constexpr auto enum_string_or_number =
229 enum_string_or_number_with_options<number_opts_def>;
230 };
231 struct reflect_all_t : reflect_base_t, refl_details::reflect_all_t {};
232 struct reflect_t : reflect_base_t {
233 static constexpr auto unchecked = reflect_all_t{ };
234 };
235
236 inline constexpr auto reflect = reflect_t{ };
237} // namespace daw::json::inline DAW_JSON_VER
238
239namespace daw::json::inline DAW_JSON_VER {
240 template<typename T>
241 inline constexpr bool enable_reflection_for = false;
242
243 template<typename T>
244 concept ReflectionEnabled =
245 std::is_class_v<T> and
246 ( enable_reflection_for<T> or
247 refl_details::has_annotation<reflect_base_t, T>( ) or
248 refl_details::has_reflected_submembers<T>( ) );
249
250 template<ReflectionEnabled T>
251 struct json_data_contract<T> {
252 using constructor_t = refl_details::reflected_constructor<T>;
253
254 using type = typename[:refl_details::get_json_member_list<T>( ):];
255
256 DAW_ATTRIB_INLINE static constexpr auto to_json_data( T const &value ) {
257 return refl_details::to_tuple( value );
258 }
259 };
260} // namespace daw::json::inline DAW_JSON_VER
261
262#endif
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
DAW_ATTRIB_NOINLINE void daw_json_error(bool b, ErrorReason reason)
basic_json_value<> json_value
An untyped JSON value.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20