# Do not modify!
# This file is autogenerated and your changes will be overwritten.
package Your::Command::Dispatcher;
use Getopt::Long::Descriptive qw/describe_options prog_name/;
use strict;
use warnings;

our $VERSION = '0.09';

my $me = prog_name;

my $program = {
    'Your::Command' => {
        'opt_spec' => [
            [ 'help|h',           'print usage message and exit' ],
            [ 'debug-dispatcher', 'print App::Dispatcher debug information' ],
            [ 'dry-run|n',        'print out SQL instead of running it' ]
        ],
        'arg_spec' => [ [ 'command=s', 'what to do', { 'required' => 1 } ] ],
        'name'          => 'Command',
        'usage_desc'    => 'usage: %c [options] <command> [...]',
        'order'         => 2147483647,
        'class'         => 'Your::Command',
        'abstract'      => '(unknown)',
        'require_order' => 1,
        'getopt_conf'   => [ 'require_order' ]
    },
    'Your::Command::deploy' => {
        'opt_spec' => [ [ 'help|h', 'print usage message and exit' ] ],
        'arg_spec' => [
            [
                'database=s',
                'production|development',
                {
                    'required' => 1,
                    'default'  => 'development'
                }
            ]
        ],
        'name'          => 'deploy',
        'usage_desc'    => 'usage: %c deploy [options] <database>',
        'order'         => 1,
        'class'         => 'Your::Command::deploy',
        'abstract'      => 'deploy to a database',
        'require_order' => 0,
        'getopt_conf'   => [ 'permute' ]
    },
    'Your::Command::test' => {
        'opt_spec' => [ [ 'help|h', 'print usage message and exit' ] ],
        'arg_spec' => [
            [
                'database=s',
                'production|development',
                {
                    'required' => 1,
                    'default'  => 'development'
                }
            ]
        ],
        'name'          => 'test',
        'usage_desc'    => 'usage: %c test [options] <database>',
        'order'         => 2,
        'class'         => 'Your::Command::test',
        'abstract'      => 'test a database',
        'require_order' => 0,
        'getopt_conf'   => [ 'permute' ]
    },
    'Your::Command::undeploy' => {
        'opt_spec' => [ [ 'help|h', 'print usage message and exit' ] ],
        'arg_spec' => [
            [
                'database=s',
                'production|development',
                {
                    'required' => 1,
                    'default'  => 'development'
                }
            ]
        ],
        'name'          => 'undeploy',
        'usage_desc'    => 'usage: %c undeploy [options] <database>',
        'order'         => 3,
        'class'         => 'Your::Command::undeploy',
        'abstract'      => 'undeploy a database',
        'require_order' => 0,
        'getopt_conf'   => [ 'permute' ]
    }
};

sub _commands {
    my $cmd = shift;
    require List::Util;

    my @commands =
      grep { $_->{class} =~ m/${cmd}::/ and not $_->{class} =~ m/${cmd}::.*:/ }
      values %$program;

    return unless @commands;

    my $max = 4 + List::Util::max( map { length $_->{name} } @commands );

    return
      map { sprintf( "    %-${max}s %s\n", $_->{name}, $_->{abstract} ) }
      sort { $a->{order} <=> $b->{order} } @commands;
}

sub _message {
    my ( $cmd, $usage, $FD ) = @_;

    print $FD $usage->text;

    my @arg_spec = @{ $program->{$cmd}->{arg_spec} };
    return unless @arg_spec;

    my @commands = _commands($cmd);

    if (@commands) {
        my $x = $arg_spec[0]->[0];
        $x =~ s/[\|=].*//;

        print $FD "\nValid values for <$x> include:\n";
        print $FD join( '', @commands );
    }
}

sub _usage {
    _message( @_, *STDERR );
    exit 2;
}

sub _help {
    _message( @_, *STDOUT );
    exit 1;
}

sub _mtime_check {
    my $cmd = shift;
    require Module::Load;
    Module::Load::load($cmd);

    ( my $plugin_file = $cmd . '.pm' ) =~ s!::!/!g;

    my $file = $INC{$plugin_file};

    if ( -M $file < -M __FILE__ ) {
        warn "warning: "
          . __PACKAGE__
          . " is out of date:\n    "
          . scalar localtime( ( stat(__FILE__) )[9] ) . " "
          . __FILE__
          . "\n    "
          . scalar localtime( ( stat($file) )[9] )
          . " $file\n";
    }
}

