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  }