src/Microsoft.Azure.WebJobs.Host/Config/FluentConverterRules.cs (86 lines of code) (raw):

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Host.Bindings; using System.Threading; namespace Microsoft.Azure.WebJobs.Host.Config { /// <summary> /// Expose fluent APIs for adding converters. This can apply to either: /// 1. globally for all attributes - in which case it's called from <see cref="ExtensionConfigContext"/> and TAttribute is System.Attribute. /// 2. a specific attribute - in which case it's called from <see cref="FluentBindingRule{TAttribute}"/> /// </summary> /// <typeparam name="TAttribute">The attribute type this applies to. Literally <see cref="Attribute"/> if it applies to all attributes.</typeparam> /// <typeparam name="TThis">For fluent API, the type to return</typeparam> [Obsolete("Not ready for public consumption.")] public abstract class FluentConverterRules<TAttribute, TThis> where TAttribute : Attribute { // Access the converter manager that we're adding rules to. internal abstract ConverterManager ConverterManager { get; } /// <summary> /// Add basic converter /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TDestination"></typeparam> /// <param name="func"></param> /// <returns></returns> public TThis AddConverter<TSource, TDestination>(Func<TSource, TDestination> func) { VerifyNotOpenTypes<TSource, TDestination>(); // The converter is implicitly for this TAttribute even though it doesn't need an attribute parameter. var pm = PatternMatcher.New(func); this.AddConverterBuilder<TSource, TDestination>(pm); return (TThis)(object)this; } /// <summary> /// Add converter that uses the control attribute. /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TDestination"></typeparam> /// <param name="func"></param> /// <returns></returns> public TThis AddConverter<TSource, TDestination>(Func<TSource, TAttribute, TDestination> func) { VerifyNotOpenTypes<TSource, TDestination>(); var pm = PatternMatcher.New(func); this.AddConverterBuilder<TSource, TDestination>(pm); return (TThis)(object)this; } /// <summary> /// Add converter that uses the control attribute. /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TDestination"></typeparam> /// <param name="func"></param> /// <returns></returns> public TThis AddConverter<TSource, TDestination>(Func<TSource, CancellationToken, Task<TDestination>> func) { VerifyNotOpenTypes<TSource, TDestination>(); var pm = PatternMatcher.New(func); this.AddConverterBuilder<TSource, TDestination>(pm); return (TThis)(object)this; } /// <summary> /// Add converter that uses the control attribute. /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TDestination"></typeparam> /// <param name="func"></param> /// <returns></returns> public TThis AddConverter<TSource, TDestination>(FuncAsyncConverter<TSource, TDestination> func) { VerifyNotOpenTypes<TSource, TDestination>(); var pm = PatternMatcher.New(func); this.AddConverterBuilder<TSource, TDestination>(pm); return (TThis)(object)this; } /// <summary> /// Add a converter that that uses open types. /// Src type must handle object since it could be unknown. /// But it's converting to a well-known destination type. /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TDestination"></typeparam> /// <param name="func"></param> /// <returns></returns> public TThis AddOpenConverter<TSource, TDestination>(FuncAsyncConverter func) { var pm = PatternMatcher.New(func); this.AddConverterBuilder<TSource, TDestination>(pm); return (TThis)(object)this; } /// <summary> /// Add a converter for the given Source to Destination conversion. /// The typeConverter type is instantiated with the type arguments and constructorArgs is passed. /// </summary> /// <typeparam name="TSource">Source type.</typeparam> /// <typeparam name="TDestination">Destination type.</typeparam> /// <param name="typeConverter">A type with conversion methods. This can be generic and will get instantiated with the /// appropriate type parameters. </param> /// <param name="constructorArgs">Constructor Arguments to pass to the constructor when instantiated. This can pass configuration and state.</param> public TThis AddOpenConverter<TSource, TDestination>( Type typeConverter, params object[] constructorArgs) { var patternMatcher = PatternMatcher.New(typeConverter, constructorArgs); return AddConverterBuilder<TSource, TDestination>(patternMatcher); } /// <summary> /// Add a converter for the given Source to Destination conversion. /// </summary> /// <typeparam name="TSource">Source type.</typeparam> /// <typeparam name="TDestination">Destination type.</typeparam> /// <param name="converterInstance">Instance of an object with convert methods on it.</param> public TThis AddConverter<TSource, TDestination>( IConverter<TSource, TDestination> converterInstance) { VerifyNotOpenTypes<TSource, TDestination>(); var patternMatcher = PatternMatcher.New(converterInstance); return AddConverterBuilder<TSource, TDestination>(patternMatcher); } /// <summary> /// Add a converter for the given Source to Destination conversion. /// </summary> /// <typeparam name="TSource">Source type.</typeparam> /// <typeparam name="TDestination">Destination type.</typeparam> /// <param name="converterInstance">Instance of an object with convert methods on it.</param> public TThis AddConverter<TSource, TDestination>( IAsyncConverter<TSource, TDestination> converterInstance) { VerifyNotOpenTypes<TSource, TDestination>(); var patternMatcher = PatternMatcher.New(converterInstance); return AddConverterBuilder<TSource, TDestination>(patternMatcher); } private TThis AddConverterBuilder<TSource, TDestination>( PatternMatcher patternMatcher) { var builder = patternMatcher.GetBuilder(); this.ConverterManager.AddConverter<TSource, TDestination, TAttribute>(builder); return (TThis)(object)this; } // Helper for enforcing that a delegate can only handle concrete types, not open types. static void VerifyNotOpenTypes<T1, T2>() { if (OpenType.IsOpenType<T1>() || OpenType.IsOpenType<T2>()) { throw new InvalidOperationException($"Use AddOpenConverter to add a converter for open types."); } if (typeof(T1) == typeof(T2)) { throw new InvalidOperationException($"Converter source and desitnation types must be different (${typeof(T1).Name})."); } } } }