diff options
author | Indrajith K L | 2022-12-03 17:00:20 +0530 |
---|---|---|
committer | Indrajith K L | 2022-12-03 17:00:20 +0530 |
commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/semver | |
download | cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2 cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip |
Diffstat (limited to 'v_windows/v/vlib/semver')
-rw-r--r-- | v_windows/v/vlib/semver/LICENSE.md | 21 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/README.md | 37 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/compare.v | 59 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/parse.v | 85 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/range.v | 252 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/semver.v | 110 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/semver_test.v | 192 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/util.v | 55 | ||||
-rw-r--r-- | v_windows/v/vlib/semver/v.mod | 5 |
9 files changed, 816 insertions, 0 deletions
diff --git a/v_windows/v/vlib/semver/LICENSE.md b/v_windows/v/vlib/semver/LICENSE.md new file mode 100644 index 0000000..8d5ff71 --- /dev/null +++ b/v_windows/v/vlib/semver/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 alexesprit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/vlib/semver/README.md b/v_windows/v/vlib/semver/README.md new file mode 100644 index 0000000..e7c8d20 --- /dev/null +++ b/v_windows/v/vlib/semver/README.md @@ -0,0 +1,37 @@ +# semver + +A library for working with versions in [semver][semver] format. + +## Usage + +```v +import semver + +fn main() { + ver1 := semver.from('1.2.4') or { + println('Invalid version') + return + } + ver2 := semver.from('2.3.4') or { + println('Invalid version') + return + } + println(ver1.gt(ver2)) + println(ver2.gt(ver1)) + println(ver1.satisfies('>=1.1.0 <2.0.0')) + println(ver2.satisfies('>=1.1.0 <2.0.0')) + println(ver2.satisfies('>=1.1.0 <2.0.0 || >2.2.0')) +} +``` + +``` +false +true +true +false +true +``` + +For more details see `semver.v` file. + +[semver]: https://semver.org/ diff --git a/v_windows/v/vlib/semver/compare.v b/v_windows/v/vlib/semver/compare.v new file mode 100644 index 0000000..0b51d29 --- /dev/null +++ b/v_windows/v/vlib/semver/compare.v @@ -0,0 +1,59 @@ +module semver + +// * Private functions. +[inline] +fn version_satisfies(ver Version, input string) bool { + range := parse_range(input) or { return false } + return range.satisfies(ver) +} + +fn compare_eq(v1 Version, v2 Version) bool { + return v1.major == v2.major && v1.minor == v2.minor && v1.patch == v2.patch + && v1.prerelease == v2.prerelease +} + +fn compare_gt(v1 Version, v2 Version) bool { + if v1.major < v2.major { + return false + } + if v1.major > v2.major { + return true + } + if v1.minor < v2.minor { + return false + } + if v1.minor > v2.minor { + return true + } + return v1.patch > v2.patch +} + +fn compare_lt(v1 Version, v2 Version) bool { + if v1.major > v2.major { + return false + } + if v1.major < v2.major { + return true + } + if v1.minor > v2.minor { + return false + } + if v1.minor < v2.minor { + return true + } + return v1.patch < v2.patch +} + +fn compare_ge(v1 Version, v2 Version) bool { + if compare_eq(v1, v2) { + return true + } + return compare_gt(v1, v2) +} + +fn compare_le(v1 Version, v2 Version) bool { + if compare_eq(v1, v2) { + return true + } + return compare_lt(v1, v2) +} diff --git a/v_windows/v/vlib/semver/parse.v b/v_windows/v/vlib/semver/parse.v new file mode 100644 index 0000000..3443f59 --- /dev/null +++ b/v_windows/v/vlib/semver/parse.v @@ -0,0 +1,85 @@ +module semver + +// * Private structs and functions. +struct RawVersion { + prerelease string + metadata string +mut: + raw_ints []string +} + +const ( + ver_major = 0 + ver_minor = 1 + ver_patch = 2 + versions = [ver_major, ver_minor, ver_patch] +) + +// TODO: Rewrite using regexps? +// /(\d+)\.(\d+)\.(\d+)(?:\-([0-9A-Za-z-.]+))?(?:\+([0-9A-Za-z-]+))?/ +fn parse(input string) RawVersion { + mut raw_version := input + mut prerelease := '' + mut metadata := '' + plus_idx := raw_version.last_index('+') or { -1 } + if plus_idx > 0 { + metadata = raw_version[(plus_idx + 1)..] + raw_version = raw_version[0..plus_idx] + } + hyphen_idx := raw_version.index('-') or { -1 } + if hyphen_idx > 0 { + prerelease = raw_version[(hyphen_idx + 1)..] + raw_version = raw_version[0..hyphen_idx] + } + raw_ints := raw_version.split('.') + return RawVersion{ + prerelease: prerelease + metadata: metadata + raw_ints: raw_ints + } +} + +fn (ver RawVersion) is_valid() bool { + if ver.raw_ints.len != 3 { + return false + } + return is_valid_number(ver.raw_ints[semver.ver_major]) + && is_valid_number(ver.raw_ints[semver.ver_minor]) + && is_valid_number(ver.raw_ints[semver.ver_patch]) && is_valid_string(ver.prerelease) + && is_valid_string(ver.metadata) +} + +fn (ver RawVersion) is_missing(typ int) bool { + return typ >= ver.raw_ints.len - 1 +} + +fn (raw_ver RawVersion) coerce() ?Version { + ver := raw_ver.complete() + if !is_valid_number(ver.raw_ints[semver.ver_major]) { + return error('Invalid major version: $ver.raw_ints[ver_major]') + } + return ver.to_version() +} + +fn (raw_ver RawVersion) complete() RawVersion { + mut raw_ints := raw_ver.raw_ints + for raw_ints.len < 3 { + raw_ints << '0' + } + return RawVersion{ + prerelease: raw_ver.prerelease + metadata: raw_ver.metadata + raw_ints: raw_ints + } +} + +fn (raw_ver RawVersion) validate() ?Version { + if !raw_ver.is_valid() { + return none + } + return raw_ver.to_version() +} + +fn (raw_ver RawVersion) to_version() Version { + return Version{raw_ver.raw_ints[semver.ver_major].int(), raw_ver.raw_ints[semver.ver_minor].int(), raw_ver.raw_ints[semver.ver_patch].int(), raw_ver.prerelease, raw_ver.metadata} +} diff --git a/v_windows/v/vlib/semver/range.v b/v_windows/v/vlib/semver/range.v new file mode 100644 index 0000000..fd0a769 --- /dev/null +++ b/v_windows/v/vlib/semver/range.v @@ -0,0 +1,252 @@ +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 InvalidComparatorCountError { + msg string + code int +} + +struct InvalidComparatorFormatError { + msg string + code int +} + +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 IError(&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 IError(&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} +} diff --git a/v_windows/v/vlib/semver/semver.v b/v_windows/v/vlib/semver/semver.v new file mode 100644 index 0000000..c35bbfb --- /dev/null +++ b/v_windows/v/vlib/semver/semver.v @@ -0,0 +1,110 @@ +// * Documentation: https://docs.npmjs.com/misc/semver +module semver + +// * Structures. +// `Version` represents a semantic version in semver format. +pub struct Version { +pub: + major int + minor int + patch int + prerelease string + metadata string +} + +// Increment represents the different types of version increments. +pub enum Increment { + major + minor + patch +} + +struct EmptyInputError { + msg string = 'Empty input' + code int +} + +struct InvalidVersionFormatError { + msg string + code int +} + +// * Constructor. +// from returns a `Version` structure parsed from `input` `string`. +pub fn from(input string) ?Version { + if input.len == 0 { + return IError(&EmptyInputError{}) + } + raw_version := parse(input) + version := raw_version.validate() or { + return IError(&InvalidVersionFormatError{ + msg: 'Invalid version format for input "$input"' + }) + } + return version +} + +// build returns a `Version` structure with given `major`, `minor` and `patch` versions. +pub fn build(major int, minor int, patch int) Version { + // TODO Check if versions are greater than zero. + return Version{major, minor, patch, '', ''} +} + +// * Transformation. +// increment returns a `Version` structure with incremented values. +pub fn (ver Version) increment(typ Increment) Version { + return increment_version(ver, typ) +} + +// * Comparison. +// satisfies returns `true` if the `input` expression can be validated to `true` +// when run against this `Version`. +// Example: assert semver.build(1,0,0).satisfies('<=2.0.0') == true +// Example: assert semver.build(1,0,0).satisfies('>=2.0.0') == false +pub fn (ver Version) satisfies(input string) bool { + return version_satisfies(ver, input) +} + +// eq returns `true` if `v1` is equal to `v2`. +pub fn (v1 Version) eq(v2 Version) bool { + return compare_eq(v1, v2) +} + +// gt returns `true` if `v1` is greater than `v2`. +pub fn (v1 Version) gt(v2 Version) bool { + return compare_gt(v1, v2) +} + +// lt returns `true` if `v1` is less than `v2`. +pub fn (v1 Version) lt(v2 Version) bool { + return compare_lt(v1, v2) +} + +// ge returns `true` if `v1` is greater than or equal to `v2`. +pub fn (v1 Version) ge(v2 Version) bool { + return compare_ge(v1, v2) +} + +// le returns `true` if `v1` is less than or equal to `v2`. +pub fn (v1 Version) le(v2 Version) bool { + return compare_le(v1, v2) +} + +// * Utilites. +// coerce converts the `input` version to a `Version` struct. +// coerce will strip any contents *after* the parsed version string: +/* +Example: +import semver +v := semver.coerce('1.3-RC1-b2') or { semver.Version{} } +assert v.satisfies('>1.0 <2.0') == true // 1.3.0 +*/ +pub fn coerce(input string) ?Version { + return coerce_version(input) +} + +// is_valid returns `true` if the `input` `string` can be converted to +// a (semantic) `Version` struct. +pub fn is_valid(input string) bool { + return is_version_valid(input) +} diff --git a/v_windows/v/vlib/semver/semver_test.v b/v_windows/v/vlib/semver/semver_test.v new file mode 100644 index 0000000..77c8cd7 --- /dev/null +++ b/v_windows/v/vlib/semver/semver_test.v @@ -0,0 +1,192 @@ +import semver + +struct TestVersion { + raw string + major int + minor int + patch int + prerelease string + metadata string +} + +struct TestRange { + raw_version string + range_satisfied string + range_unsatisfied string +} + +struct TestCoerce { + invalid string + valid string +} + +const ( + versions_to_test = [ + TestVersion{'1.2.4', 1, 2, 4, '', ''}, + TestVersion{'1.2.4-prerelease-1', 1, 2, 4, 'prerelease-1', ''}, + TestVersion{'1.2.4+20191231', 1, 2, 4, '', '20191231'}, + TestVersion{'1.2.4-prerelease-1+20191231', 1, 2, 4, 'prerelease-1', '20191231'}, + TestVersion{'1.2.4+20191231-prerelease-1', 1, 2, 4, '', '20191231-prerelease-1'}, + ] + ranges_to_test = [ + TestRange{'1.1.0', '1.1.0', '1.1.1'}, + TestRange{'1.1.0', '=1.1.0', '=1.1.1'}, + TestRange{'1.1.0', '>=1.0.0', '<1.1.0'}, + TestRange{'1.1.0', '>=1.0.0 <=1.1.0', '>=1.0.0 <1.1.0'}, + TestRange{'2.3.1', '>=1.0.0 <=1.1.0 || >2.0.0 <2.3.4', '>=1.0.0 <1.1.0'}, + TestRange{'2.3.1', '>=1.0.0 <=1.1.0 || >2.0.0 <2.3.4', '>=1.0.0 <1.1.0 || >4.0.0 <5.0.0'}, + TestRange{'2.3.1', '~2.3.0', '~2.4.0'}, + TestRange{'3.0.0', '~3.0.0', '~4.0.0'}, + TestRange{'2.3.1', '^2.0.0', '^2.4.0'}, + TestRange{'0.3.1', '^0.3.0', '^2.4.0'}, + TestRange{'0.0.4', '^0.0.1', '^0.1.0'}, + TestRange{'2.3.4', '^0.0.1 || ^2.3.0', '^3.1.0 || ^4.2.0'}, + TestRange{'2.3.4', '>2 || <3', '>3 || >4'}, + TestRange{'2.3.4', '2.3.4 - 2.3.5', '2.5.1 - 2.8.3'}, + TestRange{'2.3.4', '2.2 - 2.3', '2.4 - 2.8'}, + TestRange{'2.3.4', '2.3.x', '2.4.x'}, + TestRange{'2.3.4', '2.x', '3.x'}, + TestRange{'2.3.4', '*', '3.x'}, + ] + coerce_to_test = [ + TestCoerce{'1.2.0.4', '1.2.0'}, + TestCoerce{'1.2.0', '1.2.0'}, + TestCoerce{'1.2', '1.2.0'}, + TestCoerce{'1', '1.0.0'}, + TestCoerce{'1-alpha', '1.0.0-alpha'}, + TestCoerce{'1+meta', '1.0.0+meta'}, + TestCoerce{'1-alpha+meta', '1.0.0-alpha+meta'}, + ] + invalid_versions_to_test = [ + 'a.b.c', + '1.2', + '1.2.x', + '1.2.3.4', + '1.2.3-alpha@', + '1.2.3+meta%', + ] + invalid_ranges_to_test = [ + '^a', + '~b', + 'a - c', + '>a', + 'a', + 'a.x', + ] +) + +fn test_from() { + for item in versions_to_test { + ver := semver.from(item.raw) or { + assert false + return + } + assert ver.major == item.major + assert ver.minor == item.minor + assert ver.patch == item.patch + assert ver.metadata == item.metadata + assert ver.prerelease == item.prerelease + } + for ver in invalid_versions_to_test { + semver.from(ver) or { + assert true + continue + } + assert false + } +} + +fn test_increment() { + version1 := semver.build(1, 2, 3) + version1_inc := version1.increment(.major) + assert version1_inc.major == 2 + assert version1_inc.minor == 0 + assert version1_inc.patch == 0 + version2_inc := version1.increment(.minor) + assert version2_inc.major == 1 + assert version2_inc.minor == 3 + assert version2_inc.patch == 0 + version3_inc := version1.increment(.patch) + assert version3_inc.major == 1 + assert version3_inc.minor == 2 + assert version3_inc.patch == 4 +} + +fn test_compare() { + first := semver.build(1, 0, 0) + patch := semver.build(1, 0, 1) + minor := semver.build(1, 2, 3) + major := semver.build(2, 0, 0) + assert first.le(first) + assert first.ge(first) + assert !first.lt(first) + assert !first.gt(first) + assert patch.ge(first) + assert first.le(patch) + assert !first.ge(patch) + assert !patch.le(first) + assert patch.gt(first) + assert first.lt(patch) + assert !first.gt(patch) + assert !patch.lt(first) + assert minor.gt(patch) + assert patch.lt(minor) + assert !patch.gt(minor) + assert !minor.lt(patch) + assert major.gt(minor) + assert minor.lt(major) + assert !minor.gt(major) + assert !major.lt(minor) +} + +fn test_satisfies() { + for item in ranges_to_test { + ver := semver.from(item.raw_version) or { + assert false + return + } + assert ver.satisfies(item.range_satisfied) + assert !ver.satisfies(item.range_unsatisfied) + } +} + +fn test_satisfies_invalid() { + ver := semver.from('1.0.0') or { + assert false + return + } + for item in invalid_ranges_to_test { + assert ver.satisfies(item) == false + } +} + +fn test_coerce() { + for item in coerce_to_test { + valid := semver.from(item.valid) or { + assert false + return + } + fixed := semver.coerce(item.invalid) or { + assert false + return + } + assert fixed.eq(valid) + } +} + +fn test_coerce_invalid() { + semver.coerce('a') or { + assert true + return + } + assert false +} + +fn test_is_valid() { + for item in versions_to_test { + assert semver.is_valid(item.raw) + } + for item in invalid_versions_to_test { + assert semver.is_valid(item) == false + } +} diff --git a/v_windows/v/vlib/semver/util.v b/v_windows/v/vlib/semver/util.v new file mode 100644 index 0000000..142ce19 --- /dev/null +++ b/v_windows/v/vlib/semver/util.v @@ -0,0 +1,55 @@ +module semver + +// * Private functions. +[inline] +fn is_version_valid(input string) bool { + raw_ver := parse(input) + return raw_ver.is_valid() +} + +[inline] +fn coerce_version(input string) ?Version { + raw_ver := parse(input) + ver := raw_ver.coerce() or { return error('Invalid version for input "$input"') } + return ver +} + +[inline] +fn increment_version(ver Version, typ Increment) Version { + mut major := ver.major + mut minor := ver.minor + mut patch := ver.patch + match typ { + .major { + major++ + minor = 0 + patch = 0 + } + .minor { + minor++ + patch = 0 + } + .patch { + patch++ + } + } + return Version{major, minor, patch, ver.prerelease, ver.metadata} +} + +fn is_valid_string(input string) bool { + for c in input { + if !(c.is_letter() || c.is_digit() || c == `.` || c == `-`) { + return false + } + } + return true +} + +fn is_valid_number(input string) bool { + for c in input { + if !c.is_digit() { + return false + } + } + return true +} diff --git a/v_windows/v/vlib/semver/v.mod b/v_windows/v/vlib/semver/v.mod new file mode 100644 index 0000000..dd7671c --- /dev/null +++ b/v_windows/v/vlib/semver/v.mod @@ -0,0 +1,5 @@ +Module { + name: 'semver' + version: '0.3.0' + deps: [] +} |