File: match.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (133 lines) | stat: -rw-r--r-- 4,388 bytes parent folder | download | duplicates (2)
1
2
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
# frozen_string_literal: true

# Matches a regular expression against a string and returns an array containing the match
# and any matched capturing groups.
#
# The first argument is a string or array of strings. The second argument is either a
# regular expression, regular expression represented as a string, or Regex or Pattern
# data type that the function matches against the first argument.
#
# The returned array contains the entire match at index 0, and each captured group at
# subsequent index values. If the value or expression being matched is an array, the
# function returns an array with mapped match results.
#
# If the function doesn't find a match, it returns 'undef'.
#
# @example Matching a regular expression in a string
#
# ```puppet
# $matches = "abc123".match(/[a-z]+[1-9]+/)
# # $matches contains [abc123]
# ```
#
# @example Matching a regular expressions with grouping captures in a string
#
# ```puppet
# $matches = "abc123".match(/([a-z]+)([1-9]+)/)
# # $matches contains [abc123, abc, 123]
# ```
#
# @example Matching a regular expression with grouping captures in an array of strings
#
# ```puppet
# $matches = ["abc123","def456"].match(/([a-z]+)([1-9]+)/)
# # $matches contains [[abc123, abc, 123], [def456, def, 456]]
# ```
#
# @since 4.0.0
#
Puppet::Functions.create_function(:match) do
  dispatch :match do
    param 'String', :string
    param 'Variant[Any, Type]', :pattern
  end

  dispatch :enumerable_match do
    param 'Array[String]', :string
    param 'Variant[Any, Type]', :pattern
  end

  def initialize(closure_scope, loader)
    super

    # Make this visitor shared among all instantiations of this function since it is faster.
    # This can be used because it is not possible to replace
    # a puppet runtime (where this function is) without a reboot. If you model a function in a module after
    # this class, use a regular instance variable instead to enable reloading of the module without reboot
    #
    @@match_visitor ||= Puppet::Pops::Visitor.new(self, "match", 1, 1)
  end

  # Matches given string against given pattern and returns an Array with matches.
  # @param string [String] the string to match
  # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern
  # @return [Array<String>] matches where first match is the entire match, and index 1-n are captures from left to right
  #
  def match(string, pattern)
    @@match_visitor.visit_this_1(self, pattern, string)
  end

  # Matches given Array[String] against given pattern and returns an Array with mapped match results.
  #
  # @param array [Array<String>] the array of strings to match
  # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern
  # @return [Array<Array<String, nil>>] Array with matches (see {#match}), non matching entries produce a nil entry
  #
  def enumerable_match(array, pattern)
    array.map { |s| match(s, pattern) }
  end

  protected

  def match_Object(obj, s)
    msg = _("match() expects pattern of T, where T is String, Regexp, Regexp[r], Pattern[p], or Array[T]. Got %{klass}") % { klass: obj.class }
    raise ArgumentError, msg
  end

  def match_String(pattern_string, s)
    do_match(s, Regexp.new(pattern_string))
  end

  def match_Regexp(regexp, s)
    do_match(s, regexp)
  end

  def match_PTypeAliasType(alias_t, s)
    match(s, alias_t.resolved_type)
  end

  def match_PVariantType(var_t, s)
    # Find first matching type (or error out if one of the variants is not acceptable)
    result = nil
    var_t.types.find { |t| result = match(s, t) }
    result
  end

  def match_PRegexpType(regexp_t, s)
    raise ArgumentError, _("Given Regexp Type has no regular expression") unless regexp_t.pattern

    do_match(s, regexp_t.regexp)
  end

  def match_PPatternType(pattern_t, s)
    # Since we want the actual match result (not just a boolean), an iteration over
    # Pattern's regular expressions is needed. (They are of PRegexpType)
    result = nil
    pattern_t.patterns.find { |pattern| result = match(s, pattern) }
    result
  end

  # Returns the first matching entry
  def match_Array(array, s)
    result = nil
    array.flatten.find { |entry| result = match(s, entry) }
    result
  end

  private

  def do_match(s, regexp)
    result = regexp.match(s)
    result.to_a if result
  end
end