The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package HTML::FormFu::Role::Element::Coercible;
$HTML::FormFu::Role::Element::Coercible::VERSION = '2.00';
use Moose::Role;

use Carp qw( croak );
use HTML::FormFu::Util qw( require_class );

sub as {
    my ( $self, $type, %attrs ) = @_;

    return $self->_coerce(
        type       => $type,
        attributes => \%attrs,
        errors     => $self->_errors,
        package    => __PACKAGE__,
    );
}

sub _coerce {
    my ( $self, %args ) = @_;

    for (qw( type attributes package )) {
        croak "$_ argument required" if !defined $args{$_};
    }

    my $class = $args{type};
    if ( $class !~ /^\+/ ) {
        $class = "HTML::FormFu::Element::$class";
    }

    require_class($class);

    my $element = $class->new( { type => $args{type}, } );

    for my $method ( qw(
        name
        attributes              comment
        comment_attributes      label
        label_attributes        label_filename
        render_method           parent
        ) )
    {
        $element->$method( $self->$method );
    }

    _coerce_processors_and_errors( $self, $element, %args );

    $element->attributes( $args{attributes} );

    croak "element cannot be coerced to type '$args{type}'"
        unless $element->isa( $args{package} )
            || $element->does( $args{package} );

    $element->value( $self->value );

    return $element;
}

sub _coerce_processors_and_errors {
    my ( $self, $element, %args ) = @_;

    if ( $args{errors} && @{ $args{errors} } > 0 ) {

        my @errors = @{ $args{errors} };
        my @new_errors;

        for my $list ( qw(
            _filters        _constraints
            _inflators      _validators
            _transformers   _deflators
            ) )
        {
            $element->$list( [] );

            for my $processor ( @{ $self->$list } ) {
                my @errors_to_copy = map { $_->clone }
                    grep { $_->processor == $processor } @errors;

                my $processor_clone = $processor->clone;

                $processor_clone->parent($element);

                map { $_->processor($processor_clone) } @errors_to_copy;

                push @{ $element->$list }, $processor_clone;

                push @new_errors, @errors_to_copy;
            }
        }
        $element->_errors( \@new_errors );
    }
    else {
        $element->_errors( [] );
    }

    return;
}

1;