removed unrequired explicit 'list' data levels
[scpubgit/System-Introspector.git] / lib / System / Introspector / Util.pm
index f2a7555..530f9f9 100644 (file)
@@ -3,21 +3,35 @@ use strictures 1;
 package System::Introspector::Util;
 use Exporter 'import';
 use IPC::Run qw( run );
+use IPC::Open2;
+use File::Spec;
+use Scalar::Util qw( blessed );
+use Capture::Tiny qw( capture_stderr );
 
 our @EXPORT_OK = qw(
     handle_from_command
     handle_from_file
-    files_from_dir
     output_from_command
     output_from_file
+    lines_from_command
+    files_from_dir
     transform_exceptions
 );
 
+do {
+    package System::Introspector::_Exception;
+    use Moo;
+    has message => (is => 'ro');
+};
+
+sub fail { die System::Introspector::_Exception->new(message => shift) }
+sub is_report_exception { ref(shift) eq 'System::Introspector::_Exception' }
+
 sub files_from_dir {
     my ($dir) = @_;
     my $dh;
     opendir $dh, $dir
-        or die "Unable to read directory $dir: $!\n";
+        or fail "Unable to read directory $dir: $!";
     my @files;
     while (defined( my $item = readdir $dh )) {
         next if -d "$dir/$item";
@@ -28,10 +42,12 @@ sub files_from_dir {
 
 sub transform_exceptions (&) {
     my ($code) = @_;
-    local $@;
     my $result = eval { $code->() };
-    return { error => "$@" }
-        if $@;
+    if (my $error = $@) {
+        return { __error__ => $error->message }
+            if is_report_exception $error;
+        die $@;
+    }
     return $result;
 }
 
@@ -45,22 +61,54 @@ sub output_from_command {
         if wantarray;
     $command = join ' ', @$command
         if ref $command;
-    die "Error running command ($command): $err\n"
+    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 die "Unable to read from command '$command': $!\n";
+    my $pipe;
+    local $@;
+    my $ok = eval {
+        my $out;
+        my $child_pid;
+        my @lines;
+        my ($err) = capture_stderr {
+          $child_pid = open2($out, File::Spec->devnull, $command);
+          @lines = <$out>;
+          close $out;
+          waitpid $child_pid, 0;
+        };
+        my $content = join '', @lines;
+        my $status = $? >> 8;
+        $err = "Unknown error"
+            unless defined $err;
+        fail "Command error ($command): $err\n"
+            if $status;
+        open $pipe, '<', \$content;
+        1;
+    };
+    unless ($ok) {
+        my $err = $@;
+        die $err
+            if blessed($err) and $err->isa('System::Introspector::_Exception');
+        fail "Error from command '$command': $err";
+    }
     return $pipe;
 }
 
 sub handle_from_file {
     my ($file) = @_;
     open my $fh, '<', $file
-        or die "Unable to read $file: $!\n";
+        or fail "Unable to read $file: $!";
     return $fh;
 }