| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 
 | module Puppet::Pops::Evaluator
# Converts nested 4x supported values to 3x values. This is required because
# resources and other objects do not know about the new type system, and does not support
# regular expressions. Unfortunately this has to be done for array and hash as well.
# A complication is that catalog types needs to be resolved against the scope.
#
# Users should not create instances of this class. Instead the class methods {Runtime3Converter.convert},
# {Runtime3Converter.map_args}, or {Runtime3Converter.instance} should be used
class Runtime3Converter
  MAX_INTEGER =  Puppet::Pops::MAX_INTEGER
  MIN_INTEGER = Puppet::Pops::MIN_INTEGER
  # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.map_args(...)
  #
  # @param args [Array] Array of values to convert
  # @param scope [Puppet::Parser::Scope] The scope to use when converting
  # @param undef_value [Object] The value that nil is converted to
  # @return [Array] The converted values
  #
  def self.map_args(args, scope, undef_value)
    @instance.map_args(args, scope, undef_value)
  end
  # Converts 4x supported values to a 3x values. Same as calling Runtime3Converter.instance.convert(...)
  #
  # @param o [Object]The value to convert
  # @param scope [Puppet::Parser::Scope] The scope to use when converting
  # @param undef_value [Object] The value that nil is converted to
  # @return [Object] The converted value
  #
  def self.convert(o, scope, undef_value)
    @instance.convert(o, scope, undef_value)
  end
  # Returns the singleton instance of this class.
  # @return [Runtime3Converter] The singleton instance
  def self.instance
    @instance
  end
  # Converts 4x supported values to a 3x values.
  #
  # @param args [Array] Array of values to convert
  # @param scope [Puppet::Parser::Scope] The scope to use when converting
  # @param undef_value [Object] The value that nil is converted to
  # @return [Array] The converted values
  #
  def map_args(args, scope, undef_value)
    args.map {|a| convert(a, scope, undef_value) }
  end
  # Converts a 4x supported value to a 3x value.
  #
  # @param o [Object]The value to convert
  # @param scope [Puppet::Parser::Scope] The scope to use when converting
  # @param undef_value [Object] The value that nil is converted to
  # @return [Object] The converted value
  #
  def convert(o, scope, undef_value)
    @convert_visitor.visit_this_2(self, o, scope, undef_value)
  end
  def convert_NilClass(o, scope, undef_value)
    @inner ? nil : undef_value
  end
  def convert_Integer(o, scope, undef_value)
    return o unless o < MIN_INTEGER || o > MAX_INTEGER
    range_end = o > MAX_INTEGER ? 'max' : 'min'
    raise Puppet::Error, "Use of a Ruby Integer outside of Puppet Integer #{range_end} range, got '#{"0x%x" % o}'"
  end
  def convert_BigDecimal(o, scope, undef_value)
    # transform to same value float value if possible without any rounding error
    f = o.to_f
    return f unless f != o
    raise Puppet::Error, "Use of a Ruby BigDecimal value outside Puppet Float range, got '#{o}'"
  end
  def convert_String(o, scope, undef_value)
    # Although wasteful, a dup is needed because user code may mutate these strings when applying
    # Resources. This does not happen when in server mode since it only uses Resources that are
    # in puppet core and those are all safe.
    o.frozen? && !Puppet.run_mode.server? ? o.dup : o
  end
  def convert_Object(o, scope, undef_value)
    o
  end
  def convert_Array(o, scope, undef_value)
    ic = @inner_converter
    o.map {|x| ic.convert(x, scope, undef_value) }
  end
  def convert_Hash(o, scope, undef_value)
    result = {}
    ic = @inner_converter
    o.each {|k,v| result[ic.convert(k, scope, undef_value)] = ic.convert(v, scope, undef_value) }
    result
  end
  def convert_Iterator(o, scope, undef_value)
    raise Puppet::Error, _('Use of an Iterator is not supported here')
  end
  def convert_Symbol(o, scope, undef_value)
    return o unless o == :undef
    !@inner ? undef_value : nil
  end
  def convert_PAnyType(o, scope, undef_value)
    o
  end
  def convert_PCatalogEntryType(o, scope, undef_value)
    # Since 4x does not support dynamic scoping, all names are absolute and can be
    # used as is (with some check/transformation/mangling between absolute/relative form
    # due to Puppet::Resource's idiosyncratic behavior where some references must be
    # absolute and others cannot be.
    # Thus there is no need to call scope.resolve_type_and_titles to do dynamic lookup.
    t, title = catalog_type_to_split_type_title(o)
    t = Runtime3ResourceSupport.find_resource_type(scope, t) unless t == 'class' || t == 'node'
    Puppet::Resource.new(t, title)
  end
  # Produces an array with [type, title] from a PCatalogEntryType
  # This method is used to produce the arguments for creation of reference resource instances
  # (used when 3x is operating on a resource).
  # Ensures that resources are *not* absolute.
  #
  def catalog_type_to_split_type_title(catalog_type)
    split_type = catalog_type.is_a?(Puppet::Pops::Types::PTypeType) ? catalog_type.type : catalog_type
    case split_type
      when Puppet::Pops::Types::PClassType
        class_name = split_type.class_name
        ['class', class_name.nil? ? nil : class_name.sub(/^::/, '')]
      when Puppet::Pops::Types::PResourceType
        type_name = split_type.type_name
        title = split_type.title
        if type_name =~ /^(::)?[Cc]lass$/
          ['class', title.nil? ? nil : title.sub(/^::/, '')]
        else
          # Ensure that title is '' if nil
          # Resources with absolute name always results in error because tagging does not support leading ::
          [type_name.nil? ? nil : type_name.sub(/^::/, '').downcase, title.nil? ? '' : title]
        end
      else
        #TRANSLATORS 'PClassType' and 'PResourceType' are Puppet types and should not be translated
        raise ArgumentError, _("Cannot split the type %{class_name}, it represents neither a PClassType, nor a PResourceType.") %
            { class_name: catalog_type.class }
    end
  end
  protected
  def initialize(inner = false)
    @inner = inner
    @inner_converter = inner ? self : self.class.new(true)
    @convert_visitor = Puppet::Pops::Visitor.new(self, 'convert', 2, 2)
  end
  @instance = self.new
