builtin-os-file: add function fileReadIterator() that creates an iterator over lines of a text file
This commit is contained in:
parent
0677180456
commit
b6b09b2fb1
149
builtin-os-file-iter.go
Normal file
149
builtin-os-file-iter.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// builtin-os-file.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
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@<nil>)", 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{NextName, ResetName, IndexName, CountName, CurrentName, 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 NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case ResetName:
|
||||||
|
err = it.Reset()
|
||||||
|
case CleanName:
|
||||||
|
err = it.Clean()
|
||||||
|
case IndexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case CountName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = errNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileReadIteratorFunc(ctx 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{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")
|
||||||
|
// }
|
||||||
@ -250,6 +250,10 @@ func ImportOsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||||
NewFuncParam(ParamHandle),
|
NewFuncParam(ParamHandle),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
||||||
|
NewFuncParam(paramHandleOrPath),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const (
|
|||||||
TypeFileHandle = "file-handle"
|
TypeFileHandle = "file-handle"
|
||||||
TypeInt = "integer"
|
TypeInt = "integer"
|
||||||
TypeItem = "item"
|
TypeItem = "item"
|
||||||
|
TypeIterator = "iterator"
|
||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
TypePair = "pair"
|
TypePair = "pair"
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
|
|||||||
@ -21,10 +21,11 @@ func TestFuncRun(t *testing.T) {
|
|||||||
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
|
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
|
||||||
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
|
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
|
||||||
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
|
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
|
||||||
|
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileReadIterator("test-file.txt"); run(it)`, nil, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
//t.Setenv("EXPR_PATH", ".")
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 1)
|
runTestSuiteSpec(t, section, inputs, 10)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,10 +27,13 @@ func TestFuncOs(t *testing.T) {
|
|||||||
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
|
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
|
||||||
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
||||||
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
||||||
|
/* 16 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++`, "uno", nil},
|
||||||
|
/* 17 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()},
|
||||||
|
/* 18 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it.clean`, nil, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 2)
|
runTestSuiteSpec(t, section, inputs, 18)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user