Example #1
0
        /// <summary>
        /// Builds an <see cref="IntSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with an <see cref="IntSchema" />
        /// if <paramref name="type" /> is less than or equal to 32 bits; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (
                type == typeof(sbyte) || type == typeof(byte) ||
                type == typeof(short) || type == typeof(ushort) || type == typeof(char) ||
                type == typeof(int) || type == typeof(uint))
            {
                var intSchema = new IntSchema();

                try
                {
                    context.Schemas.Add(type, intSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(intSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(IntSchemaBuilderCase)} can only be applied to integral types less than or equal than 32 bits.")));
            }
        }
Example #2
0
        /// <summary>
        /// Builds a <see cref="UnionSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="UnionSchema" />
        /// if <paramref name="type" /> is <see cref="T:System.Nullable`1" />; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (Nullable.GetUnderlyingType(type) is Type underlyingType)
            {
                // defer setting the item schema so the union schema can be cached:
                var unionSchema = new UnionSchema();

                try
                {
                    context.Schemas.Add(type, unionSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                unionSchema.Schemas.Add(new NullSchema());
                unionSchema.Schemas.Add(SchemaBuilder.BuildSchema(underlyingType, context));

                return(SchemaBuilderCaseResult.FromSchema(unionSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(UnionSchemaBuilderCase)} can only be applied to nullable value types.")));
            }
        }
Example #3
0
        /// <summary>
        /// Builds a <see cref="StringSchema" /> with a <see cref="UuidLogicalType" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="StringSchema" />
        /// and associated <see cref="UuidLogicalType" /> if <paramref name="type" /> is
        /// <see cref="Guid" />; an unsuccessful <see cref="SchemaBuilderCaseResult" />
        /// with an <see cref="UnsupportedTypeException" /> otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(Guid))
            {
                var uuidSchema = new StringSchema()
                {
                    LogicalType = new UuidLogicalType(),
                };

                try
                {
                    context.Schemas.Add(type, uuidSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(uuidSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(UuidSchemaBuilderCase)} can only be applied to the {nameof(Guid)} type.")));
            }
        }
        /// <summary>
        /// Builds a <see cref="BytesSchema" /> with a <see cref="DecimalLogicalType" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="BytesSchema" />
        /// and associated <see cref="DecimalLogicalType" /> if <paramref name="type" /> is
        /// <see cref="decimal" />; an unsuccessful <see cref="SchemaBuilderCaseResult" /> with an
        /// <see cref="UnsupportedTypeException" /> otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(decimal))
            {
                var decimalSchema = new BytesSchema
                {
                    // default precision/scale to .NET limits:
                    LogicalType = new DecimalLogicalType(29, 14),
                };

                try
                {
                    context.Schemas.Add(type, decimalSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(decimalSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(DecimalSchemaBuilderCase)} can only be applied to the {typeof(decimal)} type.")));
            }
        }
        /// <summary>
        /// Builds a <see cref="BytesSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="BytesSchema" />
        /// if <paramref name="type" /> is <see cref="T:System.Byte[]" />; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(byte[]))
            {
                var bytesSchema = new BytesSchema();

                Schema schema = bytesSchema;

                if (!type.IsValueType && NullableReferenceTypeBehavior == NullableReferenceTypeBehavior.All)
                {
                    if (!context.Schemas.TryGetValue(NullableType, out var nullSchema))
                    {
                        context.Schemas.Add(NullableType, nullSchema = new NullSchema());
                    }

                    schema = new UnionSchema(new[] { nullSchema, schema });
                }

                try
                {
                    context.Schemas.Add(type, schema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(schema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(BytesSchemaBuilderCase)} can only be applied to the {typeof(byte[])} type.")));
            }
        }
Example #6
0
        /// <summary>
        /// Builds a <see cref="LongSchema" /> with a <see cref="TimestampLogicalType" /> or a
        /// <see cref="StringSchema" /> based on the value of <see cref="TemporalBehavior" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="LongSchema" />
        /// and <see cref="TimestampLogicalType" /> or <see cref="StringSchema" /> if
        /// <paramref name="type" /> is <see cref="DateTime" /> or <see cref="DateTimeOffset" />;
        /// an unsuccessful <see cref="SchemaBuilderCaseResult" /> with an
        /// <see cref="UnsupportedTypeException" /> otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(DateTime) || type == typeof(DateTimeOffset))
            {
                Schema timestampSchema = TemporalBehavior switch
                {
                    TemporalBehavior.EpochMicroseconds => new LongSchema()
                    {
                        LogicalType = new MicrosecondTimestampLogicalType(),
                    },
                    TemporalBehavior.EpochMilliseconds => new LongSchema()
                    {
                        LogicalType = new MillisecondTimestampLogicalType(),
                    },
                    TemporalBehavior.Iso8601 => new StringSchema(),
                    _ => throw new ArgumentOutOfRangeException(nameof(TemporalBehavior)),
                };

                try
                {
                    context.Schemas.Add(type, timestampSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(timestampSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(TimestampSchemaBuilderCase)} can only be applied to the {nameof(DateTime)} and {nameof(DateTimeOffset)} types.")));
            }
        }
        /// <summary>
        /// Builds a <see cref="MapSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="MapSchema" /> if
        /// <paramref name="type" /> implements <see cref="IEnumerable{T}" /> and the item type is
        /// <see cref="KeyValuePair{TKey, TValue}" />; an unsuccessful <see cref="SchemaBuilderCaseResult" />
        /// with an <see cref="UnsupportedTypeException" /> otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type.GetDictionaryTypes()?.Value is Type valueType)
            {
                // defer setting the value schema so the map schema can be cached:
                var mapSchema = ReflectionExtensions.GetUninitializedInstance <MapSchema>();

                Schema schema = mapSchema;

                if (!type.IsValueType && NullableReferenceTypeBehavior == NullableReferenceTypeBehavior.All)
                {
                    schema = new UnionSchema(new[] { new NullSchema(), schema });
                }

                try
                {
                    context.Schemas.Add(type, schema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                mapSchema.Value = SchemaBuilder.BuildSchema(valueType, context);

                return(SchemaBuilderCaseResult.FromSchema(schema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(MapSchemaBuilderCase)} can only be applied to dictionary types.")));
            }
        }
        /// <summary>
        /// Builds an <see cref="EnumSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with an <see cref="EnumSchema" />
        /// if <paramref name="type" /> is an <see cref="Enum" />; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type.IsEnum)
            {
                Schema schema;

                if (type.GetAttribute <FlagsAttribute>() is not null || EnumBehavior == EnumBehavior.Integral)
                {
                    schema = SchemaBuilder.BuildSchema(type.GetEnumUnderlyingType(), context);
                }
                else if (EnumBehavior == EnumBehavior.Nominal)
                {
                    schema = new StringSchema();
                }
                else
                {
                    var enumSchema = new EnumSchema(GetSchemaName(type))
                    {
                        Namespace = GetSchemaNamespace(type),
                    };

                    foreach (var member in type.GetEnumMembers()
                             .OrderBy(field => Enum.Parse(type, field.Name))
                             .ThenBy(field => field.Name))
                    {
                        enumSchema.Symbols.Add(GetSymbol(member));
                    }

                    schema = enumSchema;
                }

                try
                {
                    context.Schemas.Add(type, schema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(schema));
            }
        /// <summary>
        /// Builds a <see cref="BooleanSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="BooleanSchema" />
        /// if <paramref name="type" /> is <see cref="bool" />; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(bool))
            {
                var booleanSchema = new BooleanSchema();

                try
                {
                    context.Schemas.Add(type, booleanSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(booleanSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(BooleanSchemaBuilderCase)} can only be applied to the {typeof(bool)} type.")));
            }
        }
        /// <summary>
        /// Builds a <see cref="LongSchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with a <see cref="LongSchema" />
        /// if <paramref name="type" /> is greater than 32 bits; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (type == typeof(long) || type == typeof(ulong))
            {
                var longSchema = new LongSchema();

                try
                {
                    context.Schemas.Add(type, longSchema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                return(SchemaBuilderCaseResult.FromSchema(longSchema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(LongSchemaBuilderCase)} can only be applied to integral types greater than 32 bits.")));
            }
        }
        /// <summary>
        /// Builds an <see cref="ArraySchema" />.
        /// </summary>
        /// <returns>
        /// A successful <see cref="SchemaBuilderCaseResult" /> with an <see cref="ArraySchema" />
        /// if <paramref name="type" /> implements <see cref="IEnumerable{T}" />; an unsuccessful
        /// <see cref="SchemaBuilderCaseResult" /> with an <see cref="UnsupportedTypeException" />
        /// otherwise.
        /// </returns>
        /// <inheritdoc />
        public virtual SchemaBuilderCaseResult BuildSchema(Type type, SchemaBuilderContext context)
        {
            if (GetEnumerableType(type) is Type itemType)
            {
                // defer setting the item schema so the array schema can be cached:
                var arraySchema = ReflectionExtensions.GetUninitializedInstance <ArraySchema>();

                Schema schema = arraySchema;

                if (!type.IsValueType && NullableReferenceTypeBehavior == NullableReferenceTypeBehavior.All)
                {
                    if (!context.Schemas.TryGetValue(NullableType, out var nullSchema))
                    {
                        context.Schemas.Add(NullableType, nullSchema = new NullSchema());
                    }

                    schema = new UnionSchema(new[] { nullSchema, schema });
                }

                try
                {
                    context.Schemas.Add(type, schema);
                }
                catch (ArgumentException exception)
                {
                    throw new InvalidOperationException($"A schema for {type} already exists on the schema builder context.", exception);
                }

                arraySchema.Item = SchemaBuilder.BuildSchema(itemType, context);

                return(SchemaBuilderCaseResult.FromSchema(schema));
            }
            else
            {
                return(SchemaBuilderCaseResult.FromException(new UnsupportedTypeException(type, $"{nameof(ArraySchemaBuilderCase)} can only be applied to enumerable types.")));
            }
        }