File Coverage

blib/lib/Devel/REPL/Server/Select.pm
Criterion Covered Total %
statement 24 79 30.3
branch 0 26 0.0
condition 0 11 0.0
subroutine 8 15 53.3
pod 0 4 0.0
total 32 135 23.7


line stmt bran cond sub pod time code
1             package Devel::REPL::Server::Select;
2              
3 1     1   754 use strict;
  1         1  
  1         23  
4 1     1   3 use warnings;
  1         1  
  1         18  
5              
6 1     1   396 use Devel::REPL::Script;
  1         278388  
  1         4  
7 1     1   605 use IO::Pty;
  1         15770  
  1         40  
8 1     1   402 use IO::Select;
  1         1020  
  1         35  
9 1     1   420 use IO::Socket;
  1         9149  
  1         3  
10 1     1   350 use Term::ReadLine;
  1         1  
  1         20  
11 1     1   3 use Scalar::Util;
  1         2  
  1         541  
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             skip_levels => $args{skip_levels} // 0,
31             profile => $args{profile},
32             rcfile => $args{rcfile},
33 0   0       socket => undef,
34             pty => undef,
35             repl => undef,
36             repl_script => undef,
37             }, $class;
38              
39 0           return $self;
40             }
41              
42             sub create {
43 0     0 0   my ($self) = @_;
44              
45 0           $self->{pty} = IO::Pty->new;
46 0           $self->{fds} = IO::Select->new;
47              
48 0 0         if ($self->{port}) {
    0          
49             $self->{socket} = IO::Socket::INET->new(
50             PeerAddr => '127.0.0.1',
51             PeerPort => $self->{port},
52 0           Blocking => 0,
53             );
54             } elsif ($self->{path}) {
55             $self->{socket} = IO::Socket::UNIX->new(
56             Peer => $self->{path},
57 0           );
58             }
59              
60 0 0         die "Error during connect: $!" unless $self->{socket};
61              
62 0           $self->{fds}->add($self->{pty}, $self->{socket});
63              
64 0   0       my $term = $TERM ||= Term::ReadLine->new;
65 0           my $weak_self = $self;
66              
67 0           Scalar::Util::weaken($weak_self);
68              
69 0           $term->newTTY($self->{pty}->slave, $self->{pty}->slave);
70 0     0     $term->event_loop(sub { $weak_self->_shuttle_data });
  0            
71              
72 0           $self->{repl} = Devel::REPL->new(term => $term);
73             $self->{repl_script} = Devel::REPL::Script->new(
74             _repl => $self->{repl},
75             !$self->{profile} ? () : (
76             profile => $self->{profile},
77             ),
78             !$self->{rcfile} ? () : (
79             rcfile => $self->{rcfile},
80 0 0         ),
    0          
81             );
82 0           $self->{repl}->load_plugin('InProcess');
83 0           $self->{repl}->skip_levels($self->{skip_levels});
84             }
85              
86             sub run {
87 0     0 0   my ($self) = @_;
88              
89 0           $self->{repl}->run;
90             }
91              
92             sub _shuttle_data {
93 0     0     my ($self) = @_;
94              
95             eval {
96 0           for (;;) {
97 0           my ($rd, undef, $err) = IO::Select->select($self->{fds}, undef, $self->{fds}, 10);
98              
99 0 0 0       if ($err && @$err) {
100 0           die "One of the handles became invalid";
101             }
102              
103 0           my $got_input;
104 0           for my $hnd (@$rd) {
105 0 0         if ($hnd == $self->{socket}) {
106             # using anything > 1 here breaks (for example) control
107             # char sequences, because STDIN is buffered, and I have
108             # not found a way of either looking at the buffer or
109             # making it unbuffered
110 0           my $read = _from_to($self->{socket}, $self->{pty}, 1);
111 0 0         if ($read == 0) {
112 0           $self->{pty}->close_slave;
113 0           $self->{pty}->close;
114             }
115 0           $got_input = 1;
116             }
117 0 0         if ($hnd == $self->{pty}) {
118 0           _from_to($self->{pty}, $self->{socket}, 1000);
119             }
120             }
121 0 0         last if $got_input;
122             }
123              
124 0           1;
125 0 0         } or do {
126 0           warn "Error while waiting for input $@\n";
127             };
128             }
129              
130             sub _from_to {
131 0     0     my ($from, $to, $max) = @_;
132 0           my $buff;
133              
134 0           my $count = sysread $from, $buff, $max;
135 0 0         die "Error during read: $!" if !defined $count;
136 0           my $written = syswrite $to, $buff, $count;
137 0 0 0       die "Error during write: $!" if !defined $written || $written != $count;
138 0           return $count;
139             }
140              
141             1;