lib/custom_facts/util/normalization.rb
# frozen_string_literal: true
module LegacyFacter
module Util
module Normalization
class NormalizationError < StandardError; end
VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, Symbol, String, Array, Hash].freeze
module_function
# Recursively normalize the given data structure
#
# @api public
# @raise [NormalizationError] If the data structure contained an invalid element.
# @return [void]
def normalize(value)
case value
when Integer, Float, TrueClass, FalseClass, NilClass, Symbol, Date
value
when String
normalize_string(value)
when Array
normalize_array(value)
when Hash
normalize_hash(value)
else
raise NormalizationError, "Expected #{value} to be one of #{VALID_TYPES.inspect}, but was #{value.class}"
end
end
# @!method normalize_string(value)
#
# Attempt to normalize and validate the given string.
#
# On Ruby 1.8 the string is checked by stripping out all non UTF-8
# characters and comparing the converted string to the original. If they
# do not match then the string is considered invalid.
#
# On Ruby 1.9+, the string is validate by checking that the string encoding
# is UTF-8 and that the string content matches the encoding. If the string
# is not an expected encoding then it is converted to UTF-8.
#
# @api public
# @raise [NormalizationError] If the string used an unsupported encoding or did not match its encoding
# @param value [String]
# @return [void]
if RUBY_VERSION =~ /^1\.8/
require 'iconv'
def normalize_string(value)
converted = Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
raise NormalizationError, "String #{value.inspect} is not valid UTF-8" if converted != value
value
end
else
def normalize_string(value)
value = value.encode(Encoding::UTF_8)
unless value.valid_encoding?
raise NormalizationError, "String #{value.inspect} doesn't match the reported encoding #{value.encoding}"
end
value
rescue EncodingError
raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
end
end
# Validate all elements of the array.
#
# @api public
# @raise [NormalizationError] If one of the elements failed validation
# @param value [Array]
# @return [void]
def normalize_array(value)
value.collect do |elem|
normalize(elem)
end
end
# Validate all keys and values of the hash.
#
# @api public
# @raise [NormalizationError] If one of the keys or values failed normalization
# @param value [Hash]
# @return [void]
def normalize_hash(value)
Hash[value.collect { |k, v| [normalize(k), normalize(v)] }]
end
end
end
end