Skip to content

Commit

Permalink
Allow Enum type constraints to make "natural" unions and intersection…
Browse files Browse the repository at this point in the history
…s which are also Enum type constraints (issue #111)
  • Loading branch information
tobyink committed Sep 8, 2022
1 parent 522a14f commit 6dcabfd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
33 changes: 33 additions & 0 deletions lib/Type/Tiny/Enum.pm
Expand Up @@ -68,6 +68,23 @@ sub new {
return $proto->SUPER::new( %opts );
} #/ sub new

sub new_union {
my $proto = shift;
my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
my @types = @{ delete $opts{type_constraints} };
my @values = map @$_, @types;
$proto->new( %opts, values => \@values );
}

sub new_intersection {
my $proto = shift;
my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
my @types = @{ delete $opts{type_constraints} };
my %values; ++$values{$_} for map @$_, @types;
my @values = sort grep $values{$_}==@types, keys %values;
$proto->new( %opts, values => \@values );
}

sub values { $_[0]{values} }
sub unique_values { $_[0]{unique_values} }
sub constraint { $_[0]{constraint} ||= $_[0]->_build_constraint }
Expand Down Expand Up @@ -406,6 +423,22 @@ Enum type constraints.
This package inherits from L<Type::Tiny>; see that for most documentation.
Major differences are listed below:
=head2 Constructors
=over
=item C<< new_union( type_constraints => @enums, %opts ) >>
Creates a new enum type constraint which is the union of existing enum
type constraints.
=item C<< new_intersection( type_constraints => @enums, %opts ) >>
Creates a new enum type constraint which is the intersection of existing enum
type constraints.
=back
=head2 Attributes
=over
Expand Down
12 changes: 12 additions & 0 deletions lib/Type/Tiny/Intersection.pm
Expand Up @@ -26,6 +26,18 @@ __PACKAGE__->_install_overloads(
sub new_by_overload {
my $proto = shift;
my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;

my @types = @{ $opts{type_constraints} };
if ( my @makers = map scalar( blessed($_) && $_->can( 'new_intersection' ) ), @types ) {
my $first_maker = shift @makers;
if ( ref $first_maker ) {
my $all_same = not grep $_ ne $first_maker, @makers;
if ( $all_same ) {
return ref( $types[0] )->$first_maker( %opts );
}
}
}

return $proto->new( \%opts );
}

Expand Down
12 changes: 12 additions & 0 deletions lib/Type/Tiny/Union.pm
Expand Up @@ -25,6 +25,18 @@ __PACKAGE__->_install_overloads(
sub new_by_overload {
my $proto = shift;
my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;

my @types = @{ $opts{type_constraints} };
if ( my @makers = map scalar( blessed($_) && $_->can( 'new_union' ) ), @types ) {
my $first_maker = shift @makers;
if ( ref $first_maker ) {
my $all_same = not grep $_ ne $first_maker, @makers;
if ( $all_same ) {
return ref( $types[0] )->$first_maker( %opts );
}
}
}

return $proto->new( \%opts );
}

Expand Down
55 changes: 55 additions & 0 deletions t/20-modules/Type-Tiny-Enum/union_intersection.t
@@ -0,0 +1,55 @@
=pod
=encoding utf-8
=head1 PURPOSE
Checks enums form natural unions and intersections.
=head1 AUTHOR
Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
=head1 COPYRIGHT AND LICENCE
This software is copyright (c) 2022 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

use strict;
use warnings;
use Test::More;

use Types::Standard qw( Enum );

my $foo = Enum[ 1, 2, 3 ];
my $bar = Enum[ 1, 4, 5 ];

isa_ok(
( my $foo_union_bar = $foo | $bar ),
'Type::Tiny::Enum',
'$foo_union_bar',
);

is_deeply(
$foo_union_bar->unique_values,
[ 1 .. 5 ],
'$foo_union_bar->unique_values',
);

isa_ok(
( my $foo_intersect_bar = $foo & $bar ),
'Type::Tiny::Enum',
'$foo_intersect_bar',
);

is_deeply(
$foo_intersect_bar->unique_values,
[ 1 ],
'$foo_intersect_bar->unique_values',
);

done_testing;

0 comments on commit 6dcabfd

Please sign in to comment.