package Enigmatic::Types; use DAKKAR::p; use MooseX::Types -declare => [qw( Letter RotorPos WiringMap ReflWiringMap PlugWiringMap PlugboardT RotorT ReflectorT )]; use MooseX::Types::Moose qw(Str Int); use MooseX::Types::Structured qw(Map); use Moose::Util::TypeConstraints; subtype Letter, as Str, where { /[A-Z]/ }; subtype RotorPos, as Int, where { $_ >=0 and $_ <= 25 }; coerce RotorPos, from Letter, via { ord($_) - ord('A') }; subtype WiringMap, as Map[Letter,Letter], where { my $in = $_->keys->sort->join; my $out = $_->values->sort->join; $in eq $out and $in eq ['A'..'Z']->join; }; subtype ReflWiringMap, as WiringMap, where { my %reverse; @reverse{values %$_} = keys %$_; (join '',@reverse{keys %$_}) eq $_->values->join; }; sub _coerce_wiring { my @out = $_[0]->uc->split(qr//)->flatten; croak "invalid wiring string <$_>" unless @out == 26; my %ret; @ret{'A'..'Z'}=@out; \%ret; } coerce WiringMap, from Str, via \&_coerce_wiring; coerce ReflWiringMap, from Str, via \&_coerce_wiring; subtype PlugWiringMap, as ReflWiringMap; coerce PlugWiringMap, from Str, via { my %map = map { $_, $_ } 'A'..'Z'; my $in = $_; $in =~ s{\s+}{}g; my @pairs = $in->split(qr{(..)})->grep(sub{length})->flatten; for my $pair (@pairs) { my ($from,$to) = ($pair =~ m{(.)(.)}); croak "useless plug $pair" if $from eq $to; croak "excessive plug in $from" if $map{$from} ne $from; croak "excessive plug in $to" if $map{$to} ne $to; $map{$from} = $to; $map{$to} = $from; } return \%map; }; subtype RotorT, as class_type('Enigmatic::Rotor'); coerce RotorT, from Str, via { require Enigmatic::RotorBox; Enigmatic::RotorBox->new->get($_) }; subtype ReflectorT, as class_type('Enigmatic::Reflector'); coerce ReflectorT, from Str, via { require Enigmatic::ReflectorBox; Enigmatic::ReflectorBox->new->get($_) }; subtype PlugboardT, as class_type('Enigmatic::Plugboard'); coerce PlugboardT, from Str, via { require Enigmatic::Plugboard; Enigmatic::Plugboard->new($_); };