PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
| Dir : /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/types/ |
| Server: Linux server1.ngambekcore.com 4.18.0-553.51.1.el8_10.x86_64 #1 SMP Wed Apr 30 04:00:07 EDT 2025 x86_64 IP: 159.198.77.92 |
| Dir : //opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/pops/types/type_formatter.rb |
# frozen_string_literal: true
require_relative '../../../puppet/concurrent/thread_local_singleton'
module Puppet::Pops
module Types
# String
# ------
# Creates a string representation of a type.
#
# @api public
#
class TypeFormatter
extend Puppet::Concurrent::ThreadLocalSingleton
# Produces a String representation of the given type.
# @param t [PAnyType] the type to produce a string form
# @return [String] the type in string form
#
# @api public
#
def self.string(t)
singleton.string(t)
end
def initialize
@string_visitor = Visitor.new(nil, 'string', 0, 0)
end
def expanded
tf = clone
tf.instance_variable_set(:@expanded, true)
tf
end
def indented(indent = 0, indent_width = 2)
tf = clone
tf.instance_variable_set(:@indent, indent)
tf.instance_variable_set(:@indent_width, indent_width)
tf
end
def ruby(ref_ctor)
tf = clone
tf.instance_variable_set(:@ruby, true)
tf.instance_variable_set(:@ref_ctor, ref_ctor)
tf
end
# Produces a string representing the type
# @api public
#
def string(t)
@bld = ''.dup
append_string(t)
@bld
end
# Produces an string containing newline characters and indentation that represents the given
# type or literal _t_.
#
# @param t [Object] the type or literal to produce a string for
# @param indent [Integer] the current indentation level
# @param indent_width [Integer] the number of spaces to use for one indentation
#
# @api public
def indented_string(t, indent = 0, indent_width = 2)
@bld = ''.dup
append_indented_string(t, indent, indent_width)
@bld
end
# @api private
def append_indented_string(t, indent = 0, indent_width = 2, skip_initial_indent = false)
save_indent = @indent
save_indent_width = @indent_width
@indent = indent
@indent_width = indent_width
begin
(@indent * @indent_width).times { @bld << ' ' } unless skip_initial_indent
append_string(t)
@bld << "\n"
ensure
@indent = save_indent
@indent_width = save_indent_width
end
end
# @api private
def ruby_string(ref_ctor, indent, t)
@ruby = true
@ref_ctor = ref_ctor
begin
indented_string(t, indent)
ensure
@ruby = nil
@ref_ctor = nil
end
end
def append_default
@bld << 'default'
end
def append_string(t)
if @ruby && t.is_a?(PAnyType)
@ruby = false
begin
@bld << @ref_ctor << '('
@string_visitor.visit_this_0(self, TypeFormatter.new.string(t))
@bld << ')'
ensure
@ruby = true
end
else
@string_visitor.visit_this_0(self, t)
end
end
# Produces a string representing the type where type aliases have been expanded
# @api public
#
def alias_expanded_string(t)
@expanded = true
begin
string(t)
ensure
@expanded = false
end
end
# Produces a debug string representing the type (possibly with more information that the regular string format)
# @api public
#
def debug_string(t)
@debug = true
begin
string(t)
ensure
@debug = false
end
end
# @api private
def string_PAnyType(_) ; @bld << 'Any' ; end
# @api private
def string_PUndefType(_) ; @bld << 'Undef' ; end
# @api private
def string_PDefaultType(_) ; @bld << 'Default' ; end
# @api private
def string_PBooleanType(t)
append_array('Boolean', t.value.nil?) { append_string(t.value) }
end
# @api private
def string_PScalarType(_) ; @bld << 'Scalar' ; end
# @api private
def string_PScalarDataType(_) ; @bld << 'ScalarData' ; end
# @api private
def string_PNumericType(_) ; @bld << 'Numeric' ; end
# @api private
def string_PBinaryType(_) ; @bld << 'Binary' ; end
# @api private
def string_PIntegerType(t)
append_array('Integer', t.unbounded?) { append_elements(range_array_part(t)) }
end
# @api private
def string_PTypeType(t)
append_array('Type', t.type.nil?) { append_string(t.type) }
end
# @api private
def string_PInitType(t)
append_array('Init', t.type.nil?) { append_strings([t.type, *t.init_args]) }
end
# @api private
def string_PIterableType(t)
append_array('Iterable', t.element_type.nil?) { append_string(t.element_type) }
end
# @api private
def string_PIteratorType(t)
append_array('Iterator', t.element_type.nil?) { append_string(t.element_type) }
end
# @api private
def string_PFloatType(t)
append_array('Float', t.unbounded? ) { append_elements(range_array_part(t)) }
end
# @api private
def string_PRegexpType(t)
append_array('Regexp', t.pattern.nil?) { append_string(t.regexp) }
end
# @api private
def string_PStringType(t)
range = range_array_part(t.size_type)
append_array('String', range.empty? && !(@debug && !t.value.nil?)) do
if @debug
append_elements(range, !t.value.nil?)
append_string(t.value) unless t.value.nil?
else
append_elements(range)
end
end
end
# @api private
def string_PEnumType(t)
append_array('Enum', t.values.empty?) do
append_strings(t.values)
if t.case_insensitive?
@bld << COMMA_SEP
append_string(true)
end
end
end
# @api private
def string_PVariantType(t)
append_array('Variant', t.types.empty?) { append_strings(t.types) }
end
# @api private
def string_PSemVerType(t)
append_array('SemVer', t.ranges.empty?) { append_strings(t.ranges) }
end
# @api private
def string_PSemVerRangeType(t)
@bld << 'SemVerRange'
end
# @api private
def string_PTimestampType(t)
min = t.from
max = t.to
append_array('Timestamp', min.nil? && max.nil?) do
min.nil? ? append_default : append_string(min)
unless max.nil? || max == min
@bld << COMMA_SEP
append_string(max)
end
end
end
# @api private
def string_PTimespanType(t)
min = t.from
max = t.to
append_array('Timespan', min.nil? && max.nil?) do
min.nil? ? append_default : append_string(min)
unless max.nil? || max == min
@bld << COMMA_SEP
append_string(max)
end
end
end
# @api private
def string_PTupleType(t)
append_array('Tuple', t.types.empty?) do
append_strings(t.types, true)
append_elements(range_array_part(t.size_type), true)
chomp_list
end
end
# @api private
def string_PCallableType(t)
if t.return_type.nil?
append_array('Callable', t.param_types.nil?) { append_callable_params(t) }
else
if t.param_types.nil?
append_array('Callable', false) { append_strings([[], t.return_type], false) }
else
append_array('Callable', false) do
append_array('', false) { append_callable_params(t) }
@bld << COMMA_SEP
append_string(t.return_type)
end
end
end
end
def append_callable_params(t)
# translate to string, and skip Unit types
append_strings(t.param_types.types.reject {|t2| t2.class == PUnitType }, true)
if t.param_types.types.empty?
append_strings([0, 0], true)
else
append_elements(range_array_part(t.param_types.size_type), true)
end
# Add block T last (after min, max) if present)
#
append_strings([t.block_type], true) unless t.block_type.nil?
chomp_list
end
# @api private
def string_PStructType(t)
append_array('Struct', t.elements.empty?) { append_hash(Hash[t.elements.map {|e| struct_element_pair(e) }]) }
end
# @api private
def struct_element_pair(t)
k = t.key_type
value_optional = t.value_type.assignable?(PUndefType::DEFAULT)
if k.is_a?(POptionalType)
# Output as literal String
k = t.name if value_optional
else
k = value_optional ? PNotUndefType.new(k) : t.name
end
[k, t.value_type]
end
# @api private
def string_PPatternType(t)
append_array('Pattern', t.patterns.empty?) { append_strings(t.patterns.map(&:regexp)) }
end
# @api private
def string_PCollectionType(t)
range = range_array_part(t.size_type)
append_array('Collection', range.empty? ) { append_elements(range) }
end
def string_Object(t)
type = TypeCalculator.infer(t)
if type.is_a?(PObjectTypeExtension)
type = type.base_type
end
if type.is_a?(PObjectType)
init_hash = type.extract_init_hash(t)
@bld << type.name << '('
if @indent
append_indented_string(init_hash, @indent, @indent_width, true)
@bld.chomp!
else
append_string(init_hash)
end
@bld << ')'
else
@bld << 'Instance of '
append_string(type)
end
end
def string_PuppetObject(t)
@bld << t._pcore_type.name << '('
if @indent
append_indented_string(t._pcore_init_hash, @indent, @indent_width, true)
@bld.chomp!
else
append_string(t._pcore_init_hash)
end
@bld << ')'
end
# @api private
def string_PURIType(t)
append_array('URI', t.parameters.nil?) { append_string(t._pcore_init_hash['parameters']) }
end
def string_URI(t)
@bld << 'URI('
if @indent
append_indented_string(t.to_s, @indent, @indent_width, true)
@bld.chomp!
else
append_string(t.to_s)
end
@bld << ')'
end
# @api private
def string_PUnitType(_)
@bld << 'Unit'
end
# @api private
def string_PRuntimeType(t)
append_array('Runtime', t.runtime.nil? && t.name_or_pattern.nil?) { append_strings([t.runtime, t.name_or_pattern]) }
end
# @api private
def string_PArrayType(t)
if t.has_empty_range?
append_array('Array') { append_strings([0, 0]) }
else
append_array('Array', t == PArrayType::DEFAULT) do
append_strings([t.element_type], true)
append_elements(range_array_part(t.size_type), true)
chomp_list
end
end
end
# @api private
def string_PHashType(t)
if t.has_empty_range?
append_array('Hash') { append_strings([0, 0]) }
else
append_array('Hash', t == PHashType::DEFAULT) do
append_strings([t.key_type, t.value_type], true)
append_elements(range_array_part(t.size_type), true)
chomp_list
end
end
end
# @api private
def string_PCatalogEntryType(_)
@bld << 'CatalogEntry'
end
# @api private
def string_PClassType(t)
append_array('Class', t.class_name.nil?) { append_elements([t.class_name]) }
end
# @api private
def string_PResourceType(t)
if t.type_name
append_array(capitalize_segments(t.type_name), t.title.nil?) { append_string(t.title) }
else
@bld << 'Resource'
end
end
# @api private
def string_PNotUndefType(t)
contained_type = t.type
append_array('NotUndef', contained_type.nil? || contained_type.class == PAnyType) do
if contained_type.is_a?(PStringType) && !contained_type.value.nil?
append_string(contained_type.value)
else
append_string(contained_type)
end
end
end
# @api private
def string_PAnnotatedMember(m)
hash = m._pcore_init_hash
if hash.size == 1
string(m.type)
else
string(hash)
end
end
# Used when printing names of well known keys in an Object type. Placed in a separate
# method to allow override.
# @api private
def symbolic_key(key)
@ruby ? "'#{key}'" : key
end
# @api private
def string_PTypeSetType(t)
append_array('TypeSet') do
append_hash(t._pcore_init_hash.each, proc { |k| @bld << symbolic_key(k) }) do |k,v|
case k
when KEY_TYPES
old_ts = @type_set
@type_set = t
begin
append_hash(v, proc { |tk| @bld << symbolic_key(tk) }) do |tk, tv|
if tv.is_a?(Hash)
append_object_hash(tv)
else
append_string(tv)
end
end
rescue
@type_set = old_ts
end
when KEY_REFERENCES
append_hash(v, proc { |tk| @bld << symbolic_key(tk) })
else
append_string(v)
end
end
end
end
# @api private
def string_PObjectType(t)
if @expanded
append_object_hash(t._pcore_init_hash(@type_set.nil? || !@type_set.defines_type?(t)))
else
@bld << (@type_set ? @type_set.name_for(t, t.label) : t.label)
end
end
def string_PObjectTypeExtension(t)
append_array(@type_set ? @type_set.name_for(t, t.name) : t.name, false) do
ips = t.init_parameters
if ips.is_a?(Array)
append_strings(ips)
else
append_string(ips)
end
end
end
# @api private
def string_PSensitiveType(t)
append_array('Sensitive', PAnyType::DEFAULT == t.type) { append_string(t.type) }
end
# @api private
def string_POptionalType(t)
optional_type = t.optional_type
append_array('Optional', optional_type.nil?) do
if optional_type.is_a?(PStringType) && !optional_type.value.nil?
append_string(optional_type.value)
else
append_string(optional_type)
end
end
end
# @api private
def string_PTypeAliasType(t)
expand = @expanded
if expand && t.self_recursion?
@guard ||= RecursionGuard.new
@guard.with_this(t) { |state| format_type_alias_type(t, (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0) }
else
format_type_alias_type(t, expand)
end
end
# @api private
def format_type_alias_type(t, expand)
if @type_set.nil?
@bld << t.name
if expand && !Loader::StaticLoader::BUILTIN_ALIASES.include?(t.name)
@bld << ' = '
append_string(t.resolved_type)
end
else
if expand && @type_set.defines_type?(t)
append_string(t.resolved_type)
else
@bld << @type_set.name_for(t, t.name)
end
end
end
# @api private
def string_PTypeReferenceType(t)
append_array('TypeReference') { append_string(t.type_string) }
end
# @api private
def string_Array(t)
append_array('') do
if @indent && !is_short_array?(t)
@indent += 1
t.each { |elem| newline; append_string(elem); @bld << COMMA_SEP }
chomp_list
@indent -= 1
newline
else
append_strings(t)
end
end
end
# @api private
def string_FalseClass(t) ; @bld << 'false' ; end
# @api private
def string_Hash(t)
append_hash(t)
end
# @api private
def string_Module(t)
append_string(TypeCalculator.singleton.type(t))
end
# @api private
def string_NilClass(t) ; @bld << (@ruby ? 'nil' : 'undef') ; end
# @api private
def string_Numeric(t) ; @bld << t.to_s ; end
# @api private
def string_Regexp(t) ; @bld << PRegexpType.regexp_to_s_with_delimiters(t); end
# @api private
def string_String(t)
# Use single qoute on strings that does not contain single quotes, control characters, or backslashes.
@bld << StringConverter.singleton.puppet_quote(t)
end
# @api private
def string_Symbol(t) ; @bld << t.to_s ; end
# @api private
def string_TrueClass(t) ; @bld << 'true' ; end
# @api private
def string_Version(t) ; @bld << "'#{t}'" ; end
# @api private
def string_VersionRange(t) ; @bld << "'#{t}'" ; end
# @api private
def string_Timespan(t) ; @bld << "'#{t}'" ; end
# @api private
def string_Timestamp(t) ; @bld << "'#{t}'" ; end
# Debugging to_s to reduce the amount of output
def to_s
'[a TypeFormatter]'
end
NAME_SEGMENT_SEPARATOR = '::'
STARTS_WITH_ASCII_CAPITAL = /^[A-Z]/
# Capitalizes each segment in a name separated with the {NAME_SEPARATOR} conditionally. The name
# will not be subject to capitalization if it already starts with a capital letter. This to avoid
# that existing camel casing is lost.
#
# @param qualified_name [String] the name to capitalize
# @return [String] the capitalized name
#
# @api private
def capitalize_segments(qualified_name)
if !qualified_name.is_a?(String) || qualified_name =~ STARTS_WITH_ASCII_CAPITAL
qualified_name
else
segments = qualified_name.split(NAME_SEGMENT_SEPARATOR)
if segments.size == 1
qualified_name.capitalize
else
segments.each(&:capitalize!)
segments.join(NAME_SEGMENT_SEPARATOR)
end
end
end
private
COMMA_SEP = ', '
HASH_ENTRY_OP = ' => '
def is_short_array?(t)
t.empty? || 100 - @indent * @indent_width > t.inject(0) do |sum, elem|
case elem
when true, false, nil, Numeric, Symbol
sum + elem.inspect.length()
when String
sum + 2 + elem.length
when Hash, Array
sum + (elem.empty? ? 2 : 1000)
else
sum + 1000
end
end
end
def range_array_part(t)
if t.nil? || t.unbounded?
EMPTY_ARRAY
else
result = [t.from.nil? ? 'default' : t.from.to_s]
result << t.to.to_s unless t.to.nil?
result
end
end
def append_object_hash(hash)
begin
@expanded = false
append_array('Object') do
append_hash(hash, proc { |k| @bld << symbolic_key(k) }) do |k,v|
case k
when KEY_ATTRIBUTES, KEY_FUNCTIONS
# Types might need to be output as type references
append_hash(v) do |_, fv|
if fv.is_a?(Hash)
append_hash(fv, proc { |fak| @bld << symbolic_key(fak) }) do |fak,fav|
case fak
when KEY_KIND
@bld << fav
else
append_string(fav)
end
end
else
append_string(fv)
end
end
when KEY_EQUALITY
append_array('') { append_strings(v) } if v.is_a?(Array)
else
append_string(v)
end
end
end
ensure
@expanded = true
end
end
def append_elements(array, to_be_continued = false)
case array.size
when 0
when 1
@bld << array[0]
@bld << COMMA_SEP if to_be_continued
else
array.each { |elem| @bld << elem << COMMA_SEP }
chomp_list unless to_be_continued
end
end
def append_strings(array, to_be_continued = false)
case array.size
when 0
when 1
append_string(array[0])
@bld << COMMA_SEP if to_be_continued
else
array.each do |elem|
append_string(elem)
@bld << COMMA_SEP
end
chomp_list unless to_be_continued
end
end
def append_array(start, empty = false)
@bld << start
unless empty
@bld << '['
yield
@bld << ']'
end
end
def append_hash(hash, key_proc = nil)
@bld << '{'
@indent += 1 if @indent
hash.each do |k, v|
newline if @indent
if key_proc.nil?
append_string(k)
else
key_proc.call(k)
end
@bld << HASH_ENTRY_OP
if block_given?
yield(k, v)
else
append_string(v)
end
@bld << COMMA_SEP
end
chomp_list
if @indent
@indent -= 1
newline
end
@bld << '}'
end
def newline
@bld.rstrip!
@bld << "\n"
(@indent * @indent_width).times { @bld << ' ' }
end
def chomp_list
@bld.chomp!(COMMA_SEP)
end
end
end
end