Avro
view release on metacpan or search on metacpan
lib/Avro/Schema.pm view on Meta::CPAN
unless (defined $namespace && length $namespace) {
$namespace = $param{namespace};
}
$schema->set_names($namespace, $name);
$schema->add_name($names);
return $schema;
}
sub is_type_valid {
return $NamedType{ $_[1] || "" };
}
sub set_names {
my $schema = shift;
my ($namespace, $name) = @_;
my @parts = split /\./, ($name || ""), -1;
if (@parts > 1) {
$name = pop @parts;
$namespace = join ".", @parts;
if (grep { ! length $_ } @parts) {
throw Avro::Schema::Error::Name(
"name '$name' is not a valid name"
);
}
}
## 1.3.2 The name portion of a fullname, and record field names must:
## * start with [A-Za-z_]
## * subsequently contain only [A-Za-z0-9_]
my $type = $schema->{type};
unless (length $name && $name =~ m/^[A-Za-z_][A-Za-z0-9_]*$/) {
throw Avro::Schema::Error::Name(
"name '$name' is not valid for $type"
);
}
if (defined $namespace && length $namespace) {
for (split /\./, $namespace, -1) {
unless ($_ && /^[A-Za-z_][A-Za-z0-9_]*$/) {
throw Avro::Schema::Error::Name(
"namespace '$namespace' is not valid for $type"
);
}
}
}
$schema->{name} = $name;
$schema->{namespace} = $namespace;
}
sub add_name {
my $schema = shift;
my ($names) = @_;
my $name = $schema->fullname;
if ( exists $names->{ $name } ) {
throw Avro::Schema::Error::Parse( "Name $name is already defined" );
}
$names->{$name} = $schema;
Scalar::Util::weaken( $names->{$name} );
return;
}
sub fullname {
my $schema = shift;
return join ".",
grep { defined $_ && length $_ }
map { $schema->{$_ } }
qw/namespace name/;
}
sub namespace {
my $schema = shift;
return $schema->{namespace};
}
package Avro::Schema::Record;
our @ISA = qw/Avro::Schema::Named/;
use Scalar::Util;
my %ValidOrder = map { $_ => 1 } qw/ascending descending ignore/;
sub new {
my $class = shift;
my %param = @_;
my $names = $param{names} ||= {};
my $schema = $class->SUPER::new(%param);
my $fields = $param{struct}{fields}
or throw Avro::Schema::Error::Parse("Record must have Fields");
throw Avro::Schema::Error::Parse("Record.Fields must me an array")
unless ref $fields eq 'ARRAY';
my $namespace = $schema->namespace;
my @fields;
for my $field (@$fields) {
my $f = Avro::Schema::Field->new($field, $names, $namespace);
push @fields, $f;
}
$schema->{fields} = \@fields;
return $schema;
}
sub to_struct {
my $schema = shift;
my $known_names = shift || {};
## consider that this record type is now known (will serialize differently)
my $fullname = $schema->fullname;
if ($known_names->{ $fullname }++) {
return $fullname;
}
return {
type => $schema->{type},
name => $fullname,
fields => [
map { $_->to_struct($known_names) } @{ $schema->{fields} }
],
};
}
sub fields {
my $schema = shift;
return $schema->{fields};
}
sub fields_as_hash {
my $schema = shift;
unless (exists $schema->{_fields_as_hash}) {
$schema->{_fields_as_hash} = {
map { $_->{name} => $_ } @{ $schema->{fields} }
};
}
return $schema->{_fields_as_hash};
}
sub is_data_valid {
my $schema = shift;
my $data = shift;
for my $field (@{ $schema->{fields} }) {
my $key = $field->{name};
return 0 unless $field->is_data_valid($data->{$key});
}
return 1;
}
package Avro::Schema::Field;
sub to_struct {
my $field = shift;
my $known_names = shift || {};
my $type = $field->{type}->to_struct($known_names);
return { name => $field->{name}, type => $type };
}
sub new {
my $class = shift;
my ($struct, $names, $namespace) = @_;
my $name = $struct->{name};
throw Avro::Schema::Error::Parse("Record.Field.name is required")
unless defined $name && length $name;
my $type = $struct->{type};
throw Avro::Schema::Error::Parse("Record.Field.name is required")
unless defined $type && length $type;
$type = Avro::Schema->parse_struct($type, $names, $namespace);
my $field = { name => $name, type => $type };
#TODO: find where to weaken precisely
#Scalar::Util::weaken($struct->{type});
if (exists $struct->{default}) {
my $is_valid = $type->is_data_valid($struct->{default});
my $t = $type->type;
throw Avro::Schema::Error::Parse(
"default value doesn't validate $t: '$struct->{default}'"
) unless $is_valid;
## small Perlish special case
if ($type eq 'boolean') {
$field->{default} = $struct->{default} ? 1 : 0;
}
else {
$field->{default} = $struct->{default};
}
}
if (my $order = $struct->{order}) {
throw Avro::Schema::Error::Parse(
"Order '$order' is not valid'"
) unless $ValidOrder{$order};
$field->{order} = $order;
}
return bless $field, $class;
}
sub is_data_valid {
my $field = shift;
my $data = shift;
return 1 if $field->{type}->is_data_valid($data);
return 0;
}
package Avro::Schema::Enum;
our @ISA = qw/Avro::Schema::Named/;
sub new {
my $class = shift;
my %param = @_;
my $schema = $class->SUPER::new(%param);
my $struct = $param{struct}
or throw Avro::Schema::Error::Parse("Enum instantiation");
my $symbols = $struct->{symbols} || [];
unless (@$symbols) {
throw Avro::Schema::Error::Parse("Enum needs at least one symbol");
}
my %symbols;
my $pos = 0;
for (@$symbols) {
if (ref $_) {
throw Avro::Schema::Error::Parse(
"Enum.symbol should be a string"
);
}
throw Avro::Schema::Error::Parse("Duplicate symbol in Enum")
if exists $symbols{$_};
$symbols{$_} = $pos++;
}
$schema->{hash_symbols} = \%symbols;
( run in 0.964 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )