go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/types/types.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package types
     5  
     6  import (
     7  	"encoding/json"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // Type information
    13  type Type string
    14  
    15  // MarshalJSON generates escapes the \u0000 string for postgres
    16  // Otherwise we are not able to store the compile code as json blob in pg since
    17  // llx and type use \x00 or \u0000. This is not allowed in Postgres json blobs
    18  // see https://www.postgresql.org/docs/9.4/release-9-4-1.html
    19  func (typ Type) MarshalJSON() ([]byte, error) {
    20  	newVal := strings.ReplaceAll(string(typ), "\u0000", "\\u0000")
    21  	return json.Marshal(newVal)
    22  }
    23  
    24  // UnmarshalJSON reverts MarshalJSON data arrays to its base type.
    25  func (typ *Type) UnmarshalJSON(data []byte) error {
    26  	var d string
    27  	if err := json.Unmarshal(data, &d); err != nil {
    28  		return err
    29  	}
    30  	reverted := strings.ReplaceAll(d, "\\u0000", "\u0000")
    31  	*typ = Type(reverted)
    32  	return nil
    33  }
    34  
    35  const (
    36  	byteUnset byte = iota
    37  	byteAny
    38  	byteNil
    39  	byteRef
    40  	byteBool
    41  	byteInt
    42  	byteFloat
    43  	byteString
    44  	byteRegex
    45  	byteTime
    46  	byteDict
    47  	byteScore
    48  	byteBlock
    49  	byteEmpty
    50  	byteArray = 1<<4 + iota - 5 // set to 25 to avoid breaking changes
    51  	byteMap
    52  	byteResource
    53  	byteFunction
    54  	byteStringSlice
    55  	byteRange
    56  )
    57  
    58  // NoType type is one whose type information is not available at all
    59  const NoType Type = ""
    60  
    61  const (
    62  	// Unset type indicates that the type has not yet been set
    63  	Unset = Type(rune(byteUnset))
    64  	// Any type indicating an untyped value that can have any type
    65  	Any = Type(rune(byteAny))
    66  	// Nil for the empty type
    67  	Nil = Type(rune(byteNil))
    68  	// Ref for internal code chunk references
    69  	Ref = Type(rune(byteRef))
    70  	// Bool for the booleans true and false
    71  	Bool = Type(rune(byteBool))
    72  	// Int for integers
    73  	Int = Type(rune(byteInt))
    74  	// Float for any decimal values
    75  	Float = Type(rune(byteFloat))
    76  	// String for strings
    77  	String = Type(rune(byteString))
    78  	// Regex for regular expressions
    79  	Regex = Type(rune(byteRegex))
    80  	// Time for date and time
    81  	Time = Type(rune(byteTime))
    82  	// Dict for storing hierarchical simple key-value assignments
    83  	Dict = Type(rune(byteDict))
    84  	// Score for evaluations
    85  	Score = Type(rune(byteScore))
    86  	// Block evaluation results
    87  	Block = Type(rune(byteBlock))
    88  	// Empty value
    89  	Empty = Type(rune(byteEmpty))
    90  	// ArrayLike is the underlying type of all arrays
    91  	ArrayLike = Type(rune(byteArray))
    92  	// MapLike is the underlying type of all maps
    93  	MapLike = Type(rune(byteMap))
    94  	// ResourceLike is the underlying type of all resources
    95  	ResourceLike = Type(rune(byteResource))
    96  	// FunctionLike is the underlying type of all functions
    97  	FunctionLike = Type(rune(byteFunction))
    98  
    99  	// StringSlice is used to represent special function for searching strings.
   100  	// Users are never exposed to this type directly and it is not documented
   101  	// as a primitive. It serves as a way to array functions on top of strings,
   102  	// which is required for cases where a `dict` can represent both an array
   103  	// and a string (as well as other things) at the same time. Functions like
   104  	// `contains` are defined on both arrays and strings with different behavior.
   105  	// This types allows us to keep the compiler and execution simple, while
   106  	// handling the runtime distinction for dict.
   107  	StringSlice = Type(rune(byteStringSlice))
   108  
   109  	// Range represents a range of content. This can be a number of lines
   110  	// or lines and columns combined. We use a special type for a very
   111  	// efficient storage and transmission structure.
   112  	Range = Type(rune(byteRange))
   113  )
   114  
   115  // NotSet returns true if the type has no information
   116  func (typ Type) NotSet() bool {
   117  	return typ == ""
   118  }
   119  
   120  // Array for list of values
   121  func Array(typ Type) Type {
   122  	return ArrayLike + typ
   123  }
   124  
   125  // IsArray checks if this type is an array
   126  func (typ Type) IsArray() bool {
   127  	return typ[0] == byteArray
   128  }
   129  
   130  // Map for an association of keys and values
   131  func Map(key, value Type) Type {
   132  	if key != String && key != Int {
   133  		panic("Unsupported map on key type " + key.Label())
   134  	}
   135  	return MapLike + key + value
   136  }
   137  
   138  // IsMap checks if this type is a map
   139  func (typ Type) IsMap() bool {
   140  	return typ[0] == byteMap
   141  }
   142  
   143  // Resource for complex data structures
   144  func Resource(name string) Type {
   145  	return ResourceLike + Type(name)
   146  }
   147  
   148  // IsResource checks if this type is a map
   149  func (typ Type) IsResource() bool {
   150  	if typ.NotSet() {
   151  		return false
   152  	}
   153  	return typ[0] == byteResource
   154  }
   155  
   156  // ContainsResource checks if this or any child type has a resource
   157  func (typ Type) ContainsResource() bool {
   158  	for {
   159  		if typ.IsResource() {
   160  			return true
   161  		}
   162  
   163  		if !typ.IsArray() && !typ.IsMap() {
   164  			return false
   165  		}
   166  
   167  		typ = typ.Child()
   168  	}
   169  }
   170  
   171  // Function for creating a function type signature
   172  func Function(required rune, args []Type) Type {
   173  	var sig string
   174  	for _, arg := range args {
   175  		sig += string(arg) + "\x00"
   176  	}
   177  	return FunctionLike + Type(required) + Type(sig)
   178  }
   179  
   180  // IsFunction checks if this type is a map
   181  func (typ Type) IsFunction() bool {
   182  	return typ[0] == byteFunction
   183  }
   184  
   185  // Underlying returns the basic type, e.g. types.MapLike instead of types.Map(..)
   186  func (typ Type) Underlying() Type {
   187  	return Type(typ[0])
   188  }
   189  
   190  // Enforce makes sure that both types are the same, and returns the common
   191  // type and true if they are, false otherwise (and the right type).
   192  // - if one of the types is not yet set, use the other type instead.
   193  // - if neither are set return the unset type.
   194  // - goes into child types to see if either is unset
   195  func Enforce(left, right Type) (Type, bool) {
   196  	var i int
   197  	for ; i < len(left) && i < len(right); i++ {
   198  		if left[i] == right[i] {
   199  			continue
   200  		}
   201  
   202  		if right[i] == byteUnset || right[i] == byteNil {
   203  			return left, true
   204  		}
   205  		if left[i] == byteUnset || left[i] == byteNil {
   206  			return right, true
   207  		}
   208  	}
   209  
   210  	return right, len(left) == len(right)
   211  }
   212  
   213  // Child returns the child type of arrays and maps
   214  func (typ Type) Child() Type {
   215  	switch typ[0] {
   216  	case byteDict:
   217  		return Dict
   218  	case byteArray:
   219  		return typ[1:]
   220  	case byteMap:
   221  		return typ[2:]
   222  	}
   223  	panic("cannot determine child type of " + typ.Label())
   224  }
   225  
   226  // Key returns the key type of a map
   227  func (typ Type) Key() Type {
   228  	if typ[0] != byteMap {
   229  		panic("cannot retrieve key type of non-map type " + typ.Label())
   230  	}
   231  	return Type(typ[1])
   232  }
   233  
   234  // ResourceName return the name of a resource. Has to be a resource type,
   235  // otherwise this call panics.
   236  func (typ Type) ResourceName() string {
   237  	if typ[0] == byteResource {
   238  		return string(typ[1:])
   239  	}
   240  	panic("cannot determine type name of " + typ.Label())
   241  }
   242  
   243  var labels = map[byte]string{
   244  	byteUnset:       "unset",
   245  	byteAny:         "any",
   246  	byteNil:         "null",
   247  	byteRef:         "ref",
   248  	byteBool:        "bool",
   249  	byteInt:         "int",
   250  	byteFloat:       "float",
   251  	byteString:      "string",
   252  	byteRegex:       "regex",
   253  	byteTime:        "time",
   254  	byteDict:        "dict",
   255  	byteScore:       "score",
   256  	byteBlock:       "block",
   257  	byteEmpty:       "empty",
   258  	byteStringSlice: "stringslice",
   259  	byteRange:       "range",
   260  }
   261  
   262  var labelfun map[byte]func(Type) string
   263  
   264  func init() {
   265  	labelfun = map[byte]func(Type) string{
   266  		byteArray:    func(s Type) string { return "[]" + s.Label() },
   267  		byteMap:      func(s Type) string { return "map[" + Type(s[0]).Label() + "]" + s[1:].Label() },
   268  		byteResource: func(s Type) string { return string(s) },
   269  		byteFunction: func(f Type) string { return "function(..??..)" },
   270  	}
   271  }
   272  
   273  // Label provides a user-friendly type label
   274  func (typ Type) Label() string {
   275  	if typ == "" {
   276  		return "EMPTY"
   277  	}
   278  
   279  	h, ok := labels[typ[0]]
   280  	if ok {
   281  		return h
   282  	}
   283  
   284  	hf, ok := labelfun[typ[0]]
   285  	if !ok {
   286  		panic("cannot find label for type " + typ)
   287  	}
   288  	return hf(typ[1:])
   289  }
   290  
   291  // Equal provides a set of function for a range of types to test if 2 values
   292  // of that type are equal
   293  var Equal = map[Type]func(interface{}, interface{}) bool{
   294  	Bool: func(left, right interface{}) bool {
   295  		return left.(bool) == right.(bool)
   296  	},
   297  	Int: func(left, right interface{}) bool {
   298  		return left.(int64) == right.(int64)
   299  	},
   300  	Float: func(left, right interface{}) bool {
   301  		return left.(float64) == right.(float64)
   302  	},
   303  	String: func(left, right interface{}) bool {
   304  		return left.(string) == right.(string)
   305  	},
   306  	Regex: func(left, right interface{}) bool {
   307  		return left.(string) == right.(string)
   308  	},
   309  	Time: func(left, right interface{}) bool {
   310  		l := left.(*time.Time)
   311  		r := right.(*time.Time)
   312  		if l == nil || r == nil {
   313  			return false
   314  		}
   315  		return l.Equal(*r)
   316  	},
   317  	// types.Dict: func(left, right interface{}) bool {},
   318  	Score: func(left, right interface{}) bool {
   319  		return left.(int32) == right.(int32)
   320  	},
   321  }