// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // func-os.go package expr import ( "bufio" "fmt" "io" "os" ) type osHandle interface { getFile() *os.File } type osWriter struct { fh *os.File writer *bufio.Writer } func (h *osWriter) TypeName() string { return "osWriter" } func (h *osWriter) String() string { return "writer" } func (h *osWriter) getFile() *os.File { return h.fh } type osReader struct { fh *os.File reader *bufio.Reader } func (h *osReader) TypeName() string { return "osReader" } func (h *osReader) String() string { return "reader" } func (h *osReader) getFile() *os.File { return h.fh } func errMissingFilePath(funcName string) error { return fmt.Errorf("%s(): missing or invalid file path", funcName) } func errInvalidFileHandle(funcName string, v any) error { if v != nil { return fmt.Errorf("%s(): invalid file handle %v [%T]", funcName, v, v) } else { return fmt.Errorf("%s(): invalid file handle", funcName) } } func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { if filePath, ok := args[0].(string); ok && len(filePath) > 0 { var fh *os.File if fh, err = os.Create(filePath); err == nil { result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} } } else { err = errMissingFilePath("createFile") } return } func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { if filePath, ok := args[0].(string); ok && len(filePath) > 0 { var fh *os.File if fh, err = os.Open(filePath); err == nil { result = &osReader{fh: fh, reader: bufio.NewReader(fh)} } } else { err = errMissingFilePath("openFile") } return } func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { if filePath, ok := args[0].(string); ok && len(filePath) > 0 { var fh *os.File if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil { result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} } } else { err = errMissingFilePath("openFile") } return } func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { var handle osHandle var invalidFileHandle any var ok bool if handle, ok = args[0].(osHandle); !ok { invalidFileHandle = args[0] } if handle != nil { if fh := handle.getFile(); fh != nil { if w, ok := handle.(*osWriter); ok { err = w.writer.Flush() } if err == nil { err = fh.Close() } } } if err == nil && (handle == nil || invalidFileHandle != nil) { err = errInvalidFileHandle("closeFileFunc", handle) } result = err == nil return } func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { var handle osHandle var invalidFileHandle any var ok bool if handle, ok = args[0].(osHandle); !ok { invalidFileHandle = args[0] } if handle != nil { if w, ok := handle.(*osWriter); ok { result, err = fmt.Fprint(w.writer, args[1:]...) } else { invalidFileHandle = handle } } if err == nil && (handle == nil || invalidFileHandle != nil) { err = errInvalidFileHandle("writeFileFunc", invalidFileHandle) } return } func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { var handle osHandle var invalidFileHandle any var ok bool result = nil if handle, ok = args[0].(osHandle); !ok || args[0] == nil { invalidFileHandle = args[0] } if handle != nil { if r, ok := handle.(*osReader); ok { var limit byte = '\n' var v string if s, ok := args[1].(string); ok && len(s) > 0 { limit = s[0] } if v, err = r.reader.ReadString(limit); err == nil { if len(v) > 0 && v[len(v)-1] == limit { result = v[0 : len(v)-1] } else { result = v } } if err == io.EOF { err = nil } } else { invalidFileHandle = handle } } if err == nil && (handle == nil || invalidFileHandle != nil) { err = errInvalidFileHandle("readFileFunc", invalidFileHandle) } return } func ImportOsFuncs(ctx ExprContext) { ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(paramFilepath), }) ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(paramFilepath), }) ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{ newFuncParam(paramFilepath), }) ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{ newFuncParam(typeHandle), newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""), }) ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{ newFuncParam(typeHandle), newFuncParamFlagDef("limitCh", pfOptional, "\n"), }) ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{ newFuncParam(typeHandle), }) } func init() { registerImport("os.file", ImportOsFuncs, "Operating system file functions") }