use strictures 1;

package System::Introspector::Util;
use Exporter 'import';
use IPC::Run qw( run );

our @EXPORT_OK = qw(
    handle_from_command
    handle_from_file
    output_from_command
    output_from_file
    lines_from_command
    files_from_dir
    transform_exceptions
);

do {
    package System::Introspection::_Exception;
    use Moo;
    has message => (is => 'ro');
};

sub fail { die System::Introspection::_Exception->new(message => shift) }
sub is_report_exception { ref(shift) eq 'System::Introspection::_Exception' }

sub files_from_dir {
    my ($dir) = @_;
    my $dh;
    opendir $dh, $dir
        or fail "Unable to read directory $dir: $!";
    my @files;
    while (defined( my $item = readdir $dh )) {
        next if -d "$dir/$item";
        push @files, $item;
    }
    return @files;
}

sub transform_exceptions (&) {
    my ($code) = @_;
    my $result = eval { $code->() };
    if (my $error = $@) {
        return { error => $error->message }
            if is_report_exception $error;
        die $@;
    }
    return $result;
}

sub output_from_command {
    my ($command, $in) = @_;
    $in = ''
        unless defined $in;
    my ($out, $err) = ('', '');
    my $ok = run($command, \$in, \$out, \$err);
    return $out, $err, $ok
        if wantarray;
    $command = join ' ', @$command
        if ref $command;
    fail "Error running command ($command): $err"
        unless $ok;
    return $out;
}

sub lines_from_command {
    my ($command) = @_;
    my $output = output_from_command $command;
    chomp $output;
    return split m{\n}, $output;
}

sub handle_from_command {
    my ($command) = @_;
    open my $pipe, '-|', $command
        or fail "Unable to read from command '$command': $!";
    return $pipe;
}

sub handle_from_file {
    my ($file) = @_;
    open my $fh, '<', $file
        or fail "Unable to read $file: $!";
    return $fh;
}

sub output_from_file {
    my ($file) = @_;
    my $fh = handle_from_file $file;
    return <$fh>
        if wantarray;
    return do { local $/; <$fh> };
}

1;
