in avro/src/schema.rs [1486:1701]
fn parse_complex(
&mut self,
complex: &Map<String, Value>,
enclosing_namespace: &Namespace,
parse_location: RecordSchemaParseLocation,
) -> AvroResult<Schema> {
// Try to parse this as a native complex type.
fn parse_as_native_complex(
complex: &Map<String, Value>,
parser: &mut Parser,
enclosing_namespace: &Namespace,
) -> AvroResult<Schema> {
match complex.get("type") {
Some(value) => match value {
Value::String(s) if s == "fixed" => {
parser.parse_fixed(complex, enclosing_namespace)
}
_ => parser.parse(value, enclosing_namespace),
},
None => Err(Error::GetLogicalTypeField),
}
}
// This crate support some logical types natively, and this function tries to convert
// a native complex type with a logical type attribute to these logical types.
// This function:
// 1. Checks whether the native complex type is in the supported kinds.
// 2. If it is, using the convert function to convert the native complex type to
// a logical type.
fn try_convert_to_logical_type<F>(
logical_type: &str,
schema: Schema,
supported_schema_kinds: &[SchemaKind],
convert: F,
) -> AvroResult<Schema>
where
F: Fn(Schema) -> AvroResult<Schema>,
{
let kind = SchemaKind::from(schema.clone());
if supported_schema_kinds.contains(&kind) {
convert(schema)
} else {
warn!(
"Ignoring unknown logical type '{}' for schema of type: {:?}!",
logical_type, schema
);
Ok(schema)
}
}
match complex.get("logicalType") {
Some(Value::String(t)) => match t.as_str() {
"decimal" => {
return try_convert_to_logical_type(
"decimal",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Fixed, SchemaKind::Bytes],
|inner| -> AvroResult<Schema> {
match Self::parse_precision_and_scale(complex) {
Ok((precision, scale)) => Ok(Schema::Decimal(DecimalSchema {
precision,
scale,
inner: Box::new(inner),
})),
Err(err) => {
warn!("Ignoring invalid decimal logical type: {}", err);
Ok(inner)
}
}
},
);
}
"big-decimal" => {
return try_convert_to_logical_type(
"big-decimal",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Bytes],
|_| -> AvroResult<Schema> { Ok(Schema::BigDecimal) },
);
}
"uuid" => {
return try_convert_to_logical_type(
"uuid",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::String, SchemaKind::Fixed],
|schema| match schema {
Schema::String => Ok(Schema::Uuid),
Schema::Fixed(FixedSchema { size: 16, .. }) => Ok(Schema::Uuid),
Schema::Fixed(FixedSchema { size, .. }) => {
warn!("Ignoring uuid logical type for a Fixed schema because its size ({size:?}) is not 16! Schema: {:?}", schema);
Ok(schema)
}
_ => {
warn!(
"Ignoring invalid uuid logical type for schema: {:?}",
schema
);
Ok(schema)
}
},
);
}
"date" => {
return try_convert_to_logical_type(
"date",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Int],
|_| -> AvroResult<Schema> { Ok(Schema::Date) },
);
}
"time-millis" => {
return try_convert_to_logical_type(
"date",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Int],
|_| -> AvroResult<Schema> { Ok(Schema::TimeMillis) },
);
}
"time-micros" => {
return try_convert_to_logical_type(
"time-micros",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::TimeMicros) },
);
}
"timestamp-millis" => {
return try_convert_to_logical_type(
"timestamp-millis",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::TimestampMillis) },
);
}
"timestamp-micros" => {
return try_convert_to_logical_type(
"timestamp-micros",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::TimestampMicros) },
);
}
"timestamp-nanos" => {
return try_convert_to_logical_type(
"timestamp-nanos",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::TimestampNanos) },
);
}
"local-timestamp-millis" => {
return try_convert_to_logical_type(
"local-timestamp-millis",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::LocalTimestampMillis) },
);
}
"local-timestamp-micros" => {
return try_convert_to_logical_type(
"local-timestamp-micros",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::LocalTimestampMicros) },
);
}
"local-timestamp-nanos" => {
return try_convert_to_logical_type(
"local-timestamp-nanos",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Long],
|_| -> AvroResult<Schema> { Ok(Schema::LocalTimestampNanos) },
);
}
"duration" => {
return try_convert_to_logical_type(
"duration",
parse_as_native_complex(complex, self, enclosing_namespace)?,
&[SchemaKind::Fixed],
|_| -> AvroResult<Schema> { Ok(Schema::Duration) },
);
}
// In this case, of an unknown logical type, we just pass through the underlying
// type.
_ => {}
},
// The spec says to ignore invalid logical types and just pass through the
// underlying type. It is unclear whether that applies to this case or not, where the
// `logicalType` is not a string.
Some(value) => return Err(Error::GetLogicalTypeFieldType(value.clone())),
_ => {}
}
match complex.get("type") {
Some(Value::String(t)) => match t.as_str() {
"record" => match parse_location {
RecordSchemaParseLocation::Root => {
self.parse_record(complex, enclosing_namespace)
}
RecordSchemaParseLocation::FromField => {
self.fetch_schema_ref(t, enclosing_namespace)
}
},
"enum" => self.parse_enum(complex, enclosing_namespace),
"array" => self.parse_array(complex, enclosing_namespace),
"map" => self.parse_map(complex, enclosing_namespace),
"fixed" => self.parse_fixed(complex, enclosing_namespace),
other => self.parse_known_schema(other, enclosing_namespace),
},
Some(Value::Object(data)) => {
self.parse_complex(data, enclosing_namespace, RecordSchemaParseLocation::Root)
}
Some(Value::Array(variants)) => self.parse_union(variants, enclosing_namespace),
Some(unknown) => Err(Error::GetComplexType(unknown.clone())),
None => Err(Error::GetComplexTypeField),
}
}