Skip to content

Binding converters

Adam Bajguz edited this page Dec 19, 2021 · 1 revision

Typin supports custom binding converters. Simply initialize Converter property in CommandOptionAttribute or CommandParameterAttribute with a converter type. A converter type is a class that inherits BindingConverter<TProperty>

Examples

Initializable class

    [Command("cmd")]
    public class SupportedArgumentTypesViaConverterCommand : SelfSerializeCommandBase
    {
        [CommandOption("str-class", Converter = typeof(InitializableClassTypeByConverter_Converter))]
        public InitializableClassTypeByConverter? StringInitializable { get; init; }
    }

    public class InitializableClassTypeByConverter
    {
        public int Value { get; init; }
        public DayOfWeek Day { get; init; }
    }

    public class InitializableClassTypeByConverter_Converter : BindingConverter<InitializableClassTypeByConverter>
    {
        public override InitializableClassTypeByConverter? Convert(string? value)
        {
            if (value is null)
            {
                return null;
            }

            string[] values = value.Split(':');

            if (values.Length != 2)
            {
                throw new FormatException($"Invalid format of {nameof(InitializableClassTypeByConverter)}.");
            }

            DayOfWeek day = Enum.Parse<DayOfWeek>(values[0]);
            int v = int.Parse(values[1]);

            return new InitializableClassTypeByConverter { Value = v, Day = day };
        }

        public override InitializableClassTypeByConverter ConvertCollection(IReadOnlyCollection<string> values)
        {
            throw new NotImplementedException();
        }
    }

Initializable struct

TIP: Nullable struct can be initialized by non-nullable converter.

    [Command("cmd")]
    public class SupportedArgumentTypesViaConverterCommand : SelfSerializeCommandBase
    {
        [CommandOption("str-nullable-struct-by-non-nullable-converter", Converter = typeof(InitializableStructTypeByConverter_Converter))]
        public InitializableStructTypeByConverter? StringNullableInitializableStructByNonNullableConverter { get; init; }

        [CommandOption("str-struct", Converter = typeof(InitializableStructTypeByConverter_Converter))]
        public InitializableStructTypeByConverter StringInitializableStruct { get; init; }
    }

    public struct InitializableStructTypeByConverter
    {
        public int Value { get; init; }
        public DayOfWeek Day { get; init; }
    }

    public class InitializableStructTypeByConverter_Converter : BindingConverter<InitializableStructTypeByConverter>
    {
        public override InitializableStructTypeByConverter Convert(string? value)
        {
            if (value is null)
            {
                return default;
            }

            string[] values = value.Split(':');

            if (values.Length != 2)
            {
                throw new FormatException($"Invalid format of {nameof(InitializableClassTypeByConverter)}.");
            }

            DayOfWeek day = Enum.Parse<DayOfWeek>(values[0]);
            int v = int.Parse(values[1]);

            return new InitializableStructTypeByConverter { Value = v, Day = day };
        }

        public override InitializableStructTypeByConverter ConvertCollection(IReadOnlyCollection<string> values)
        {
            throw new NotImplementedException();
        }
    }

Initializable enumerable

    [Command("cmd")]
    public class SupportedArgumentTypesViaConverterCommand : SelfSerializeCommandBase
    {
        [CommandOption("str-enumerable", Converter = typeof(InitializableEnumerableByConverter_Converter<string>))]
        public InitializableEnumerableByConverter<string>? StringEnumerableInitializable { get; init; }

        [CommandOption("str-indirect-enumerable", Converter = typeof(InitializableEnumerableByConverter_Converter<string>))]
        public InitializableEnumerableByConverter? IndirectlyStringEnumerableInitializable { get; init; }
    }

    public abstract class InitializableEnumerableByConverter : IEnumerable
    {
        public abstract IEnumerator GetEnumerator();
    }

    public class InitializableEnumerableByConverter<T> : InitializableEnumerableByConverter, IEnumerable<T>
    {
        private readonly IEnumerable<T> _arr;

        public InitializableEnumerableByConverter(IEnumerable<T> value)
        {
            _arr = value;
        }

        public override IEnumerator<T> GetEnumerator()
        {
            return _arr.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class InitializableEnumerableByConverter_Converter<T> : BindingConverter<InitializableEnumerableByConverter<T>>
    {
        // This method is used by scalar binder or non-scalar binder (when a property is a collection of <typeparamref name="T"/> - conversion for collection item type).
        public override InitializableEnumerableByConverter<T>? Convert(string? value)
        {
            if (value is null)
            {
                return null;
            }

            T[] values = value.Split(':').Cast<T>().ToArray();

            return new InitializableEnumerableByConverter<T>(values);
        }

        // This method is used by non-scalar binder when converting collection type.
        public override InitializableEnumerableByConverter<T> ConvertCollection(IReadOnlyCollection<string> values)
        {
            List<T> v = new();

            foreach (string value in values)
            {
                v.AddRange(value.Split(':').Cast<T>());
            }

            return new InitializableEnumerableByConverter<T>(v);
        }
    }
Clone this wiki locally