# This class staples together two unidirectional `IO`s to form a single, # bidirectional `IO`. # # Example (loopback): # ``` # io = IO::Stapled.new(*IO.pipe) # io.puts "linus" # io.gets # => "linus" # ``` # # Most methods simply delegate to the underlying `IO`s. class IO::Stapled < IO # If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`s. property? sync_close : Bool # Returns `true` if this `IO` is closed. # # Underlying `IO`s might have a different status. getter? closed : Bool = false # Creates a new `IO::Stapled` which reads from *reader* and writes to *writer*. def initialize(@reader : IO, @writer : IO, @sync_close : Bool = false) end # Reads a single byte from `reader`. def read_byte : UInt8? check_open @reader.read_byte end # Reads a slice from `reader`. def read(slice : Bytes) : Int32 check_open @reader.read(slice).to_i32 end # Gets a string from `reader`. def gets(delimiter : Char, limit : Int, chomp = false) : String? check_open @reader.gets(delimiter, limit, chomp) end # Peeks into `reader`. def peek : Bytes? check_open @reader.peek end # Skips `reader`. def skip(bytes_count : Int) : Nil check_open @reader.skip(bytes_count) end # Skips `reader`. def skip_to_end : Nil check_open @reader.skip_to_end end # Writes a byte to `writer`. def write_byte(byte : UInt8) : Nil check_open @writer.write_byte(byte) end # Writes a slice to `writer`. def write(slice : Bytes) : Nil check_open return if slice.empty? @writer.write(slice) end # Flushes `writer`. def flush : self check_open @writer.flush self end # Closes this `IO`. # # If `sync_close?` is `true`, it will also close the underlying `IO`s. def close : Nil return if @closed @closed = true if @sync_close @reader.close @writer.close end end # Creates a pair of bidirectional pipe endpoints connected with each other # and passes them to the given block. # # Both endpoints and the underlying `IO`s are closed after the block # (even if `sync_close?` is `false`). def self.pipe(read_blocking : Bool? = nil, write_blocking : Bool? = nil, &) IO.pipe(read_blocking, write_blocking) do |a_read, a_write| IO.pipe(read_blocking, write_blocking) do |b_read, b_write| a, b = new(a_read, b_write, true), new(b_read, a_write, true) begin yield a, b ensure a.close b.close end end end end # Creates a pair of bidirectional pipe endpoints connected with each other # and returns them in a `Tuple`. def self.pipe(read_blocking : Bool? = nil, write_blocking : Bool? = nil) : {self, self} a_read, a_write = IO.pipe(read_blocking, write_blocking) b_read, b_write = IO.pipe(read_blocking, write_blocking) return new(a_read, b_write, true), new(b_read, a_write, true) end end