end
# A Ruby function written for the 3.x API cannot be expected to handle extended data types. This
# converter ensures that they are converted to String format
# @api private
class Runtime3FunctionArgumentConverter < Runtime3Converter
  def convert_Regexp(o, scope, undef_value)
    # Puppet 3x cannot handle parameter values that are regular expressions. Turn into regexp string in
    # source form
    o.inspect
  end
  def convert_Version(o, scope, undef_value)
    # Puppet 3x cannot handle SemVers. Use the string form
    o.to_s
  end
  def convert_VersionRange(o, scope, undef_value)
    # Puppet 3x cannot handle SemVerRanges. Use the string form
    o.to_s
  end
  def convert_Binary(o, scope, undef_value)
    # Puppet 3x cannot handle Binary. Use the string form
    o.to_s
  end
  def convert_Timespan(o, scope, undef_value)
    # Puppet 3x cannot handle Timespans. Use the string form
    o.to_s
  end
  def convert_Timestamp(o, scope, undef_value)
    # Puppet 3x cannot handle Timestamps. Use the string form
    o.to_s
  end
  # Converts result back to 4.x by replacing :undef with nil in Array and Hash objects
  #
  def self.convert_return(val3x)
    if val3x == :undef
      nil
    elsif val3x.is_a?(Array)
      val3x.map {|v| convert_return(v) }
    elsif val3x.is_a?(Hash)
      hsh = {}
      val3x.each_pair {|k,v| hsh[convert_return(k)] = convert_return(v)}
      hsh
    else
      val3x
    end
  end
  @instance = self.new
end
end
 |