// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // builtin-os-file.go package expr import ( "fmt" "io" "slices" "git.portale-stac.it/go-pkg/expr/kern" ) const paramHandleOrPath = "handle-or-path" const fileReadTextIteratorType = "fileReadTextIterator" type fileReadTextIterator struct { osReader *osReader index int count int line string autoClose bool } func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator { return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose} } func (it *fileReadTextIterator) TypeName() string { return fileReadTextIteratorType } func (it *fileReadTextIterator) String() string { if it.osReader != nil && it.osReader.fh != nil { return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name()) } return fmt.Sprintf("$(%s@)", fileReadTextIteratorType) } func (it *fileReadTextIterator) Count() int { return it.count } func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item if it.osReader.fh != nil { if it.line, err = it.osReader.reader.ReadString('\n'); err == nil { it.index++ it.count++ item = it.line[0 : len(it.line)-1] } else if it.autoClose { it.Clean() } } return } func (it *fileReadTextIterator) Current() (item any, err error) { if len(it.line) > 0 { item = it.line[0 : len(it.line)-1] } return } func (it *fileReadTextIterator) Index() int { return it.index } func (it *fileReadTextIterator) Reset() (err error) { if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil { it.index = -1 it.count = 0 it.line = "" } return } func (it *fileReadTextIterator) HasOperation(name string) bool { return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name) } func (it *fileReadTextIterator) Clean() (err error) { if it.osReader.fh != nil { if err = it.osReader.fh.Close(); err == nil { it.osReader = nil } } return nil } func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) { switch name { case kern.NextName: v, err = it.Next() case kern.ResetName: err = it.Reset() case kern.CleanName: err = it.Clean() case kern.IndexName: v = int64(it.Index()) case kern.CurrentName: v, err = it.Current() case kern.CountName: v = it.count default: err = kern.ErrNoOperation(name) } return } func fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { var handle *osReader var invalidFileHandle any var ok, autoClose bool result = nil if handle, ok = args[paramHandleOrPath].(*osReader); !ok { if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 { var handleAny any if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil { return } if handleAny != nil { handle = handleAny.(*osReader) autoClose = true } } else { invalidFileHandle = args[paramHandleOrPath] } } if handle != nil { result = newReadTextIterator(handle, autoClose) } if err == nil && (handle == nil || invalidFileHandle != nil) { err = errInvalidFileHandle(name, invalidFileHandle) } return } // func ImportOsIterFuncs(ctx ExprContext) { // ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{ // NewFuncParam(paramHandleOrPath), // }) // } // func init() { // RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions") // }