/// <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."))); } }
/// <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."))); } }
/// <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."))); } }
/// <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."))); } }