// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // data-cursors.go package expr import ( "errors" "io" ) type dataCursor struct { ds map[string]Functor ctx ExprContext index int resource any nextFunc Functor cleanFunc Functor resetFunc Functor currentFunc Functor } func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { dc = &dataCursor{ ds: ds, index: -1, ctx: ctx.Clone(), } return } func (dc *dataCursor) TypeName() string { return "DataCursor" } // func mapToString(m map[string]Functor) string { // var sb strings.Builder // sb.WriteByte('{') // for key, _ := range m { // if sb.Len() > 1 { // sb.WriteString(fmt.Sprintf(", %q: func(){}", key)) // } else { // sb.WriteString(fmt.Sprintf("%q: func(){}", key)) // } // } // sb.WriteByte('}') // return sb.String() // } func (dc *dataCursor) String() string { return "$()" /* var sb strings.Builder sb.WriteString(fmt.Sprintf(`$( index: %d, ds: %s, ctx: `, dc.index, mapToString(dc.ds))) CtxToBuilder(&sb, dc.ctx, 1) sb.WriteByte(')') return sb.String() */ } func (dc *dataCursor) HasOperation(name string) (exists bool) { exists = name == indexName if !exists { f, ok := dc.ds[name] exists = ok && isFunctor(f) } return } func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { if name == indexName { value = int64(dc.Index()) } else if functor, ok := dc.ds[name]; ok && isFunctor(functor) { if functor == dc.cleanFunc { value, err = dc.Clean() } else if functor == dc.resetFunc { value, err = dc.Reset() } else { ctx := cloneContext(dc.ctx) value, err = functor.Invoke(ctx, name, []any{}) exportObjects(dc.ctx, ctx) } } else { err = errNoOperation(name) } return } func (dc *dataCursor) Reset() (success bool, err error) { if dc.resetFunc != nil { if dc.resource != nil { ctx := cloneContext(dc.ctx) if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil { dc.index = -1 } exportObjects(dc.ctx, ctx) } else { err = errInvalidDataSource() } } else { err = errNoOperation(resetName) } success = err == nil return } func (dc *dataCursor) Clean() (success bool, err error) { if dc.cleanFunc != nil { if dc.resource != nil { ctx := cloneContext(dc.ctx) _, err = dc.cleanFunc.Invoke(ctx, cleanName, []any{dc.resource}) dc.resource = nil exportObjects(dc.ctx, ctx) } else { err = errInvalidDataSource() } } else { err = errors.New("no 'clean' function defined in the data-source") } success = err == nil return } func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item ctx := cloneContext(dc.ctx) if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil { err = io.EOF } exportObjects(dc.ctx, ctx) return } func (dc *dataCursor) _Next() (item any, err error) { // must return io.EOF after the last item if dc.resource != nil { ctx := cloneContext(dc.ctx) // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil { if item == nil { err = io.EOF } else { dc.index++ } } // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) exportObjects(dc.ctx, ctx) // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0)) } else { err = errInvalidDataSource() } return } const filterName = "filter" func (dc *dataCursor) filter(item any) (filterdItem any, err error) { if filter, ok := dc.ds[filterName]; ok { ctx := cloneContext(dc.ctx) filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index}); } else { filterdItem = item } return } func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item if dc.resource != nil { ctx := cloneContext(dc.ctx) // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) for item == nil && err == nil { if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil { if item == nil { err = io.EOF } else { dc.index++ item, err = dc.filter(item) } } } // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) exportObjects(dc.ctx, ctx) // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0)) } else { err = errInvalidDataSource() } return } func (dc *dataCursor) Index() int { return dc.index }