diff --git a/int-iterator.go b/int-iterator.go new file mode 100644 index 0000000..ef29f0e --- /dev/null +++ b/int-iterator.go @@ -0,0 +1,137 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// int-iterator.go +package expr + +import ( + "fmt" + "io" + "slices" + + "git.portale-stac.it/go-pkg/expr/kern" +) + +type IntIterator struct { + count int64 + index int64 + start int64 + stop int64 + step int64 +} + +func NewIntIterator(args []any) (it *IntIterator, err error) { + var argc int = 0 + if args != nil { + argc = len(args) + } + it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1} + if argc >= 1 { + if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil { + return + } + if argc >= 2 { + it.start = it.stop + if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil { + return + } + if argc >= 3 { + if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil { + return + } + } else if it.start > it.stop { + it.step = -1 + } + } + } + if it.step == 0 { + err = fmt.Errorf("step cannot be zero") + return + } + if it.start < it.stop && it.step < 0 { + err = fmt.Errorf("step cannot be negative when start < stop") + return + } + if it.start > it.stop && it.step > 0 { + err = fmt.Errorf("step cannot be positive when start > stop") + return + } + it.Reset() + return +} + +func (it *IntIterator) String() string { + return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step) +} + +func (it *IntIterator) TypeName() string { + return "IntIterator" +} + +func (it *IntIterator) HasOperation(name string) bool { + yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name) + return yes +} + +func (it *IntIterator) 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 (it *IntIterator) Current() (item any, err error) { + if it.start <= it.stop { + if it.index >= it.start && it.index < it.stop { + item = it.index + } else { + err = io.EOF + } + } else { + if it.index > it.stop && it.index <= it.start { + item = it.index + } else { + err = io.EOF + } + } + return +} + +func (it *IntIterator) Next() (item any, err error) { + it.index += it.step + if item, err = it.Current(); err != io.EOF { + it.count++ + } + return +} + +func (it *IntIterator) Index() int64 { + return it.index +} + +func (it *IntIterator) Count() int64 { + return it.count +} + +func (it *IntIterator) Reset() error { + it.index = it.start - it.step + it.count = 0 + return nil +} + +func (it *IntIterator) Clean() error { + return nil +} diff --git a/t_utils_test.go b/t_utils_test.go index 3444103..79c168c 100644 --- a/t_utils_test.go +++ b/t_utils_test.go @@ -122,6 +122,32 @@ func TestToIntErr(t *testing.T) { } } +func TestToInt64Ok(t *testing.T) { + source := int64(64) + wantValue := int64(64) + wantErr := error(nil) + + gotValue, gotErr := kern.ToGoInt64(source, "test") + + if gotErr != nil || gotValue != wantValue { + t.Errorf("toInt64(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v", + source, gotValue, gotErr, wantValue, wantErr) + } +} + +func TestToInt64Err(t *testing.T) { + source := uint64(64) + wantValue := int64(0) + wantErr := errors.New(`test expected integer, got uint64 (64)`) + + gotValue, gotErr := kern.ToGoInt64(source, "test") + + if gotErr.Error() != wantErr.Error() || gotValue != wantValue { + t.Errorf("toInt64(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v", + source, gotValue, gotErr, wantValue, wantErr) + } +} + func TestAnyInteger(t *testing.T) { type inputType struct { source any