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 }