# frozen_string_literal: true require 'logger' require 'json' module Crate class Raw REPLACE = { "\xe2\x80\x9c" => '"', "\xe2\x80\x9d" => '"', }.freeze attr_reader :options def gsub_(raw) raw.dup.tap do |raw_| REPLACE.each do |pattern, replacement| raw_.gsub!(pattern, replacement) end end end def log(&block) options[:logger].add(options[:severity], &block) end def log_if_changed(raw, replaced) return if raw == replaced log do "#{options[:fullname]}: Harmful characters are changed " \ "from: #{raw.inspect} to: #{replaced.inspect}" end end def log_if_harmful(replaced) bytes = replaced.each_char.reject(&:ascii_only?).map(&:bytes) return if bytes.empty? log do "#{options[:fullname]}: Harmful characters are still remained: " \ "#{bytes.inspect}" end end def gsub(raw) gsub_(raw).tap do |replaced| log_if_changed(raw, replaced) log_if_harmful(replaced) end end def symbolize(object) case object when Hash object.map { |key, value| [key.to_sym, symbolize(value)] }.to_h else object end end def make_hash(raw) case raw when String JSON.parse(gsub(raw), symbolize_names: true) when Hash raw else raise end end def initialize(raw, **options) @options = { logger: Logger.new(nil), severity: Logger::WARN, **options } @raw = symbolize(make_hash(raw)) end def to_hash @raw end end end