using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Swan.Lite.Reflection; using Swan.Mappers; using Swan.Reflection; namespace Swan { /// /// Extension methods. /// public static partial class Extensions { /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The type of the source. /// The source. /// The target. /// The ignore properties. /// /// Number of properties that was copied successful. /// public static int CopyPropertiesTo(this T source, object target, params string[]? ignoreProperties) where T : class => ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties); /// /// Iterates over the public, instance, readable properties of the source and /// tries to write a compatible value to a public, instance, writable property in the destination. /// /// The source. /// The destination. /// Properties to copy. /// /// Number of properties that were successfully copied. /// public static int CopyOnlyPropertiesTo(this object source, object target, params string[]? propertiesToCopy) => ObjectMapper.Copy(source, target, propertiesToCopy); /// /// Copies the properties to new instance of T. /// /// The new object type. /// The source. /// The ignore properties. /// /// The specified type with properties copied. /// /// source. public static T CopyPropertiesToNew(this object source, string[]? ignoreProperties = null) where T : class { if (source == null) throw new ArgumentNullException(nameof(source)); var target = Activator.CreateInstance(); ObjectMapper.Copy(source, target, GetCopyableProperties(target), ignoreProperties); return target; } /// /// Copies the only properties to new instance of T. /// /// Object Type. /// The source. /// The properties to copy. /// /// The specified type with properties copied. /// /// source. public static T CopyOnlyPropertiesToNew(this object source, params string[] propertiesToCopy) where T : class { if (source == null) throw new ArgumentNullException(nameof(source)); var target = Activator.CreateInstance(); ObjectMapper.Copy(source, target, propertiesToCopy); return target; } /// /// Iterates over the keys of the source and tries to write a compatible value to a public, /// instance, writable property in the destination. /// /// The source. /// The target. /// The ignore keys. /// Number of properties that was copied successful. public static int CopyKeyValuePairTo( this IDictionary source, object target, params string[] ignoreKeys) => source == null ? throw new ArgumentNullException(nameof(source)) : ObjectMapper.Copy(source, target, null, ignoreKeys); /// /// Iterates over the keys of the source and tries to write a compatible value to a public, /// instance, writable property in the destination. /// /// Object Type. /// The source. /// The ignore keys. /// /// The specified type with properties copied. /// public static T CopyKeyValuePairToNew( this IDictionary source, params string[] ignoreKeys) { if (source == null) throw new ArgumentNullException(nameof(source)); var target = Activator.CreateInstance(); source.CopyKeyValuePairTo(target, ignoreKeys); return target; } /// /// Does the specified action. /// /// The action. /// The retry interval. /// The retry count. public static void Retry( this Action action, TimeSpan retryInterval = default, int retryCount = 3) { if (action == null) throw new ArgumentNullException(nameof(action)); Retry(() => { action(); return null; }, retryInterval, retryCount); } /// /// Does the specified action. /// /// The type of the source. /// The action. /// The retry interval. /// The retry count. /// /// The return value of the method that this delegate encapsulates. /// /// action. /// Represents one or many errors that occur during application execution. public static T Retry( this Func action, TimeSpan retryInterval = default, int retryCount = 3) { if (action == null) throw new ArgumentNullException(nameof(action)); if (retryInterval == default) retryInterval = TimeSpan.FromSeconds(1); var exceptions = new List(); for (var retry = 0; retry < retryCount; retry++) { try { if (retry > 0) Task.Delay(retryInterval).Wait(); return action(); } catch (Exception ex) { exceptions.Add(ex); } } throw new AggregateException(exceptions); } /// /// Gets the copyable properties. /// /// If there is no properties with the attribute AttributeCache returns all the properties. /// /// The object. /// /// Array of properties. /// /// model. /// public static IEnumerable GetCopyableProperties(this object @this) { if (@this == null) throw new ArgumentNullException(nameof(@this)); var collection = PropertyTypeCache.DefaultCache.Value .RetrieveAllProperties(@this.GetType(), true); var properties = collection .Select(x => new { x.Name, HasAttribute = AttributeCache.DefaultCache.Value.RetrieveOne(x) != null, }) .Where(x => x.HasAttribute) .Select(x => x.Name); return properties.Any() ? properties : collection.Select(x => x.Name); } internal static void CreateTarget( this object source, Type targetType, bool includeNonPublic, ref object? target) { switch (source) { // do nothing. Simply skip creation case string _: break; // When using arrays, there is no default constructor, attempt to build a compatible array case IList sourceObjectList when targetType.IsArray: var elementType = targetType.GetElementType(); if (elementType != null) target = Array.CreateInstance(elementType, sourceObjectList.Count); break; default: var constructors = ConstructorTypeCache.DefaultCache.Value .RetrieveAllConstructors(targetType, includeNonPublic); // Try to check if empty constructor is available if (constructors.Any(x => x.Item2.Length == 0)) { target = Activator.CreateInstance(targetType, includeNonPublic); } else { var firstCtor = constructors .OrderBy(x => x.Item2.Length) .FirstOrDefault(); target = Activator.CreateInstance(targetType, firstCtor?.Item2.Select(arg => arg.GetType().GetDefault()).ToArray()); } break; } } internal static string GetNameWithCase(this string name, JsonSerializerCase jsonSerializerCase) => jsonSerializerCase switch { JsonSerializerCase.PascalCase => char.ToUpperInvariant(name[0]) + name.Substring(1), JsonSerializerCase.CamelCase => char.ToLowerInvariant(name[0]) + name.Substring(1), JsonSerializerCase.None => name, _ => throw new ArgumentOutOfRangeException(nameof(jsonSerializerCase), jsonSerializerCase, null) }; } }