<commit_before>﻿using System;
using System.Linq.Expressions;

using Kirkin.Utilities;

namespace Kirkin.Mapping.Engine.MemberMappings
{
    /// <summary>
    /// Direct mapping between source and target members with support for conversions.
    /// </summary>
    internal sealed class DefaultMemberMapping<TSource, TTarget> : MemberMapping<TSource, TTarget>
        , IEquatable<DefaultMemberMapping<TSource, TTarget>>
    {
        /// <summary>
        /// Mapping source member.
        /// </summary>
        public Member SourceMember { get; }

        /// <summary>
        /// Nullable to non-nullable conversion behaviour of this instance.
        /// </summary>
        public NullableBehaviour NullableBehaviour { get; }

        /// <summary>
        /// Creates a new simple member-to-member mapping instance.
        /// </summary>
        public DefaultMemberMapping(Member sourceMember, Member targetMember, NullableBehaviour nullableBehaviour)
            : base(targetMember)
        {
            SourceMember = sourceMember;
            NullableBehaviour = nullableBehaviour;
        }

        /// <summary>
        /// Produces an expression which resolves the source value.
        /// </summary>
        internal override Expression GetSourceValueExpression(ParameterExpression sourceParam)
        {
            // Resolve and convert value automatically.
            Expression source = SourceMember.ResolveGetter(sourceParam);

            if (SourceMember.Type != TargetMember.Type) {
                source = ComplexConversion(source, SourceMember.Type, TargetMember.Type);
            }

            return source;
        }

        /// <summary>
        /// Produces the expression which retrieves the source value from the appropriate member.
        /// </summary>
        private Expression ComplexConversion(Expression value, Type sourceType, Type targetType)
        {
            // Nullable handling.
            Type nullableSourceType = Nullable.GetUnderlyingType(sourceType);
            Type nullableTargetType = Nullable.GetUnderlyingType(targetType);

            // String -> Enum mapping (taken from ExpressMapper and improved).
            // Must come before "normal" nullable/non-nullable conversions.
            if ((nullableTargetType ?? targetType).IsEnum && sourceType == typeof(string)) {
                return StringToEnumConversion(value, targetType, nullableTargetType);
            }

            // Nullable -> non-nullable or non-nullable to nullable.
            if (nullableSourceType != null ^ nullableTargetType != null) {
                return NullableConversion(value, sourceType, nullableSourceType, targetType, nullableTargetType, NullableBehaviour);
            }

            // Non-string -> string (simply calls value.ToString()).
            if (targetType == typeof(string)) {
                return ToStringCall(value);
            }

            // Attempt cast (will likely work for most IConvertible
            // types, so no special handling for them).
            return Expression.Convert(value, targetType);
        }

        #region Conversions

        static class ExpressionConstants
        {
            public static readonly ConstantExpression NullConstant = Expression.Constant(null);
        }

        private static Expression NullableConversion(
            Expression value,
            Type sourceType,
            Type nullableSourceType,
            Type targetType,
            Type nullableTargetType,
            NullableBehaviour behaviour)
        {
            if (behaviour == NullableBehaviour.Error) {
                throw new MappingException("Nullable to non-nullable and reverse conversions not allowed.");
            }

            if (nullableSourceType != null)
            {
                if (nullableTargetType == null) {
                    return Expression.Coalesce(value, Expression.Default(nullableSourceType));
                }
            }
            else if (nullableTargetType != null)
            {
                if (behaviour == NullableBehaviour.Error) {
                    throw new MappingException("Non-nullable to nullable conversions not allowed.");<commit_msg>DefaultMemberMapping: apply type conversion to coalesced source if necessary<commit_after>﻿using System;
using System.Linq.Expressions;

using Kirkin.Utilities;

namespace Kirkin.Mapping.Engine.MemberMappings
{
    /// <summary>
    /// Direct mapping between source and target members with support for conversions.
    /// </summary>
    internal sealed class DefaultMemberMapping<TSource, TTarget> : MemberMapping<TSource, TTarget>
        , IEquatable<DefaultMemberMapping<TSource, TTarget>>
    {
        /// <summary>
        /// Mapping source member.
        /// </summary>
        public Member SourceMember { get; }

        /// <summary>
        /// Nullable to non-nullable conversion behaviour of this instance.
        /// </summary>
        public NullableBehaviour NullableBehaviour { get; }

        /// <summary>
        /// Creates a new simple member-to-member mapping instance.
        /// </summary>
        public DefaultMemberMapping(Member sourceMember, Member targetMember, NullableBehaviour nullableBehaviour)
            : base(targetMember)
        {
            SourceMember = sourceMember;
            NullableBehaviour = nullableBehaviour;
        }

        /// <summary>
        /// Produces an expression which resolves the source value.
        /// </summary>
        internal override Expression GetSourceValueExpression(ParameterExpression sourceParam)
        {
            // Resolve and convert value automatically.
            Expression source = SourceMember.ResolveGetter(sourceParam);

            if (SourceMember.Type != TargetMember.Type) {
                source = ComplexConversion(source, SourceMember.Type, TargetMember.Type);
            }

            return source;
        }

        /// <summary>
        /// Produces the expression which retrieves the source value from the appropriate member.
        /// </summary>
        private Expression ComplexConversion(Expression value, Type sourceType, Type targetType)
        {
            // Nullable handling.
            Type nullableSourceType = Nullable.GetUnderlyingType(sourceType);
            Type nullableTargetType = Nullable.GetUnderlyingType(targetType);

            // String -> Enum mapping (taken from ExpressMapper and improved).
            // Must come before "normal" nullable/non-nullable conversions.
            if ((nullableTargetType ?? targetType).IsEnum && sourceType == typeof(string)) {
                return StringToEnumConversion(value, targetType, nullableTargetType);
            }

            // Nullable -> non-nullable or non-nullable to nullable.
            if (nullableSourceType != null ^ nullableTargetType != null) {
                return NullableConversion(value, sourceType, nullableSourceType, targetType, nullableTargetType, NullableBehaviour);
            }

            // Non-string -> string (simply calls value.ToString()).
            if (targetType == typeof(string)) {
                return ToStringCall(value);
            }

            // Attempt cast (will likely work for most IConvertible
            // types, so no special handling for them).
            return Expression.Convert(value, targetType);
        }

        #region Conversions

        static class ExpressionConstants
        {
            public static readonly ConstantExpression NullConstant = Expression.Constant(null);
        }

        private static Expression NullableConversion(
            Expression value,
            Type sourceType,
            Type nullableSourceType,
            Type targetType,
            Type nullableTargetType,
            NullableBehaviour behaviour)
        {
            if (behaviour == NullableBehaviour.Error) {
                throw new MappingException("Nullable to non-nullable and reverse conversions not allowed.");
            }

            if (nullableSourceType != null)
            {
                if (nullableTargetType == null)
                {
                    Expression coalesce = Expression.Coalesce(value, Expression.Default(nullableSourceType));

                    return (nullableSourceType == targetType)
                        ? coalesce
                        : Expression.Convert(coalesce, targetType);
                }
            }
            else if (nullableTargetType != null)
            {
                if (behaviour == NullableBehaviour.Error) {
                    throw new MappingException("Non-nullable to nullable conversions not allowed.");
{'fraction_non_alphanumeric': 0.08189357290946786, 'fraction_numerical': 0.0, 'mean_word_length': 1.9879559532002753, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2403813', 'n_tokens_mistral': 2002, 'n_tokens_neox': 1938, 'n_words': 552}