github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/pipes/streams/reader.go (about) 1 package streams 2 3 import ( 4 "bufio" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "os" 10 "sync" 11 12 "github.com/lmorg/murex/config" 13 "github.com/lmorg/murex/lang/stdio" 14 "github.com/lmorg/murex/lang/types" 15 "github.com/lmorg/murex/utils" 16 ) 17 18 // Reader is a wrapper around an io.Reader interface 19 type Reader struct { 20 mutex sync.Mutex 21 ctx context.Context 22 forceClose func() 23 reader io.Reader 24 readCloser io.ReadCloser 25 bRead uint64 26 bWritten uint64 27 dependents int 28 dataType string 29 dtLock sync.Mutex 30 } 31 32 // NewReader creates a new Stdio.Io interface wrapper around a io.Reader interface 33 func NewReader(reader io.Reader) (r *Reader) { 34 if reader == nil { 35 panic("streams.Reader interface has nil reader interface") 36 } 37 38 r = new(Reader) 39 r.reader = reader 40 r.ctx, r.forceClose = context.WithCancel(context.Background()) 41 return 42 } 43 44 // IsTTY returns false because the reader interface is not a pseudo-TTY 45 func (r *Reader) IsTTY() bool { return false } 46 47 func (t *Reader) File() *os.File { 48 return nil 49 } 50 51 // Stats provides real time stream stats. Useful for progress bars etc. 52 func (r *Reader) Stats() (bytesWritten, bytesRead uint64) { 53 //r.mutex.RLock() 54 r.mutex.Lock() 55 bytesWritten = r.bWritten 56 bytesRead = r.bRead 57 //r.mutex.RUnlock() 58 r.mutex.Unlock() 59 return 60 } 61 62 // Read is the reader interface Read() method. 63 func (r *Reader) Read(p []byte) (int, error) { 64 select { 65 case <-r.ctx.Done(): 66 return 0, io.EOF 67 default: 68 } 69 70 r.mutex.Lock() 71 i, err := r.reader.Read(p) 72 r.bRead += uint64(i) 73 r.mutex.Unlock() 74 75 return i, err 76 } 77 78 // ReadLine returns each line in the stream as a callback function 79 func (r *Reader) ReadLine(callback func([]byte)) error { 80 scanner := bufio.NewScanner(r) 81 for scanner.Scan() { 82 b := scanner.Bytes() 83 callback(append(b, utils.NewLineByte...)) 84 } 85 86 err := scanner.Err() 87 if err != nil { 88 return fmt.Errorf("error while reader.ReadLine: %s", err.Error()) 89 } 90 return nil 91 } 92 93 // ReadAll reads everything and dump it into one byte slice. 94 func (r *Reader) ReadAll() (b []byte, err error) { 95 w := NewStdinWithContext(r.ctx, r.forceClose) 96 97 _, err = w.ReadFrom(r.reader) 98 if err != nil { 99 return 100 } 101 102 b, err = w.ReadAll() 103 104 r.mutex.Lock() 105 r.bRead = uint64(len(b)) 106 r.mutex.Unlock() 107 108 return b, err 109 } 110 111 // ReadArray returns a data type-specific array returned via a callback function 112 func (r *Reader) ReadArray(ctx context.Context, callback func([]byte)) error { 113 return stdio.ReadArray(ctx, r, callback) 114 } 115 116 // ReadArrayWithType returns a data type-specific array returned via a callback function 117 func (r *Reader) ReadArrayWithType(ctx context.Context, callback func(interface{}, string)) error { 118 return stdio.ReadArrayWithType(ctx, r, callback) 119 } 120 121 // ReadMap returns a data type-specific key/values returned via a callback function 122 func (r *Reader) ReadMap(config *config.Config, callback func(*stdio.Map)) error { 123 return stdio.ReadMap(r, config, callback) 124 } 125 126 // Write is a dummy function because it's a reader interface 127 func (r *Reader) Write(p []byte) (int, error) { 128 return 0, errors.New("cannot write to a reader interface") 129 } 130 131 // Writeln is a dummy function because it's a reader interface 132 func (r *Reader) Writeln(b []byte) (int, error) { 133 return 0, errors.New("cannot write to a reader interface") 134 } 135 136 // WriteArray is a dummy function because it's a reader interface 137 func (r *Reader) WriteArray(dataType string) (stdio.ArrayWriter, error) { 138 return nil, errors.New("cannot write to a reader interface") 139 } 140 141 // Open the stream.Io interface for another dependant 142 func (r *Reader) Open() { 143 r.mutex.Lock() 144 r.dependents++ 145 r.mutex.Unlock() 146 } 147 148 // Close the stream.Io interface 149 func (r *Reader) Close() { 150 r.mutex.Lock() 151 defer r.mutex.Unlock() 152 153 r.dependents-- 154 155 if r.dependents < 0 { 156 panic("More closed dependents than open") 157 } 158 159 if r.dependents == 0 && r.readCloser != nil { 160 r.readCloser.Close() 161 } 162 } 163 164 // ForceClose forces the stream.Io interface to close. This should only be called by a STDIN reader 165 func (r *Reader) ForceClose() { 166 r.forceClose() 167 } 168 169 // WriteTo reads from the stream.Io interface and writes to a destination 170 // io.Writer interface 171 func (r *Reader) WriteTo(w io.Writer) (int64, error) { 172 return stdio.WriteTo(r, w) 173 } 174 175 // GetDataType returns the murex data type for the stream.Io interface 176 func (r *Reader) GetDataType() (dt string) { 177 for { 178 select { 179 case <-r.ctx.Done(): 180 return types.Generic 181 default: 182 } 183 184 r.dtLock.Lock() 185 dt = r.dataType 186 r.dtLock.Unlock() 187 if dt != "" { 188 return 189 } 190 } 191 } 192 193 // SetDataType defines the murex data type for the stream.Io interface 194 func (r *Reader) SetDataType(dt string) { 195 r.dtLock.Lock() 196 r.dataType = dt 197 r.dtLock.Unlock() 198 } 199 200 // DefaultDataType defines the murex data type for the stream.Io interface if 201 // it's not already set 202 func (r *Reader) DefaultDataType(err bool) { 203 r.dtLock.Lock() 204 dt := r.dataType 205 r.dtLock.Unlock() 206 207 if dt == "" { 208 if err { 209 r.dtLock.Lock() 210 r.dataType = types.Null 211 r.dtLock.Unlock() 212 } else { 213 r.dtLock.Lock() 214 r.dataType = types.Generic 215 r.dtLock.Unlock() 216 } 217 } 218 }