/// <summary>
        /// Rule to provide an IValueBinder from a resolved attribute.
        /// IValueBinder will let you have an OnCompleted hook that is invoked after the user function completes.
        /// </summary>
        /// <typeparam name="TType">An Open Type. This rule is only applied if the parameter type matches the open type</typeparam>
        /// <param name="builder">Builder function to create a IValueBinder given a resolved attribute and the user parameter type. </param>
        /// <returns>A binding provider that applies these semantics.</returns>
        public FluentBinder BindToValueProvider <TType>(Func <TAttribute, Type, Task <IValueBinder> > builder)
        {
            var ot           = OpenType.FromType <TType>();
            var nameResolver = _nameResolver;
            var binder       = new ItemBindingProvider <TAttribute>(_configuration, nameResolver, builder, ot);

            return(Bind(binder));
        }
        /// <summary>
        /// Add a builder function that returns a converter. This can use <see cref="Microsoft.Azure.WebJobs.Host.Bindings.OpenType"/>  to match against an
        /// open set of types. The builder can then do one time static type checking and code gen caching before
        /// returning a converter function that is called on each invocation.
        /// The types passed to that converter are gauranteed to have matched against the TSource and TDestination parameters.
        /// </summary>
        /// <typeparam name="TSource">Source type. Can be a concrete type or a <see cref="OpenType"/></typeparam>
        /// <typeparam name="TDestination">Destination type. Can be a concrete type or a <see cref="OpenType"/></typeparam>
        /// <typeparam name="TAttribute">Attribute on the binding.
        /// If this is <see cref="System.Attribute"/>, then it applies to any attribute.
        /// Else it applies to the specific TAttribte.</typeparam>
        /// <param name="converterBuilder">A function that is invoked if-and-only-if there is a compatible type match for the
        /// source and destination types. It then produce a converter function that can be called many times </param>
        public void AddConverter <TSource, TDestination, TAttribute>(
            FuncConverterBuilder converterBuilder)
            where TAttribute : Attribute
        {
            var openTypeSource = OpenType.FromType <TSource>();
            var openTypeDest   = OpenType.FromType <TDestination>();

            AddOpenConverter <TAttribute>(openTypeSource, openTypeDest, converterBuilder);
        }
        public void OpenTypeContext()
        {
            var ctx = new OpenTypeMatchContext();

            var ot      = OpenType.FromType(typeof(OpenType));
            var otArray = OpenType.FromType(typeof(OpenType[]));

            Assert.True(ot.IsMatch(typeof(string), ctx));
            Assert.False(ot.IsMatch(typeof(string[]), ctx));
            Assert.True(otArray.IsMatch(typeof(string[]), ctx));
            Assert.True(ot.IsMatch(typeof(DateTime)));       // We could match DateTime normally
            Assert.False(ot.IsMatch(typeof(DateTime), ctx)); // false because we already commited to 'string' within this context
        }