1 /**
2 License:
3 	Boost Software License - Version 1.0 - August 17th, 2003
4 
5 	Permission is hereby granted, free of charge, to any person or organization
6 	obtaining a copy of the software and accompanying documentation covered by
7 	this license (the "Software") to use, reproduce, display, distribute,
8 	execute, and transmit the Software, and to prepare derivative works of the
9 	Software, and to permit third-parties to whom the Software is furnished to
10 	do so, all subject to the following:
11 
12 	The copyright notices in the Software and this entire statement, including
13 	the above license grant, this restriction and the following disclaimer,
14 	must be included in all copies of the Software, in whole or in part, and
15 	all derivative works of the Software, unless such copies or derivative
16 	works are solely in the form of machine-executable object code generated by
17 	a source language processor.
18 
19 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 	DEALINGS IN THE SOFTWARE.
26 
27 Authors:
28 	aermicioi
29 **/
30 module aermicioi.aedi_property_reader.core.core;
31 
32 import aermicioi.aedi.storage.storage;
33 import aermicioi.aedi.storage.locator;
34 import aermicioi.aedi_property_reader.core.convertor;
35 import aermicioi.aedi_property_reader.core.mapper;
36 import aermicioi.aedi_property_reader.core.document;
37 import std.meta;
38 import std.experimental.logger;
39 import std.exception;
40 
41 /**
42 Configuration context that is providing a nice api to add convertors into a container for a document.
43 **/
44 struct DocumentContainerBuilder(DocumentContainerType : Storage!(Convertor, string), alias AdvisedConvertor, F) {
45 
46 	alias FromType = F;
47 
48 	/**
49 	Configured container.
50 	**/
51     DocumentContainerType container;
52 
53 	bool containerAsConvertor = true;
54 
55 	this(DocumentContainerType container, bool containerAsConvertor = true) {
56 		this.container = container;
57 		this.containerAsConvertor = containerAsConvertor;
58 
59 		static foreach (Type; AliasSeq!(
60 			ubyte, byte, ushort, short, uint, int, ulong, long, float, double, real, char, wchar, dchar,
61 			ubyte[], byte[], ushort[], short[], uint[], int[], ulong[], long[], float[], double[], real[], char[], wchar[], dchar[], string, wstring, dstring
62 		)) {
63 			this.register!(Type, Type);
64 		}
65 	}
66 
67 	/**
68 	ditto
69 	**/
70     alias container this;
71 
72 	/**
73 	Associate a convertor to a field in document that has property path, to be used for conversion.
74 
75 	Params:
76 		To = the type of destination component that convertor should yield
77 		path = property path for a field in document.
78 	**/
79     ref typeof(this) register(To)(string path) {
80 		debug(trace) trace(
81 			"Injecting convertor for ",
82 			path,
83 			" in document container to convert to ",
84 			typeid(To),
85 			" using ",
86 			typeid(AdvisedConvertor!(To, FromType)())
87 		);
88 
89 		auto convertor = AdvisedConvertor!(To, FromType)();
90 
91         return this.register(convertor, path);
92     }
93 
94 	/**
95 	Associate a convertor to a type to be used by document when no specific convertor is provided for document field.
96 
97 	Params:
98 		To = the type of destination component that convertor should yield
99 	**/
100     ref typeof(this) register(To)() {
101 
102         return this.register!(To, To);
103     }
104 
105 	/**
106 	Associate a convertor to a type for an interface to be used by document when no specific convertor is provided for document field.
107 
108 	Params:
109 		Iface = interface for which the convertor is bound to.
110 		To = the type of destination component that convertor should yield
111 	**/
112     ref typeof(this) register(Iface, To : Iface)() {
113         import std.traits : fullyQualifiedName;
114 
115         return this.register!To(fullyQualifiedName!Iface);
116     }
117 
118 	ref typeof(this) register(Convertor convertor, string path) {
119 		static if (is(DocumentContainerType : Convertor)) {
120 			if (this.containerAsConvertor && (cast(CombinedConvertor) convertor !is null)) {
121 
122 				debug(trace) trace(
123 					"Detected that document container ", typeid(DocumentContainerType), " provides converting capabilities, and injected convertor ",
124 					convertor.classinfo, " accepts convertors, injecting container into convertor"
125 				);
126 				(cast(CombinedConvertor) convertor).add(container);
127 			}
128 		}
129 
130 		this.container.set(convertor, path);
131 
132 		return this;
133 	}
134 
135 	ref typeof(this) register(Convertor convertor) {
136 		return this.register(convertor, convertor.to.toString);
137 	}
138 
139     alias property = register;
140 }
141 
142 /**
143 Take a document container and create a configuration context for it.
144 
145 Params:
146 	container = container for which configuration context is created.
147 
148 Returns:
149 	DocumentContainerBuilder(DocumentContainerType, AdvisedConvertor, DocumentType, FromType) a configuration context
150 **/
151 auto configure(DocumentContainerType : DocumentContainer!(DocumentType, FromType), alias AdvisedConvertor, DocumentType, FromType)(DocumentContainerType container) {
152     return DocumentContainerBuilder!(DocumentContainerType, AdvisedConvertor, FromType)(container);
153 }
154 
155 /**
156 ditto
157 **/
158 auto configure(DocumentContainerType : AdvisedDocumentContainer!(DocumentType, FromType, AdvisedConvertor), DocumentType, FromType, alias AdvisedConvertor)(DocumentContainerType container) {
159 	return container.configure!(DocumentContainerType, AdvisedConvertor);
160 }
161 
162 mixin template MapperBuilderMixin(AdvisedConvertors...) {
163 
164 	struct MapperBuilderImpl(MapperType : Mapper!(To, From), To, From) {
165 
166 		MapperType mapper;
167 
168 		alias mapper this;
169 
170 		ref auto register(To, From)() {
171 			static foreach (AdvisedConvertor; AdvisedConvertors) {
172 				static if (is(typeof(AdvisedConvertor.AdvisedConvertorImplementation!(To, From)))) {
173 					enum converted = true;
174 					mapper.convertors = mapper.convertors ~ AdvisedConvertor.AdvisedConvertorImplementation!(To, From)();
175 				}
176 			}
177 
178 			static if (is(typeof(converted)) && !converted) {
179 				static assert("Could not configure mapper with a convertor");
180 			}
181 
182 			return this;
183 		}
184 
185 		struct WithFrom(With) {
186 
187 			ref auto register(To)() {
188 				return register!(To, From);
189 			}
190 		}
191 
192 		struct WithTo(With) {
193 
194 			ref auto register(From)() {
195 				return register!(To, From);
196 			}
197 		}
198 	}
199 
200 	auto configure(MapperType : Mapper!(To, From), To, From)(MapperType mapper) {
201 		return MapperBuilderImpl!MapperType(mapper);
202 	}
203 }
204 
205 import aermicioi.aedi_property_reader.core.std_conv;
206 import aermicioi.aedi_property_reader.core.convertor;
207 import aermicioi.aedi_property_reader.arg.convertor;
208 import aermicioi.aedi_property_reader.json.convertor;
209 import aermicioi.aedi_property_reader.xml.convertor;
210 import aermicioi.aedi_property_reader.yaml.convertor;
211 import aermicioi.aedi_property_reader.sdlang.convertor;
212 
213 mixin MapperBuilderMixin!(
214 	JsonConvertor,
215 	XmlConvertor,
216 	YamlConvertor,
217 	SdlangConvertor,
218 	ArgumentAdvisedConvertor,
219 	StdConvAdvisedConvertor,
220 	CompositeAdvisedConvertor
221 );