File Coverage

blib/lib/Devel/REPL/Server/Select.pm
Criterion Covered Total %
statement 24 78 30.7
branch 0 22 0.0
condition 0 11 0.0
subroutine 8 15 53.3
pod 0 4 0.0
total 32 130 24.6


line stmt bran cond sub pod time code
1             package Devel::REPL::Server::Select;
2              
3 1     1   727 use strict;
  1         1  
  1         23  
4 1     1   3 use warnings;
  1         1  
  1         17  
5              
6 1     1   484 use Devel::REPL;
  1         53180  
  1         35  
7 1     1   421 use IO::Pty;
  1         14862  
  1         38  
8 1     1   417 use IO::Select;
  1         1023  
  1         36  
9 1     1   433 use IO::Socket;
  1         9533  
  1         3  
10 1     1   351 use Term::ReadLine;
  1         2  
  1         15  
11 1     1   3 use Scalar::Util;
  1         1  
  1         494  
12              
13             my $TERM;
14              
15             sub run_repl {
16 0     0 0   my ($class, %args) = @_;
17 0           my $repl = $class->new(%args);
18              
19 0           $repl->create;
20              
21 0           @_ = ($repl);
22 0           goto &{$repl->can('run')};
  0            
23             }
24              
25             sub new {
26 0     0 0   my ($class, %args) = @_;
27             my $self = bless {
28             port => $args{port},
29             path => $args{path},
30 0   0       skip_levels => $args{skip_levels} // 0,
31             socket => undef,
32             pty => undef,
33             repl => undef,
34             }, $class;
35              
36 0           return $self;
37             }
38              
39             sub create {
40 0     0 0   my ($self) = @_;
41              
42 0           $self->{pty} = IO::Pty->new;
43 0           $self->{fds} = IO::Select->new;
44              
45 0 0         if ($self->{port}) {
    0          
46             $self->{socket} = IO::Socket::INET->new(
47             PeerAddr => '127.0.0.1',
48             PeerPort => $self->{port},
49 0           Blocking => 0,
50             );
51             } elsif ($self->{path}) {
52             $self->{socket} = IO::Socket::UNIX->new(
53             Local => $self->{path},
54 0           );
55             }
56              
57 0 0         die "Error during connect: $!" unless $self->{socket};
58              
59 0           $self->{fds}->add($self->{pty}, $self->{socket});
60              
61 0   0       my $term = $TERM ||= Term::ReadLine->new;
62 0           my $weak_self = $self;
63              
64 0           Scalar::Util::weaken($weak_self);
65              
66 0           $term->newTTY($self->{pty}->slave, $self->{pty}->slave);
67 0     0     $term->event_loop(sub { $weak_self->_shuttle_data });
  0            
68              
69 0           $self->{repl} = Devel::REPL->new(term => $term);
70 0           $self->{repl}->load_plugin('InProcess');
71 0           $self->{repl}->skip_levels($self->{skip_levels});
72             }
73              
74             sub run {
75 0     0 0   my ($self) = @_;
76              
77 0           $self->{repl}->run;
78             }
79              
80             sub _shuttle_data {
81 0     0     my ($self) = @_;
82              
83             eval {
84 0           for (;;) {
85 0           my ($rd, undef, $err) = IO::Select->select($self->{fds}, undef, $self->{fds}, 10);
86              
87 0 0 0       if ($err && @$err) {
88 0           die "One of the handles became invalid";
89             }
90              
91 0           my $got_input;
92 0           for my $hnd (@$rd) {
93 0 0         if ($hnd == $self->{socket}) {
94             # using anything > 1 here breaks (for example) control
95             # char sequences, because STDIN is buffered, and I have
96             # not found a way of either looking at the buffer or
97             # making it unbuffered
98 0           my $read = _from_to($self->{socket}, $self->{pty}, 1);
99 0 0         if ($read == 0) {
100 0           $self->{pty}->close_slave;
101 0           $self->{pty}->close;
102             }
103 0           $got_input = 1;
104             }
105 0 0         if ($hnd == $self->{pty}) {
106 0           _from_to($self->{pty}, $self->{socket}, 1000);
107             }
108             }
109 0 0         last if $got_input;
110             }
111              
112 0           1;
113 0 0         } or do {
114 0           warn "Error while waiting for input $@\n";
115             };
116             }
117              
118             sub _from_to {
119 0     0     my ($from, $to, $max) = @_;
120 0           my $buff;
121              
122 0           my $count = sysread $from, $buff, $max;
123 0 0         die "Error during read: $!" if !defined $count;
124 0           my $written = syswrite $to, $buff, $count;
125 0 0 0       die "Error during write: $!" if !defined $written || $written != $count;
126 0           return $count;
127             }
128              
129             1;