sub run {
    my $class = shift;

    my $cmd = 'Your::Command';

    _mtime_check($cmd);

    my @ORIG_ARGV = @ARGV;

    # Global options, possibly actual (main) command options
    my ( $gopt, $gusage ) = describe_options(
        $program->{$cmd}->{usage_desc},
        @{ $program->{$cmd}->{opt_spec} },
        { getopt_conf => $program->{$cmd}->{getopt_conf} },
    );

    my $debug = $gopt->debug_dispatcher;
    if ($debug) {
        print __PACKAGE__. ": $cmd @ORIG_ARGV\n";
        print __PACKAGE__. ":   gopt:\n";
        map { print __PACKAGE__ . ":     " . $_ . ' => ' . $gopt->$_ . "\n" }
          keys %$gopt;
    }

    _help( $cmd, $gusage ) if ( $gopt->can('help') && $gopt->help );

    # Look for a subcommand
    while ( @ARGV && exists $program->{ $cmd . '::' . $ARGV[0] } ) {
        $cmd = $cmd . '::' . shift @ARGV;
    }

    print __PACKAGE__. ": $cmd @ARGV\n" if $debug;

    _mtime_check($cmd);

    my ( $opt, $usage ) = ( $gopt, $gusage );
    if ( $cmd ne 'Your::Command' ) {
        ( $opt, $usage ) = describe_options(
            $program->{$cmd}->{usage_desc},
            @{ $program->{$cmd}->{opt_spec} },
            { getopt_conf => $program->{$cmd}->{getopt_conf} },
        );
    }

    if ( $debug and $cmd ne 'Your::Command' ) {
        print __PACKAGE__. ":   opt:\n";
        map { print __PACKAGE__ . ":     " . $_ . ' => ' . $opt->$_ . "\n" }
          keys %$opt;
        print __PACKAGE__. ": $cmd @ARGV\n";
    }

    _help( $cmd, $usage ) if ( $opt->can('help') && $opt->help );

    my @arg_spec = @{ $program->{$cmd}->{arg_spec} };

    # Missing a required argument?
    my @narg_spec = @arg_spec;
    while ( scalar @narg_spec > scalar @ARGV ) {

        if ($debug) {
            print __PACKAGE__
              . ": arg_spec/ARGV "
              . scalar @narg_spec . '.'
              . @ARGV . "\n";
        }

        my $arg = pop @narg_spec;
        next unless ( exists $arg->[2]->{required} );

        _usage( $cmd, $usage );
    }

    # Now rebuild the whole command line that includes options and
    # arguments together.
    my @newargv;
    my @remainder;

    my $i = 0;
    while ( my $val = shift @ARGV ) {
        if ( !@arg_spec ) {
            @remainder = ( $val, @ARGV );
            last;
        }
        my $arg = shift @arg_spec;
        my $x   = $arg->[0];
        $x =~ s/[|=].*//;
        push( @newargv, '--' . $x, $val );
    }

    @ARGV = @newargv;

    if ($debug) {
        print __PACKAGE__. ": \@remainder: @remainder\n";
        print __PACKAGE__. ": $cmd @ARGV\n";
    }

    my ( $new_opt, $new_usage ) = describe_options(
        $program->{$cmd}->{usage_desc},
        @{ $program->{$cmd}->{arg_spec} },
        map { [ $_->[0], $_->[1] ] }    # ignore 'require', 'default'
          @{ $program->{$cmd}->{opt_spec} },
    );

    while ( my ( $key, $val ) = each %$opt ) {
        $new_opt->{$key} = $val;
    }
    $opt = $new_opt;

    @ARGV = @remainder;

    if ($debug) {
        print __PACKAGE__. ":   opt:\n";
        map { print __PACKAGE__ . ":     " . $_ . ' => ' . $opt->$_ . "\n" }
          keys %$opt;
        print __PACKAGE__. ": $cmd\->run( \$opt, \$gopt ) @ARGV\n\n";
    }

    require Module::Load;
    Module::Load::load($cmd);

    # FIXME move this check into App::Dispatcher?
    if ( !$cmd->can('run') ) {
        _usage( $cmd, $usage );
        die "$cmd missing run() method or 'required' attribute on arg 1\n";
    }
    return $cmd->run( $opt, $gopt, $usage, $gusage );
}

1;
__END__


=head1 NAME

Your::Command::Dispatch - Dispatcher for Your::Command commands

=head1 SYNOPSIS

  use Your::Command::Dispatch;
  Your::Command::Dispatch->run;

=head1 DESCRIPTION

B<Your::Command::Dispatch> provides option checking, argument checking,
and command dispatching for commands implemented under the
Your::Command::* namespace.

This class has a single method:

=over 4

=item run

Dispatch to a L<Your::Command> command based on the contents of @ARGV.

=back

This module was automatically generated by L<App::Dispatcher>(3p).

=head1 SEE ALSO

L<App::Dispatcher>(3p), L<app-dispatcher>(1)

=head1 AUTHOR

Mark Lawrence E<lt>nomad@null.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2011 Mark Lawrence E<lt>nomad@null.netE<gt>

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

=cut

# vim: set tabstop=4 expandtab:
