1# Copyright (c) 2021-2022 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 14require 'strscan' 15 16Message = Struct.new(:name, :component, :number, :level, :args, :short_message, :message, keyword_init: true) do 17 def macro_name 18 ['LOG', component, Messages.split_words(name)].flatten.join('_').upcase 19 end 20 21 def scan_until_exclusive(scanner, pattern) 22 scanner.scan_until(Regexp.new(Regexp.escape(pattern)))&.delete_suffix(pattern) 23 end 24 25 def escape_string_literal(string) 26 %("#{string.gsub('"', '\\"').gsub("\n", '\\n').gsub("\t", '\\t').gsub('\\', '\\\\')}") 27 end 28 29 def stream_ops(is_short) 30 msg = is_short ? "#{component} #{level} #{number}: #{short_message}" : message 31 scanner = StringScanner.new(msg) 32 parts = [] 33 until scanner.eos? 34 str_part = scan_until_exclusive(scanner, '${') 35 if str_part 36 parts << escape_string_literal(str_part) unless str_part.empty? 37 expr_part = scan_until_exclusive(scanner, '}') 38 if expr_part 39 parts << expr_part 40 else 41 raise "Message template '#{msg}' has a '${' not followed by a '}'" 42 end 43 elsif !scanner.eos? 44 parts << escape_string_literal(scanner.rest) 45 scanner.terminate 46 end 47 end 48 parts.join(' << ') 49 end 50end 51 52module Messages 53 module_function 54 55 def split_words(string) 56 scanner = StringScanner.new(string) 57 words = [] 58 until scanner.eos? 59 word = scanner.scan(/[A-Z0-9][a-z0-9]*/) 60 if word.empty? 61 raise "Message name '#{string}' is not in UpperCamelCase" 62 else 63 words << word 64 end 65 end 66 words 67 end 68 69 def load_messages(data) 70 @component = data.component.capitalize 71 @namespace = data.namespace 72 @enum_name = data.enum_name 73 @messages = data.messages.each_pair.map do |name, msg_data| 74 name = name.to_s 75 level = msg_data.level || data.default_level 76 short_message = msg_data.short_message&.strip || split_words(name).join(' ').capitalize 77 Message.new( 78 name: name, 79 component: @component, 80 number: msg_data.number, 81 level: level, 82 args: msg_data.args || '', 83 short_message: short_message, 84 message: msg_data.message&.strip 85 ) 86 end 87 end 88 89 [:component, :enum_name, :namespace, :messages].each do |attr| 90 # equivalent to attr_reader(attr), but that gives a warning in JRuby 9.2.17.0 91 define_method(attr) do 92 instance_variable_get("@#{attr}") 93 end 94 end 95end 96 97def Gen.on_require(data) 98 Messages.load_messages(data) 99end 100