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

     1  package values
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"hash/fnv"
     8  	"reflect"
     9  	"sort"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/wI2L/jettison"
    14  
    15  	"github.com/MontFerret/ferret/pkg/runtime/core"
    16  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    17  )
    18  
    19  // GetIn checks that from implements core.Getter interface. If it implements,
    20  // GetIn call from.GetIn method, otherwise iterates over values and tries to resolve a given path.
    21  func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, core.PathError) {
    22  	if len(byPath) == 0 {
    23  		return None, nil
    24  	}
    25  
    26  	var result = from
    27  
    28  	for i, segment := range byPath {
    29  		if result == None || result == nil {
    30  			break
    31  		}
    32  
    33  		segType := segment.Type()
    34  
    35  		switch curVal := result.(type) {
    36  		case *Object:
    37  			result, _ = curVal.Get(ToString(segment))
    38  		case *Array:
    39  			if segType != types.Int {
    40  				return nil, core.NewPathError(
    41  					core.TypeError(segType, types.Int),
    42  					i,
    43  				)
    44  			}
    45  
    46  			result = curVal.Get(segment.(Int))
    47  		case String:
    48  			if segType != types.Int {
    49  				return nil, core.NewPathError(
    50  					core.TypeError(segType, types.Int),
    51  					i,
    52  				)
    53  			}
    54  
    55  			result = curVal.At(ToInt(segment))
    56  		case core.Getter:
    57  			return curVal.GetIn(ctx, byPath[i:])
    58  		default:
    59  			return None, core.NewPathError(core.ErrInvalidPath, i)
    60  		}
    61  	}
    62  
    63  	return result, nil
    64  }
    65  
    66  func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) core.PathError {
    67  	if len(byPath) == 0 {
    68  		return nil
    69  	}
    70  
    71  	var parent core.Value
    72  	var current = to
    73  	target := len(byPath) - 1
    74  
    75  	for idx, segment := range byPath {
    76  		parent = current
    77  		isTarget := target == idx
    78  		segmentType := segment.Type()
    79  
    80  		switch parVal := parent.(type) {
    81  		case *Object:
    82  			if segmentType != types.String {
    83  				return core.NewPathError(
    84  					core.TypeError(segmentType, types.String),
    85  					idx,
    86  				)
    87  			}
    88  
    89  			if !isTarget {
    90  				current, _ = parVal.Get(segment.(String))
    91  			} else {
    92  				parVal.Set(segment.(String), value)
    93  			}
    94  		case *Array:
    95  			if segmentType != types.Int {
    96  				return core.NewPathError(
    97  					core.TypeError(segmentType, types.Int),
    98  					idx,
    99  				)
   100  			}
   101  
   102  			if !isTarget {
   103  				current = parVal.Get(segment.(Int))
   104  			} else {
   105  				if err := parVal.Set(segment.(Int), value); err != nil {
   106  					return core.NewPathError(err, idx)
   107  				}
   108  			}
   109  		case core.Setter:
   110  			return parVal.SetIn(ctx, byPath[idx:], value)
   111  		default:
   112  			// redefine parent
   113  			isArray := segmentType.Equals(types.Int)
   114  
   115  			// it's not an index
   116  			if !isArray {
   117  				obj := NewObject()
   118  				parent = obj
   119  
   120  				if segmentType != types.String {
   121  					return core.NewPathError(core.TypeError(segmentType, types.String), idx)
   122  				}
   123  
   124  				if isTarget {
   125  					obj.Set(segment.(String), value)
   126  				}
   127  			} else {
   128  				arr := NewArray(10)
   129  				parent = arr
   130  
   131  				if isTarget {
   132  					if err := arr.Set(segment.(Int), value); err != nil {
   133  						return core.NewPathError(err, idx)
   134  					}
   135  				}
   136  			}
   137  
   138  			// set new parent
   139  			nextPath := byPath
   140  
   141  			if idx > 0 {
   142  				nextPath = byPath[0 : idx-1]
   143  			} else {
   144  				nextPath = byPath[0:]
   145  			}
   146  
   147  			if err := SetIn(ctx, to, nextPath, parent); err != nil {
   148  				return err
   149  			}
   150  
   151  			if !isTarget {
   152  				current = None
   153  			}
   154  		}
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func ReturnOrNext(ctx context.Context, path []core.Value, idx int, out core.Value, err error) (core.Value, core.PathError) {
   161  	if err != nil {
   162  		pathErr, ok := err.(core.PathError)
   163  
   164  		if ok {
   165  			return None, core.NewPathErrorFrom(pathErr, idx)
   166  		}
   167  
   168  		return None, core.NewPathError(err, idx)
   169  	}
   170  
   171  	if len(path) > (idx + 1) {
   172  		out, pathErr := GetIn(ctx, out, path[idx+1:])
   173  
   174  		if pathErr != nil {
   175  			return None, core.NewPathErrorFrom(pathErr, idx)
   176  		}
   177  
   178  		return out, nil
   179  	}
   180  
   181  	return out, nil
   182  }
   183  
   184  func Parse(input interface{}) core.Value {
   185  	switch value := input.(type) {
   186  	case bool:
   187  		return NewBoolean(value)
   188  	case string:
   189  		return NewString(value)
   190  	case int64:
   191  		return NewInt(int(value))
   192  	case int32:
   193  		return NewInt(int(value))
   194  	case int16:
   195  		return NewInt(int(value))
   196  	case int8:
   197  		return NewInt(int(value))
   198  	case int:
   199  		return NewInt(value)
   200  	case float64:
   201  		return NewFloat(value)
   202  	case float32:
   203  		return NewFloat(float64(value))
   204  	case time.Time:
   205  		return NewDateTime(value)
   206  	case []interface{}:
   207  		arr := NewArray(len(value))
   208  
   209  		for _, el := range value {
   210  			arr.Push(Parse(el))
   211  		}
   212  
   213  		return arr
   214  	case map[string]interface{}:
   215  		obj := NewObject()
   216  
   217  		for key, el := range value {
   218  			obj.Set(NewString(key), Parse(el))
   219  		}
   220  
   221  		return obj
   222  	case []byte:
   223  		return NewBinary(value)
   224  	case nil:
   225  		return None
   226  	default:
   227  		v := reflect.ValueOf(value)
   228  		t := reflect.TypeOf(value)
   229  		kind := t.Kind()
   230  
   231  		if kind == reflect.Ptr {
   232  			el := v.Elem()
   233  
   234  			if el.Kind() == 0 {
   235  				return None
   236  			}
   237  
   238  			return Parse(el.Interface())
   239  		}
   240  
   241  		if kind == reflect.Slice || kind == reflect.Array {
   242  			size := v.Len()
   243  			arr := NewArray(size)
   244  
   245  			for i := 0; i < size; i++ {
   246  				curVal := v.Index(i)
   247  				arr.Push(Parse(curVal.Interface()))
   248  			}
   249  
   250  			return arr
   251  		}
   252  
   253  		if kind == reflect.Map {
   254  			keys := v.MapKeys()
   255  			obj := NewObject()
   256  
   257  			for _, k := range keys {
   258  				key := Parse(k.Interface())
   259  				curVal := v.MapIndex(k)
   260  
   261  				obj.Set(NewString(key.String()), Parse(curVal.Interface()))
   262  			}
   263  
   264  			return obj
   265  		}
   266  
   267  		if kind == reflect.Struct {
   268  			obj := NewObject()
   269  			size := t.NumField()
   270  
   271  			for i := 0; i < size; i++ {
   272  				field := t.Field(i)
   273  				fieldValue := v.Field(i)
   274  
   275  				obj.Set(NewString(field.Name), Parse(fieldValue.Interface()))
   276  			}
   277  
   278  			return obj
   279  		}
   280  
   281  		return None
   282  	}
   283  }
   284  
   285  func Unmarshal(value json.RawMessage) (core.Value, error) {
   286  	var o interface{}
   287  
   288  	err := json.Unmarshal(value, &o)
   289  
   290  	if err != nil {
   291  		return None, err
   292  	}
   293  
   294  	return Parse(o), nil
   295  }
   296  
   297  func MustMarshal(value core.Value) json.RawMessage {
   298  	out, err := value.MarshalJSON()
   299  
   300  	if err != nil {
   301  		panic(err)
   302  	}
   303  
   304  	return out
   305  }
   306  
   307  func MustMarshalAny(input interface{}) json.RawMessage {
   308  	out, err := jettison.MarshalOpts(input, jettison.NoHTMLEscaping())
   309  
   310  	if err != nil {
   311  		panic(err)
   312  	}
   313  
   314  	return out
   315  }
   316  
   317  func ToBoolean(input core.Value) Boolean {
   318  	switch input.Type() {
   319  	case types.Boolean:
   320  		return input.(Boolean)
   321  	case types.String:
   322  		return NewBoolean(input.(String) != "")
   323  	case types.Int:
   324  		return NewBoolean(input.(Int) != 0)
   325  	case types.Float:
   326  		return NewBoolean(input.(Float) != 0)
   327  	case types.DateTime:
   328  		return NewBoolean(!input.(DateTime).IsZero())
   329  	case types.None:
   330  		return False
   331  	default:
   332  		return True
   333  	}
   334  }
   335  
   336  func ToFloat(input core.Value) Float {
   337  	switch val := input.(type) {
   338  	case Float:
   339  		return val
   340  	case Int:
   341  		return Float(val)
   342  	case String:
   343  		i, err := strconv.ParseFloat(string(val), 64)
   344  
   345  		if err != nil {
   346  			return ZeroFloat
   347  		}
   348  
   349  		return Float(i)
   350  	case Boolean:
   351  		if val {
   352  			return Float(1)
   353  		}
   354  
   355  		return Float(0)
   356  	case DateTime:
   357  		dt := input.(DateTime)
   358  
   359  		if dt.IsZero() {
   360  			return ZeroFloat
   361  		}
   362  
   363  		return NewFloat(float64(dt.Unix()))
   364  	case *Array:
   365  		length := val.Length()
   366  
   367  		if length == 0 {
   368  			return ZeroFloat
   369  		}
   370  
   371  		res := ZeroFloat
   372  
   373  		for i := Int(0); i < length; i++ {
   374  			res += ToFloat(val.Get(i))
   375  		}
   376  
   377  		return res
   378  	default:
   379  		return ZeroFloat
   380  	}
   381  }
   382  
   383  func ToString(input core.Value) String {
   384  	switch val := input.(type) {
   385  	case String:
   386  		return val
   387  	default:
   388  		return NewString(val.String())
   389  	}
   390  }
   391  
   392  func ToInt(input core.Value) Int {
   393  	switch val := input.(type) {
   394  	case Int:
   395  		return val
   396  	case Float:
   397  		return Int(val)
   398  	case String:
   399  		i, err := strconv.ParseInt(string(val), 10, 64)
   400  
   401  		if err != nil {
   402  			return ZeroInt
   403  		}
   404  
   405  		return Int(i)
   406  	case Boolean:
   407  		if val {
   408  			return Int(1)
   409  		}
   410  
   411  		return Int(0)
   412  	case DateTime:
   413  		dt := input.(DateTime)
   414  
   415  		if dt.IsZero() {
   416  			return ZeroInt
   417  		}
   418  
   419  		return NewInt(int(dt.Unix()))
   420  	case *Array:
   421  		length := val.Length()
   422  
   423  		if length == 0 {
   424  			return ZeroInt
   425  		}
   426  
   427  		res := ZeroInt
   428  
   429  		for i := Int(0); i < length; i++ {
   430  			res += ToInt(val.Get(i))
   431  		}
   432  
   433  		return res
   434  	default:
   435  		return ZeroInt
   436  	}
   437  }
   438  
   439  func ToIntDefault(input core.Value, defaultValue Int) Int {
   440  	if result := ToInt(input); result > 0 {
   441  		return result
   442  	}
   443  
   444  	return defaultValue
   445  }
   446  
   447  func ToArray(ctx context.Context, input core.Value) *Array {
   448  	switch value := input.(type) {
   449  	case Boolean,
   450  		Int,
   451  		Float,
   452  		String,
   453  		DateTime:
   454  
   455  		return NewArrayWith(value)
   456  	case *Array:
   457  		return value.Copy().(*Array)
   458  	case *Object:
   459  		arr := NewArray(int(value.Length()))
   460  
   461  		value.ForEach(func(value core.Value, key string) bool {
   462  			arr.Push(value)
   463  
   464  			return true
   465  		})
   466  
   467  		return arr
   468  	case core.Iterable:
   469  		iterator, err := value.Iterate(ctx)
   470  
   471  		if err != nil {
   472  			return NewArray(0)
   473  		}
   474  
   475  		arr := NewArray(10)
   476  
   477  		for {
   478  			val, _, err := iterator.Next(ctx)
   479  
   480  			if err != nil {
   481  				return NewArray(0)
   482  			}
   483  
   484  			if val == None {
   485  				break
   486  			}
   487  
   488  			arr.Push(val)
   489  		}
   490  
   491  		return arr
   492  	default:
   493  		return EmptyArray()
   494  	}
   495  }
   496  
   497  func ToObject(ctx context.Context, input core.Value) *Object {
   498  	switch value := input.(type) {
   499  	case *Object:
   500  		return value
   501  	case *Array:
   502  		obj := NewObject()
   503  
   504  		value.ForEach(func(value core.Value, idx int) bool {
   505  			obj.Set(ToString(Int(idx)), value)
   506  
   507  			return true
   508  		})
   509  
   510  		return obj
   511  	case core.Iterable:
   512  		iterator, err := value.Iterate(ctx)
   513  
   514  		if err != nil {
   515  			return NewObject()
   516  		}
   517  
   518  		obj := NewObject()
   519  
   520  		for {
   521  			val, key, err := iterator.Next(ctx)
   522  
   523  			if err != nil {
   524  				return obj
   525  			}
   526  
   527  			if val == None {
   528  				break
   529  			}
   530  
   531  			obj.Set(String(key.String()), val)
   532  		}
   533  
   534  		return obj
   535  	default:
   536  		return NewObject()
   537  	}
   538  }
   539  
   540  func ToStrings(input *Array) []String {
   541  	res := make([]String, input.Length())
   542  
   543  	input.ForEach(func(v core.Value, i int) bool {
   544  		res[i] = NewString(v.String())
   545  
   546  		return true
   547  	})
   548  
   549  	return res
   550  }
   551  
   552  func Hash(rtType core.Type, content []byte) uint64 {
   553  	h := fnv.New64a()
   554  
   555  	h.Write([]byte(rtType.String()))
   556  	h.Write([]byte(":"))
   557  	h.Write(content)
   558  
   559  	return h.Sum64()
   560  }
   561  
   562  func MapHash(input map[string]core.Value) uint64 {
   563  	h := fnv.New64a()
   564  
   565  	keys := make([]string, 0, len(input))
   566  
   567  	for key := range input {
   568  		keys = append(keys, key)
   569  	}
   570  
   571  	// order does not really matter
   572  	// but it will give us a consistent hash sum
   573  	sort.Strings(keys)
   574  	endIndex := len(keys) - 1
   575  
   576  	h.Write([]byte("{"))
   577  
   578  	for idx, key := range keys {
   579  		h.Write([]byte(key))
   580  		h.Write([]byte(":"))
   581  
   582  		el := input[key]
   583  
   584  		bytes := make([]byte, 8)
   585  		binary.LittleEndian.PutUint64(bytes, el.Hash())
   586  
   587  		h.Write(bytes)
   588  
   589  		if idx != endIndex {
   590  			h.Write([]byte(","))
   591  		}
   592  	}
   593  
   594  	h.Write([]byte("}"))
   595  
   596  	return h.Sum64()
   597  }
   598  
   599  func IsNumber(input core.Value) Boolean {
   600  	t := input.Type()
   601  
   602  	return t == types.Int || t == types.Float
   603  }
   604  
   605  func UnwrapStrings(values []String) []string {
   606  	out := make([]string, len(values))
   607  
   608  	for i, v := range values {
   609  		out[i] = v.String()
   610  	}
   611  
   612  	return out
   613  }