Skip to content

Commit 06e733b

Browse files
committed
Copy TypeAdapter.cs into TypeAdapters/TypeAdapterToTarget.cs
2 parents 9103951 + 2b09f5d commit 06e733b

File tree

1 file changed

+309
-0
lines changed

1 file changed

+309
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Linq;
5+
using System.Reflection;
6+
using Mapster.Models;
7+
8+
namespace Mapster
9+
{
10+
public static class TypeAdapter
11+
{
12+
public static ITypeAdapterBuilder<TSource> BuildAdapter<TSource>(this TSource source)
13+
{
14+
return new TypeAdapterBuilder<TSource>(source, TypeAdapterConfig.GlobalSettings);
15+
}
16+
17+
public static ITypeAdapterBuilder<TSource> BuildAdapter<TSource>(this TSource source, TypeAdapterConfig config)
18+
{
19+
return new TypeAdapterBuilder<TSource>(source, config);
20+
}
21+
22+
/// <summary>
23+
/// Adapt the source object to the destination type.
24+
/// </summary>
25+
/// <typeparam name="TDestination">Destination type.</typeparam>
26+
/// <param name="source">Source object to adapt.</param>
27+
/// <returns>Adapted destination type.</returns>
28+
[return: NotNullIfNotNull(nameof(source))]
29+
public static TDestination? Adapt<TDestination>(this object? source)
30+
{
31+
return Adapt<TDestination>(source, TypeAdapterConfig.GlobalSettings);
32+
}
33+
34+
/// <summary>
35+
/// Adapt the source object to the destination type.
36+
/// </summary>
37+
/// <typeparam name="TDestination">Destination type.</typeparam>
38+
/// <param name="source">Source object to adapt.</param>
39+
/// <param name="config">Configuration</param>
40+
/// <returns>Adapted destination type.</returns>
41+
[return: NotNullIfNotNull(nameof(source))]
42+
public static TDestination? Adapt<TDestination>(this object? source, TypeAdapterConfig config)
43+
{
44+
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
45+
if (source == null)
46+
return default;
47+
var type = source.GetType();
48+
var fn = config.GetDynamicMapFunction<TDestination>(type);
49+
return fn(source)!;
50+
}
51+
52+
/// <summary>
53+
/// Adapt the source object to the destination type.
54+
/// </summary>
55+
/// <typeparam name="TSource">Source type.</typeparam>
56+
/// <typeparam name="TDestination">Destination type.</typeparam>
57+
/// <param name="source">Source object to adapt.</param>
58+
/// <returns>Adapted destination type.</returns>
59+
public static TDestination Adapt<TSource, TDestination>(this TSource source)
60+
{
61+
return TypeAdapter<TSource, TDestination>.Map(source);
62+
}
63+
64+
/// <summary>
65+
/// Adapt the source object to the destination type.
66+
/// </summary>
67+
/// <typeparam name="TSource">Source type.</typeparam>
68+
/// <typeparam name="TDestination">Destination type.</typeparam>
69+
/// <param name="source">Source object to adapt.</param>
70+
/// <param name="config">Configuration</param>
71+
/// <returns>Adapted destination type.</returns>
72+
public static TDestination Adapt<TSource, TDestination>(this TSource source, TypeAdapterConfig config)
73+
{
74+
var fn = config.GetMapFunction<TSource, TDestination>();
75+
return fn(source);
76+
}
77+
78+
/// <summary>
79+
/// Adapt the source object to the existing destination object.
80+
/// </summary>
81+
/// <typeparam name="TSource">Source type.</typeparam>
82+
/// <typeparam name="TDestination">Destination type.</typeparam>
83+
/// <param name="source">Source object to adapt.</param>
84+
/// <param name="destination">The destination object to populate.</param>
85+
/// <returns>Adapted destination type.</returns>
86+
public static TDestination Adapt<TSource, TDestination>(this TSource source, TDestination destination)
87+
{
88+
return Adapt(source, destination, TypeAdapterConfig.GlobalSettings);
89+
}
90+
91+
/// <summary>
92+
/// Adapt the source object to the existing destination object.
93+
/// </summary>
94+
/// <typeparam name="TSource">Source type.</typeparam>
95+
/// <typeparam name="TDestination">Destination type.</typeparam>
96+
/// <param name="source">Source object to adapt.</param>
97+
/// <param name="destination">The destination object to populate.</param>
98+
/// <param name="config">Configuration</param>
99+
/// <returns>Adapted destination type.</returns>
100+
public static TDestination Adapt<TSource, TDestination>(this TSource source, TDestination destination, TypeAdapterConfig config)
101+
{
102+
var sourceType = source?.GetType();
103+
var destinationType = destination?.GetType();
104+
105+
if (sourceType == typeof(object)) // Infinity loop in ObjectAdapter if Runtime Type of source is Object
106+
return destination;
107+
108+
if (typeof(TSource) == typeof(object) || typeof(TDestination) == typeof(object))
109+
return UpdateFuncFromPackedinObject(source, destination, config, sourceType, destinationType);
110+
111+
var fn = config.GetMapToTargetFunction<TSource, TDestination>();
112+
return fn(source, destination);
113+
}
114+
115+
private static TDestination UpdateFuncFromPackedinObject<TSource, TDestination>(TSource source, TDestination destination, TypeAdapterConfig config, Type sourceType, Type destinationType)
116+
{
117+
dynamic del = config.GetMapToTargetFunction(sourceType, destinationType);
118+
119+
120+
if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible)
121+
{
122+
dynamic objfn = del;
123+
return objfn((dynamic)source, (dynamic)destination);
124+
}
125+
else
126+
{
127+
//NOTE: if type is non-public, we cannot use dynamic
128+
//DynamicInvoke is slow, but works with non-public
129+
return (TDestination)del.DynamicInvoke(source, destination);
130+
}
131+
}
132+
133+
/// <summary>
134+
/// Adapt the source object to the destination type.
135+
/// </summary>
136+
/// <param name="source">Source object to adapt.</param>
137+
/// <param name="sourceType">The type of the source object.</param>
138+
/// <param name="destinationType">The type of the destination object.</param>
139+
/// <returns>Adapted destination type.</returns>
140+
public static object? Adapt(this object source, Type sourceType, Type destinationType)
141+
{
142+
if (source != null &&
143+
sourceType.IsOpenGenericType() && destinationType.IsOpenGenericType())
144+
{
145+
var arg = source.GetType().GetGenericArguments();
146+
return Adapt(source, sourceType.MakeGenericType(arg), destinationType.MakeGenericType(arg), TypeAdapterConfig.GlobalSettings);
147+
}
148+
return Adapt(source, sourceType, destinationType, TypeAdapterConfig.GlobalSettings);
149+
}
150+
151+
/// <summary>
152+
/// Adapt the source object to the destination type.
153+
/// </summary>
154+
/// <param name="source">Source object to adapt.</param>
155+
/// <param name="sourceType">The type of the source object.</param>
156+
/// <param name="destinationType">The type of the destination object.</param>
157+
/// <param name="config">Configuration</param>
158+
/// <returns>Adapted destination type.</returns>
159+
public static object? Adapt(this object source, Type sourceType, Type destinationType, TypeAdapterConfig config)
160+
{
161+
var del = config.GetMapFunction(sourceType, destinationType);
162+
if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible)
163+
{
164+
dynamic fn = del;
165+
return fn((dynamic)source);
166+
}
167+
else
168+
{
169+
//NOTE: if type is non-public, we cannot use dynamic
170+
//DynamicInvoke is slow, but works with non-public
171+
return del.DynamicInvoke(source);
172+
}
173+
}
174+
175+
/// <summary>
176+
/// Adapt the source object to an existing destination object.
177+
/// </summary>
178+
/// <param name="source">Source object to adapt.</param>
179+
/// <param name="destination">Destination object to populate.</param>
180+
/// <param name="sourceType">The type of the source object.</param>
181+
/// <param name="destinationType">The type of the destination object.</param>
182+
/// <returns>Adapted destination type.</returns>
183+
public static object? Adapt(this object source, object destination, Type sourceType, Type destinationType)
184+
{
185+
return Adapt(source, destination, sourceType, destinationType, TypeAdapterConfig.GlobalSettings);
186+
}
187+
188+
/// <summary>
189+
/// Adapt the source object to an existing destination object.
190+
/// </summary>
191+
/// <param name="source">Source object to adapt.</param>
192+
/// <param name="destination">Destination object to populate.</param>
193+
/// <param name="sourceType">The type of the source object.</param>
194+
/// <param name="destinationType">The type of the destination object.</param>
195+
/// <param name="config">Configuration</param>
196+
/// <returns>Adapted destination type.</returns>
197+
public static object? Adapt(this object source, object destination, Type sourceType, Type destinationType, TypeAdapterConfig config)
198+
{
199+
var del = config.GetMapToTargetFunction(sourceType, destinationType);
200+
if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible)
201+
{
202+
dynamic fn = del;
203+
return fn((dynamic)source, (dynamic)destination);
204+
}
205+
else
206+
{
207+
//NOTE: if type is non-public, we cannot use dynamic
208+
//DynamicInvoke is slow, but works with non-public
209+
return del.DynamicInvoke(source, destination);
210+
}
211+
}
212+
213+
/// <summary>
214+
/// Validate properties and Adapt the source object to the destination type.
215+
/// </summary>
216+
/// <typeparam name="TSource">Source type.</typeparam>
217+
/// <typeparam name="TDestination">Destination type.</typeparam>
218+
/// <param name="source">Source object to adapt.</param>
219+
/// <returns>Adapted destination type.</returns>
220+
public static TDestination ValidateAndAdapt<TSource, TDestination>(this TSource source)
221+
{
222+
var sourceType = typeof(TSource);
223+
var selectorType = typeof(TDestination);
224+
225+
var sourceProperties = new HashSet<string>(sourceType.GetProperties().Select(p => p.Name));
226+
var selectorProperties = new HashSet<string>(selectorType.GetProperties().Select(p=> p.Name));
227+
228+
foreach (var selectorProperty in selectorProperties)
229+
{
230+
if (sourceProperties.Contains(selectorProperty)) continue;
231+
throw new Exception($"Property {selectorProperty} does not exist in {sourceType.Name} and is not configured in Mapster");
232+
}
233+
return source.Adapt<TDestination>();
234+
}
235+
236+
/// <summary>
237+
/// Validate properties with configuration and Adapt the source object to the destination type.
238+
/// </summary>
239+
/// <typeparam name="TSource">Source type.</typeparam>
240+
/// <typeparam name="TDestination">Destination type.</typeparam>
241+
/// <param name="source">Source object to adapt.</param>
242+
/// <param name="config">Configuration</param>
243+
/// <returns>Adapted destination type.</returns>
244+
public static TDestination ValidateAndAdapt<TSource, TDestination>(this TSource source, TypeAdapterConfig config)
245+
{
246+
var sourceType = typeof(TSource);
247+
var selectorType = typeof(TDestination);
248+
249+
var sourceProperties = new HashSet<string>(sourceType.GetProperties().Select(p => p.Name));
250+
var selectorProperties = new HashSet<string>(selectorType.GetProperties().Select(p=> p.Name));
251+
252+
// Get the rule map for the current types
253+
var ruleMap = config.RuleMap;
254+
var typeTuple = new TypeTuple(sourceType, selectorType);
255+
ruleMap.TryGetValue(typeTuple, out var rule);
256+
257+
foreach (var selectorProperty in selectorProperties)
258+
{
259+
if (sourceProperties.Contains(selectorProperty)) continue;
260+
// Check whether the adapter config has a config for the property
261+
if (rule != null && rule.Settings.Resolvers.Any(r => r.DestinationMemberName.Equals(selectorProperty))) continue;
262+
throw new Exception($"Property {selectorProperty} does not exist in {sourceType.Name} and is not configured in Mapster");
263+
}
264+
return source.Adapt<TDestination>(config);
265+
}
266+
267+
268+
/// <summary>
269+
/// Adapt the source object to a destination type using a temporary configuration.
270+
/// A new TypeAdapterConfig is created for this call, ensuring GlobalSettings remain unchanged.
271+
/// Safe for init-only properties and record types.
272+
/// </summary>
273+
/// <typeparam name="TDestination">Destination type.</typeparam>
274+
/// <param name="source">Source object to adapt.</param>
275+
/// <param name="configAction">Action to customize the temporary config.</param>
276+
/// <returns>Adapted destination object of type TDestination.</returns>
277+
public static TDestination Adapt<TDestination>(this object? source, Action<TypeAdapterConfig> configAction)
278+
{
279+
var config = TypeAdapterConfig.GlobalSettings.Clone();
280+
configAction(config);
281+
return source.Adapt<TDestination>(config);
282+
}
283+
284+
/// <summary>
285+
/// Adapt the source object from TSource to TDestination using a dedicated TypeAdapterSetter.
286+
/// A temporary TypeAdapterConfig is created and configured via the setter.
287+
/// Safe for init-only properties and record types, without modifying GlobalSettings.
288+
/// </summary>
289+
/// <typeparam name="TSource">Source type.</typeparam>
290+
/// <typeparam name="TDestination">Destination type.</typeparam>
291+
/// <param name="source">Source object to adapt.</param>
292+
/// <param name="configAction">Action to customize the TypeAdapterSetter.</param>
293+
/// <returns>Adapted destination object of type TDestination.</returns>
294+
public static TDestination Adapt<TSource, TDestination>(this object? source, Action<TypeAdapterSetter<TSource, TDestination>> configAction)
295+
{
296+
var config = TypeAdapterConfig.GlobalSettings.Clone();
297+
var setter = config.ForType<TSource, TDestination>();
298+
configAction(setter);
299+
setter.Settings.Resolvers.Reverse();
300+
return source.Adapt<TDestination>(config);
301+
}
302+
}
303+
304+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "<Pending>")]
305+
public static class TypeAdapter<TSource, TDestination>
306+
{
307+
public static Func<TSource, TDestination> Map = TypeAdapterConfig.GlobalSettings.GetMapFunction<TSource, TDestination>();
308+
}
309+
}

0 commit comments

Comments
 (0)