1# Copyright (c) 2024 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14module ESChecker 15 module Types 16 def self.value_is_number(r) 17 r.kind_of?(Float) || 18 r.kind_of?(Integer) || 19 r.kind_of?(Hash) && r["__kind"] == "num" || 20 ["#__NaN", "#__NegInf", "#__Inf", "#__NaN"].include?(r) || 21 r.kind_of?(String) && /^Double.bitCastFromLong.*/ =~ r 22 end 23 24 def self.value_is_string(r) 25 return r.kind_of?(String) && !UNMANGLE.has_key?(r) || 26 r.kind_of?(Hash) && r["__kind"] == "str" 27 end 28 29 def self.dump(t, buf: "") 30 case t[:kind] 31 when :array 32 buf << "Array<" 33 dump t[:el], buf: buf 34 buf << ">" 35 when :tuple 36 buf << "[" 37 t[:els].each_with_index { |t, i| 38 if i != 0 39 buf << ", " 40 end 41 dump t, buf: buf 42 } 43 buf << "]" 44 when :trivial 45 buf << t[:str] 46 else 47 raise "unknown type kind #{t.kind}" 48 end 49 buf 50 end 51 52 def self.detect(rb_obj, dflt: nil) 53 if !dflt.nil? 54 return ESChecker::Types::Parser.new(dflt).run 55 end 56 if [true, false].include? rb_obj 57 return { :kind => :trivial, :str => "boolean" } 58 end 59 if value_is_number(rb_obj) 60 return { :kind => :trivial, :str => "number" } 61 end 62 if value_is_string rb_obj 63 return { :kind => :trivial, :str => "string" } 64 end 65 if rb_obj.kind_of?(Hash) && rb_obj["__kind"] == "bigint" 66 return { :kind => :trivial, :str => "bigint" } 67 end 68 if rb_obj.kind_of?(Array) 69 if rb_obj.size == 0 70 return { :kind => :array, :el => { :kind => :trivial, :str => "Object|null|undefined" } } 71 end 72 typs = rb_obj.map { |r| detect r } 73 if typs.all? { |t| t == typs[0] } 74 return { :kind => :array, :el => typs[0] } 75 end 76 end 77 if rb_obj == "#__undefined" || rb_obj == "#__null" 78 return { :kind => :trivial, :str => "NullishType" } 79 end 80 raise "can't detect type for #{rb_obj}" 81 end 82 83 class Parser 84 def initialize(str) 85 @str = str 86 @chrs = str.chars.reverse 87 end 88 89 def error(msg: nil) 90 raise "can't parse type from '#{@str}' : tail is '#{@chrs.reverse[..100].join}' ; #{msg}" 91 end 92 93 def skip_ws 94 while @chrs.size != 0 && /\s+/ =~ @chrs[-1] 95 @chrs.pop 96 end 97 end 98 def fetch_expected(s) 99 if @chrs[-1] != s 100 error(msg: "expected #{s}") 101 end 102 @chrs.pop 103 end 104 def starts_with(s) 105 if s.size > @chrs.size 106 return false 107 end 108 s.each_char.with_index { |c, i| 109 if c != @chrs[-1 - i] 110 return false 111 end 112 } 113 return true 114 end 115 116 def run 117 ret = step 118 skip_ws 119 if @chrs.size != 0 120 error(msg: "unparsed tail") 121 end 122 return ret 123 end 124 125 def parse_tuple_els 126 skip_ws 127 els = [] 128 while @chrs.size != 0 && @chrs[-1] != ']' 129 els.push(step) 130 skip_ws 131 if @chrs[-1] == ',' 132 @chrs.pop 133 else 134 break 135 end 136 skip_ws 137 end 138 els 139 end 140 141 def parse_trivial 142 stck = [] 143 res = String.new 144 while @chrs.size > 0 145 if @chrs[-1] == '<' || @chrs[-1] == '[' 146 stck.push @chrs[-1] 147 res << @chrs.pop 148 elsif @chrs[-1] == '>' || @chrs[-1] == ']' 149 if stck.size == 0 150 break 151 end 152 res << @chrs.pop 153 stck.pop 154 elsif stck.size == 0 && @chrs[-1] == ',' 155 break 156 else 157 res << @chrs.pop 158 end 159 end 160 res 161 end 162 163 def step 164 skip_ws 165 if starts_with "Array<" 166 @chrs.pop("Array<".size) 167 el = step 168 skip_ws 169 fetch_expected '>' 170 return { :kind => :array, :el => el } 171 elsif starts_with "Iterable<" 172 @chrs.pop("Iterable<".size) 173 el = step 174 skip_ws 175 fetch_expected '>' 176 return { :kind => :array, :el => el } 177 elsif starts_with '[' 178 @chrs.pop 179 els = parse_tuple_els 180 fetch_expected ']' 181 return { :kind => :tuple, :els => els } 182 else 183 res = parse_trivial 184 return { :kind => :trivial, :str => res } 185 end 186 end 187 end 188 end 189end 190