module MetadataJsonLint # Parses a string module version requirement with semantic_puppet and # provides methods to analyse it for lint warnings class VersionRequirement def initialize(requirement) @requirement = requirement if defined?(SemanticPuppet::VersionRange) @range = SemanticPuppet::VersionRange.parse(requirement) if @range == SemanticPuppet::VersionRange::EMPTY_RANGE raise ArgumentError, "Range matches no versions: \"#{requirement}\"" end elsif requirement.match(/\A[a-z0-9*.\-^~><=|\t ]*\Z/i).nil? raise ArgumentError, "Unparsable version range: \"#{requirement}\"" end end # Whether the range uses a comparison operator (e.g. >=) with a wildcard # syntax, such as ">= 1.x" or "< 2.0.x" def mixed_syntax? !/ [><=^~]{1,2} # comparison operators \s* \d\. # MAJOR (?: (?:x|\*) # MINOR is wildcard | \d\.(?:x|\*) # MINOR is digit and PATCH is wildcard ) /x.match(requirement).nil? end def open_ended? if range range.end == SemanticPuppet::Version::MAX else # Empty requirement strings are open-ended. return true if requirement.strip.empty? # Strip superfluous whitespace. range_set = requirement.gsub(/([><=~^])(?:\s+|\s*v)/, '\1') # Split on logical OR ranges = range_set.split(/\s*\|\|\s*/) # Returns true if any range includes a '>' but not a corresponding '<' # which should be the only way to declare an open-ended range. ranges.select { |r| r.include?('>') }.any? { |r| !r.include?('<') } end end def puppet_eol? true if range.begin < SemanticPuppet::Version.parse(MIN_PUPPET_VER) end def ver_range range end def min range.begin end def max range.end end private attr_reader :range, :requirement end end