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 { return true in r.comparator_sets.map(it.satisfies(ver)) } 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 { return match c.op { .gt { ver.gt(c.ver) } .lt { ver.lt(c.ver) } .ge { ver.ge(c.ver) } .le { ver.le(c.ver) } .eq { ver.eq(c.ver) } } } 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 raw_version := match true { input.starts_with('>=') { op = .ge input[2..] } input.starts_with('<=') { op = .le input[2..] } input.starts_with('>') { op = .gt input[1..] } input.starts_with('<') { op = .lt input[1..] } input.starts_with('=') { input[1..] } else { 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 {} } } return raw_ver.validate() } 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 } max_ver := if min_ver.minor == 0 && min_ver.patch == 0 { min_ver.increment(.major) } else { 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 } max_ver := if min_ver.major == 0 { min_ver.increment(.minor) } else { 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 } if raw_max_ver.is_missing(ver_minor) { max_ver := raw_max_ver.coerce() or { return none }.increment(.minor) return make_comparator_set_ge_lt(min_ver, max_ver) } max_ver := raw_max_ver.coerce() or { return none } 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 { return ComparatorSet{[Comparator{min_ver, Operator.ge}]} } max_ver := if min_ver.minor == 0 { min_ver.increment(.major) } else { 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 { return ComparatorSet{[ Comparator{min, Operator.ge}, Comparator{max, Operator.lt}, ]} } fn make_comparator_set_ge_le(min Version, max Version) ComparatorSet { return ComparatorSet{[ Comparator{min, Operator.ge}, Comparator{max, Operator.le}, ]} }