github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/array.go (about)

     1  package values
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"hash/fnv"
     7  	"sort"
     8  
     9  	"github.com/wI2L/jettison"
    10  
    11  	"github.com/MontFerret/ferret/pkg/runtime/core"
    12  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    13  )
    14  
    15  type (
    16  	ArrayPredicate = func(value core.Value, idx int) bool
    17  
    18  	ArraySorter = func(first, second core.Value) bool
    19  
    20  	Array struct {
    21  		items []core.Value
    22  	}
    23  )
    24  
    25  func EmptyArray() *Array {
    26  	return &Array{items: make([]core.Value, 0, 0)}
    27  }
    28  
    29  func NewArray(size int) *Array {
    30  	return &Array{items: make([]core.Value, 0, size)}
    31  }
    32  
    33  func NewArrayWith(values ...core.Value) *Array {
    34  	return &Array{items: values}
    35  }
    36  
    37  func NewArrayOf(values []core.Value) *Array {
    38  	return &Array{items: values}
    39  }
    40  
    41  func (t *Array) MarshalJSON() ([]byte, error) {
    42  	return jettison.MarshalOpts(t.items, jettison.NoHTMLEscaping())
    43  }
    44  
    45  func (t *Array) Type() core.Type {
    46  	return types.Array
    47  }
    48  
    49  func (t *Array) String() string {
    50  	marshaled, err := t.MarshalJSON()
    51  
    52  	if err != nil {
    53  		return "[]"
    54  	}
    55  
    56  	return string(marshaled)
    57  }
    58  
    59  func (t *Array) Compare(other core.Value) int64 {
    60  	if other.Type() == types.Array {
    61  		other := other.(*Array)
    62  
    63  		if t.Length() == 0 && other.Length() == 0 {
    64  			return 0
    65  		}
    66  
    67  		if t.Length() < other.Length() {
    68  			return -1
    69  		}
    70  
    71  		if t.Length() > other.Length() {
    72  			return 1
    73  		}
    74  
    75  		var res int64
    76  		var val core.Value
    77  
    78  		other.ForEach(func(otherVal core.Value, idx int) bool {
    79  			val = t.Get(NewInt(idx))
    80  			res = val.Compare(otherVal)
    81  
    82  			return res == 0
    83  		})
    84  
    85  		return res
    86  	}
    87  
    88  	return types.Compare(types.Array, other.Type())
    89  }
    90  
    91  func (t *Array) Unwrap() interface{} {
    92  	arr := make([]interface{}, t.Length())
    93  
    94  	for idx, val := range t.items {
    95  		arr[idx] = val.Unwrap()
    96  	}
    97  
    98  	return arr
    99  }
   100  
   101  func (t *Array) Hash() uint64 {
   102  	h := fnv.New64a()
   103  
   104  	h.Write([]byte(t.Type().String()))
   105  	h.Write([]byte(":"))
   106  	h.Write([]byte("["))
   107  
   108  	endIndex := len(t.items) - 1
   109  
   110  	for i, el := range t.items {
   111  		bytes := make([]byte, 8)
   112  		binary.LittleEndian.PutUint64(bytes, el.Hash())
   113  
   114  		h.Write(bytes)
   115  
   116  		if i != endIndex {
   117  			h.Write([]byte(","))
   118  		}
   119  	}
   120  
   121  	h.Write([]byte("]"))
   122  
   123  	return h.Sum64()
   124  }
   125  
   126  func (t *Array) Copy() core.Value {
   127  	c := NewArray(len(t.items))
   128  
   129  	for _, el := range t.items {
   130  		c.Push(el)
   131  	}
   132  
   133  	return c
   134  }
   135  
   136  func (t *Array) Length() Int {
   137  	return Int(len(t.items))
   138  }
   139  
   140  func (t *Array) ForEach(predicate ArrayPredicate) {
   141  	for idx, val := range t.items {
   142  		if !predicate(val, idx) {
   143  			break
   144  		}
   145  	}
   146  }
   147  
   148  func (t *Array) First() core.Value {
   149  	if len(t.items) > 0 {
   150  		return t.items[0]
   151  	}
   152  
   153  	return None
   154  }
   155  
   156  func (t *Array) Last() core.Value {
   157  	size := len(t.items)
   158  
   159  	if size > 1 {
   160  		return t.items[size-1]
   161  	} else if size == 1 {
   162  		return t.items[0]
   163  	}
   164  
   165  	return None
   166  }
   167  
   168  func (t *Array) Find(predicate ArrayPredicate) (*Array, Boolean) {
   169  	result := NewArray(len(t.items))
   170  
   171  	for idx, val := range t.items {
   172  		if predicate(val, idx) {
   173  			result.Push(val)
   174  		}
   175  	}
   176  
   177  	return result, result.Length() > 0
   178  }
   179  
   180  func (t *Array) FindOne(predicate ArrayPredicate) (core.Value, Boolean) {
   181  	for idx, val := range t.items {
   182  		if predicate(val, idx) {
   183  			return val, True
   184  		}
   185  	}
   186  
   187  	return None, False
   188  }
   189  
   190  func (t *Array) Get(idx Int) core.Value {
   191  	l := len(t.items) - 1
   192  
   193  	if l < 0 {
   194  		return None
   195  	}
   196  
   197  	if int(idx) > l {
   198  		return None
   199  	}
   200  
   201  	return t.items[idx]
   202  }
   203  
   204  func (t *Array) Set(idx Int, value core.Value) error {
   205  	last := len(t.items) - 1
   206  
   207  	if last >= int(idx) {
   208  		t.items[idx] = value
   209  
   210  		return nil
   211  	}
   212  
   213  	return core.Error(core.ErrInvalidOperation, "out of bounds")
   214  }
   215  
   216  func (t *Array) Push(item core.Value) {
   217  	t.items = append(t.items, item)
   218  }
   219  
   220  func (t *Array) Slice(from, to Int) *Array {
   221  	length := t.Length()
   222  
   223  	if from >= length {
   224  		return NewArray(0)
   225  	}
   226  
   227  	if to > length {
   228  		to = length
   229  	}
   230  
   231  	result := new(Array)
   232  	result.items = t.items[from:to]
   233  
   234  	return result
   235  }
   236  
   237  func (t *Array) IndexOf(item core.Value) Int {
   238  	res := Int(-1)
   239  
   240  	for idx, el := range t.items {
   241  		if el.Compare(item) == 0 {
   242  			res = Int(idx)
   243  			break
   244  		}
   245  	}
   246  
   247  	return res
   248  }
   249  
   250  func (t *Array) Insert(idx Int, value core.Value) {
   251  	t.items = append(t.items[:idx], append([]core.Value{value}, t.items[idx:]...)...)
   252  }
   253  
   254  func (t *Array) RemoveAt(idx Int) {
   255  	i := int(idx)
   256  	max := len(t.items) - 1
   257  
   258  	if i > max {
   259  		return
   260  	}
   261  
   262  	t.items = append(t.items[:i], t.items[i+1:]...)
   263  }
   264  
   265  func (t *Array) Clone() core.Cloneable {
   266  	cloned := NewArray(0)
   267  
   268  	var value core.Value
   269  	for idx := NewInt(0); idx < t.Length(); idx++ {
   270  		value = t.Get(idx)
   271  
   272  		cloneable, ok := value.(core.Cloneable)
   273  
   274  		if ok {
   275  			value = cloneable.Clone()
   276  		}
   277  
   278  		cloned.Push(value)
   279  	}
   280  
   281  	return cloned
   282  }
   283  
   284  func (t *Array) Sort() *Array {
   285  	return t.SortWith(func(first, second core.Value) bool {
   286  		return first.Compare(second) == -1
   287  	})
   288  }
   289  
   290  func (t *Array) SortWith(sorter ArraySorter) *Array {
   291  	c := make([]core.Value, len(t.items))
   292  	copy(c, t.items)
   293  
   294  	sort.SliceStable(c, func(i, j int) bool {
   295  		return sorter(c[i], c[j])
   296  	})
   297  
   298  	res := new(Array)
   299  	res.items = c
   300  
   301  	return res
   302  }
   303  
   304  func (t *Array) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
   305  	if len(path) == 0 {
   306  		return None, nil
   307  	}
   308  
   309  	segmentIdx := 0
   310  
   311  	if typ := path[segmentIdx].Type(); typ != types.Int {
   312  		return None, core.NewPathError(core.TypeError(typ, types.Int), segmentIdx)
   313  	}
   314  
   315  	first := t.Get(path[segmentIdx].(Int))
   316  
   317  	if len(path) == 1 {
   318  		return first, nil
   319  	}
   320  
   321  	segmentIdx++
   322  
   323  	if first == None || first == nil {
   324  		return None, core.NewPathError(core.ErrInvalidPath, segmentIdx)
   325  	}
   326  
   327  	getter, ok := first.(core.Getter)
   328  
   329  	if !ok {
   330  		return GetIn(ctx, first, path[segmentIdx:])
   331  	}
   332  
   333  	return getter.GetIn(ctx, path[segmentIdx:])
   334  }