ucl/cmdlang/builtins.go

113 lines
2.0 KiB
Go

package cmdlang
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"strings"
)
func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if len(args.args) == 0 {
return asStream(""), nil
}
var line strings.Builder
for _, arg := range args.args {
if s, ok := arg.(fmt.Stringer); ok {
line.WriteString(s.String())
}
}
return asStream(line.String()), nil
}
func toUpperBuiltin(ctx context.Context, args invocationArgs) (object, error) {
// Handle args
return mapFilterStream{
in: args.inStream,
mapFn: func(x object) (object, bool) {
s, ok := x.(string)
if !ok {
return nil, false
}
return strings.ToUpper(s), true
},
}, nil
}
func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
filename, err := args.stringArg(0)
if err != nil {
return nil, err
}
return &fileLinesStream{filename: filename}, nil
}
type fileLinesStream struct {
filename string
f *os.File
scnr *bufio.Scanner
}
func (f *fileLinesStream) next() (object, error) {
var err error
// We open the file on the first pull. That way, an unconsumed stream won't result in a FD leak
if f.f == nil {
f.f, err = os.Open(f.filename)
if err != nil {
return nil, err
}
f.scnr = bufio.NewScanner(f.f)
}
if f.scnr.Scan() {
return f.scnr.Text(), nil
}
if f.scnr.Err() == nil {
return nil, io.EOF
}
return nil, f.scnr.Err()
}
func (f *fileLinesStream) close() error {
if f.f != nil {
return f.f.Close()
}
return nil
}
func errorTestBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return &timeBombStream{args.inStream, 2}, nil
}
type timeBombStream struct {
in stream
x int
}
func (ms *timeBombStream) next() (object, error) {
if ms.x > 0 {
ms.x--
return ms.in.next()
}
return nil, errors.New("BOOM")
}
func (ms *timeBombStream) close() error {
closable, ok := ms.in.(closableStream)
if ok {
return closable.close()
}
return nil
}