module PowerStencil module Utils class SemanticVersion include Comparable MANDATORY_LEVELS = %i(major minor patch).freeze OPTIONAL_LEVEL = :extra class << self def valid_version?(version, raise_error: false) new version true rescue => e raise e if raise_error false end end def initialize(semantic_version) @version = analyze_semantic_version semantic_version end def <=>(other) other_hash = analyze_semantic_version other MANDATORY_LEVELS.each do |level| r = @version[level] <=> other_hash[level] return r unless r == 0 end if @version[OPTIONAL_LEVEL].nil? and other_hash[OPTIONAL_LEVEL] return 1 end unless @version[OPTIONAL_LEVEL].nil? return -1 if other_hash[OPTIONAL_LEVEL].nil? return @version[OPTIONAL_LEVEL] <=> other_hash[OPTIONAL_LEVEL] end 0 end def increment(level = :patch) raise PowerStencil::Error, "'#{level}' is not a valid level !" unless MANDATORY_LEVELS.include? level h = self.to_hash increment_hash(h, level) self.class.new h end alias :succ :increment def increment!(level = :patch) raise PowerStencil::Error, "'#{level}' is not a valid level !" unless MANDATORY_LEVELS.include? level increment_hash(@version, level) self end alias :succ! :increment! def to_s hash_to_version_string @version end def to_hash @version.dup end private def increment_hash(hash, level) hash[level] = hash[level].succ hash[:patch] = 0 if level == :minor if level == :major hash[:minor] = 0 hash[:patch] = 0 end end def analyze_semantic_version(semantic_version) case semantic_version when String semantic_version = '0.0.0' if semantic_version.empty? version_string_to_hash semantic_version when SemanticVersion semantic_version.to_hash when Hash MANDATORY_LEVELS.each do |level| raise_analysis_error semantic_version unless semantic_version.keys.include? level raise_analysis_error semantic_version unless semantic_version[level].is_a? Fixnum end semantic_version.dup when NilClass version_string_to_hash '0.0.0' else raise_analysis_error semantic_version end end def raise_analysis_error(obj) raise PowerStencil::Error, "#{obj.class} cannot be used as semantic version !" end def hash_to_version_string(hash) version_string = MANDATORY_LEVELS.map {|l| hash[l]}.join '.' unless hash[OPTIONAL_LEVEL].nil? version_string << "-#{hash[OPTIONAL_LEVEL]}" end version_string end def version_string_to_hash(version_string) res = {} begin version_string.match(/^(?\d+)\.(?\d+)\.(?\d+)(?:-(?.*))*$/) do |md| res = { major: md['major'].to_i, minor: md['minor'].to_i, patch: md['patch'].to_i, extra: md['extra'], } return res end raise rescue raise PowerStencil::Error, "Invalid semantic version '#{version_string}' !" end res end end end end