module semver // * Private functions. const ( comparator_sep = ' ' comparator_set_sep = ' || ' hyphen_range_sep = ' - ' x_range_symbols = 'Xx*' ) enum Operator { gt lt ge le eq } struct Comparator { ver Version op Operator } struct ComparatorSet { comparators []Comparator } struct Range { comparator_sets []ComparatorSet } struct InvalidComparatorFormatError { MessageError } fn (r Range) satisfies(ver Version) bool { mut final_result := false for set in r.comparator_sets { final_result = final_result || set.satisfies(ver) } return final_result } fn (set ComparatorSet) satisfies(ver Version) bool { for comp in set.comparators { if !comp.satisfies(ver) { return false } } return true } fn (c Comparator) satisfies(ver Version) bool { if c.op == .gt { return ver.gt(c.ver) } if c.op == .lt { return ver.lt(c.ver) } if c.op == .ge { return ver.ge(c.ver) } if c.op == .le { return ver.le(c.ver) } if c.op == .eq { return ver.eq(c.ver) } return false } fn parse_range(input string) ?Range { raw_comparator_sets := input.split(semver.comparator_set_sep) mut comparator_sets := []ComparatorSet{} for raw_comp_set in raw_comparator_sets { if can_expand(raw_comp_set) { s := expand_comparator_set(raw_comp_set) or { return err } comparator_sets << s } else { s := parse_comparator_set(raw_comp_set) or { return err } comparator_sets << s } } return Range{comparator_sets} } fn parse_comparator_set(input string) ?ComparatorSet { raw_comparators := input.split(semver.comparator_sep) if raw_comparators.len > 2 { return &InvalidComparatorFormatError{ msg: 'Invalid format of comparator set for input "$input"' } } mut comparators := []Comparator{} for raw_comp in raw_comparators { c := parse_comparator(raw_comp) or { return &InvalidComparatorFormatError{ msg: 'Invalid comparator "$raw_comp" in input "$input"' } } comparators << c } return ComparatorSet{comparators} } fn parse_comparator(input string) ?Comparator { mut op := Operator.eq mut raw_version := '' if input.starts_with('>=') { op = .ge raw_version = input[2..] } else if input.starts_with('<=') { op = .le raw_version = input[2..] } else if input.starts_with('>') { op = .gt raw_version = input[1..] } else if input.starts_with('<') { op = .lt raw_version = input[1..] } else if input.starts_with('=') { raw_version = input[1..] } else { raw_version = input } version := coerce_version(raw_version) or { return none } return Comparator{version, op} } fn parse_xrange(input string) ?Version { mut raw_ver := parse(input).complete() for typ in versions { if raw_ver.raw_ints[typ].index_any(semver.x_range_symbols) == -1 { continue } match typ { ver_major { raw_ver.raw_ints[ver_major] = '0' raw_ver.raw_ints[ver_minor] = '0' raw_ver.raw_ints[ver_patch] = '0' } ver_minor { raw_ver.raw_ints[ver_minor] = '0' raw_ver.raw_ints[ver_patch] = '0' } ver_patch { raw_ver.raw_ints[ver_patch] = '0' } else {} } } if !raw_ver.is_valid() { return none } return raw_ver.to_version() } fn can_expand(input string) bool { return input[0] == `~` || input[0] == `^` || input.contains(semver.hyphen_range_sep) || input.index_any(semver.x_range_symbols) > -1 } fn expand_comparator_set(input string) ?ComparatorSet { match input[0] { `~` { return expand_tilda(input[1..]) } `^` { return expand_caret(input[1..]) } else {} } if input.contains(semver.hyphen_range_sep) { return expand_hyphen(input) } return expand_xrange(input) } fn expand_tilda(raw_version string) ?ComparatorSet { min_ver := coerce_version(raw_version) or { return none } mut max_ver := min_ver if min_ver.minor == 0 && min_ver.patch == 0 { max_ver = min_ver.increment(.major) } else { max_ver = min_ver.increment(.minor) } return make_comparator_set_ge_lt(min_ver, max_ver) } fn expand_caret(raw_version string) ?ComparatorSet { min_ver := coerce_version(raw_version) or { return none } mut max_ver := min_ver if min_ver.major == 0 { max_ver = min_ver.increment(.minor) } else { max_ver = min_ver.increment(.major) } return make_comparator_set_ge_lt(min_ver, max_ver) } fn expand_hyphen(raw_range string) ?ComparatorSet { raw_versions := raw_range.split(semver.hyphen_range_sep) if raw_versions.len != 2 { return none } min_ver := coerce_version(raw_versions[0]) or { return none } raw_max_ver := parse(raw_versions[1]) if raw_max_ver.is_missing(ver_major) { return none } mut max_ver := raw_max_ver.coerce() or { return none } if raw_max_ver.is_missing(ver_minor) { max_ver = max_ver.increment(.minor) return make_comparator_set_ge_lt(min_ver, max_ver) } return make_comparator_set_ge_le(min_ver, max_ver) } fn expand_xrange(raw_range string) ?ComparatorSet { min_ver := parse_xrange(raw_range) or { return none } if min_ver.major == 0 { comparators := [ Comparator{min_ver, Operator.ge}, ] return ComparatorSet{comparators} } mut max_ver := min_ver if min_ver.minor == 0 { max_ver = min_ver.increment(.major) } else { max_ver = min_ver.increment(.minor) } return make_comparator_set_ge_lt(min_ver, max_ver) } fn make_comparator_set_ge_lt(min Version, max Version) ComparatorSet { comparators := [ Comparator{min, Operator.ge}, Comparator{max, Operator.lt}, ] return ComparatorSet{comparators} } fn make_comparator_set_ge_le(min Version, max Version) ComparatorSet { comparators := [ Comparator{min, Operator.ge}, Comparator{max, Operator.le}, ] return ComparatorSet{comparators} }