Bump github.com/hashicorp/terraform-plugin-docs from 0.7.0 to 0.13.0
Bumps [github.com/hashicorp/terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs) from 0.7.0 to 0.13.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-docs/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-docs/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-docs/compare/v0.7.0...v0.13.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-docs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>main
parent
ad07770b6b
commit
b4859cda6b
@ -1,29 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
# Setting sudo access to false will let Travis CI use containers rather than
|
|
||||||
# VMs to run the tests. For more details see:
|
|
||||||
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
|
||||||
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make setup
|
|
||||||
- make test
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
webhooks:
|
|
||||||
urls:
|
|
||||||
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
|
|
||||||
on_success: change # options: [always|never|change] default: always
|
|
||||||
on_failure: always # options: [always|never|change] default: always
|
|
||||||
on_start: never # options: [always|never|change] default: always
|
|
@ -1,109 +0,0 @@
|
|||||||
# 1.5.0 (2019-09-11)
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil)
|
|
||||||
- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil)
|
|
||||||
- #72: Adding docs comment pointing to vert for a cli
|
|
||||||
- #71: Update the docs on pre-release comparator handling
|
|
||||||
- #89: Test with new go versions (thanks @thedevsaddam)
|
|
||||||
- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- #78: Fix unchecked error in example code (thanks @ravron)
|
|
||||||
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
|
||||||
- #97: Fixed copyright file for proper display on GitHub
|
|
||||||
- #107: Fix handling prerelease when sorting alphanum and num
|
|
||||||
- #109: Fixed where Validate sometimes returns wrong message on error
|
|
||||||
|
|
||||||
# 1.4.2 (2018-04-10)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
- #72: Updated the docs to point to vert for a console appliaction
|
|
||||||
- #71: Update the docs on pre-release comparator handling
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
|
||||||
|
|
||||||
# 1.4.1 (2018-04-02)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- Fixed #64: Fix pre-release precedence issue (thanks @uudashr)
|
|
||||||
|
|
||||||
# 1.4.0 (2017-10-04)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill)
|
|
||||||
|
|
||||||
# 1.3.1 (2017-07-10)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- Fixed #57: number comparisons in prerelease sometimes inaccurate
|
|
||||||
|
|
||||||
# 1.3.0 (2017-05-02)
|
|
||||||
|
|
||||||
## Added
|
|
||||||
- #45: Added json (un)marshaling support (thanks @mh-cbon)
|
|
||||||
- Stability marker. See https://masterminds.github.io/stability/
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- #51: Fix handling of single digit tilde constraint (thanks @dgodd)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
- #55: The godoc icon moved from png to svg
|
|
||||||
|
|
||||||
# 1.2.3 (2017-04-03)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- #46: Fixed 0.x.x and 0.0.x in constraints being treated as *
|
|
||||||
|
|
||||||
# Release 1.2.2 (2016-12-13)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- #34: Fixed issue where hyphen range was not working with pre-release parsing.
|
|
||||||
|
|
||||||
# Release 1.2.1 (2016-11-28)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha"
|
|
||||||
properly.
|
|
||||||
|
|
||||||
# Release 1.2.0 (2016-11-04)
|
|
||||||
|
|
||||||
## Added
|
|
||||||
- #20: Added MustParse function for versions (thanks @adamreese)
|
|
||||||
- #15: Added increment methods on versions (thanks @mh-cbon)
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and
|
|
||||||
might not satisfy the intended compatibility. The change here ignores pre-releases
|
|
||||||
on constraint checks (e.g., ~ or ^) when a pre-release is not part of the
|
|
||||||
constraint. For example, `^1.2.3` will ignore pre-releases while
|
|
||||||
`^1.2.3-alpha` will include them.
|
|
||||||
|
|
||||||
# Release 1.1.1 (2016-06-30)
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
- Issue #9: Speed up version comparison performance (thanks @sdboyer)
|
|
||||||
- Issue #8: Added benchmarks (thanks @sdboyer)
|
|
||||||
- Updated Go Report Card URL to new location
|
|
||||||
- Updated Readme to add code snippet formatting (thanks @mh-cbon)
|
|
||||||
- Updating tagging to v[SemVer] structure for compatibility with other tools.
|
|
||||||
|
|
||||||
# Release 1.1.0 (2016-03-11)
|
|
||||||
|
|
||||||
- Issue #2: Implemented validation to provide reasons a versions failed a
|
|
||||||
constraint.
|
|
||||||
|
|
||||||
# Release 1.0.1 (2015-12-31)
|
|
||||||
|
|
||||||
- Fixed #1: * constraint failing on valid versions.
|
|
||||||
|
|
||||||
# Release 1.0.0 (2015-10-20)
|
|
||||||
|
|
||||||
- Initial release
|
|
@ -1,36 +0,0 @@
|
|||||||
.PHONY: setup
|
|
||||||
setup:
|
|
||||||
go get -u gopkg.in/alecthomas/gometalinter.v1
|
|
||||||
gometalinter.v1 --install
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test: validate lint
|
|
||||||
@echo "==> Running tests"
|
|
||||||
go test -v
|
|
||||||
|
|
||||||
.PHONY: validate
|
|
||||||
validate:
|
|
||||||
@echo "==> Running static validations"
|
|
||||||
@gometalinter.v1 \
|
|
||||||
--disable-all \
|
|
||||||
--enable deadcode \
|
|
||||||
--severity deadcode:error \
|
|
||||||
--enable gofmt \
|
|
||||||
--enable gosimple \
|
|
||||||
--enable ineffassign \
|
|
||||||
--enable misspell \
|
|
||||||
--enable vet \
|
|
||||||
--tests \
|
|
||||||
--vendor \
|
|
||||||
--deadline 60s \
|
|
||||||
./... || exit_code=1
|
|
||||||
|
|
||||||
.PHONY: lint
|
|
||||||
lint:
|
|
||||||
@echo "==> Running linters"
|
|
||||||
@gometalinter.v1 \
|
|
||||||
--disable-all \
|
|
||||||
--enable golint \
|
|
||||||
--vendor \
|
|
||||||
--deadline 60s \
|
|
||||||
./... || :
|
|
@ -1,194 +0,0 @@
|
|||||||
# SemVer
|
|
||||||
|
|
||||||
The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
|
|
||||||
|
|
||||||
* Parse semantic versions
|
|
||||||
* Sort semantic versions
|
|
||||||
* Check if a semantic version fits within a set of constraints
|
|
||||||
* Optionally work with a `v` prefix
|
|
||||||
|
|
||||||
[![Stability:
|
|
||||||
Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)
|
|
||||||
[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver)
|
|
||||||
|
|
||||||
If you are looking for a command line tool for version comparisons please see
|
|
||||||
[vert](https://github.com/Masterminds/vert) which uses this library.
|
|
||||||
|
|
||||||
## Parsing Semantic Versions
|
|
||||||
|
|
||||||
To parse a semantic version use the `NewVersion` function. For example,
|
|
||||||
|
|
||||||
```go
|
|
||||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
|
||||||
```
|
|
||||||
|
|
||||||
If there is an error the version wasn't parseable. The version object has methods
|
|
||||||
to get the parts of the version, compare it to other versions, convert the
|
|
||||||
version back into a string, and get the original string. For more details
|
|
||||||
please see the [documentation](https://godoc.org/github.com/Masterminds/semver).
|
|
||||||
|
|
||||||
## Sorting Semantic Versions
|
|
||||||
|
|
||||||
A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/)
|
|
||||||
package from the standard library. For example,
|
|
||||||
|
|
||||||
```go
|
|
||||||
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
|
||||||
vs := make([]*semver.Version, len(raw))
|
|
||||||
for i, r := range raw {
|
|
||||||
v, err := semver.NewVersion(r)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing version: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vs[i] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(semver.Collection(vs))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Checking Version Constraints
|
|
||||||
|
|
||||||
Checking a version against version constraints is one of the most featureful
|
|
||||||
parts of the package.
|
|
||||||
|
|
||||||
```go
|
|
||||||
c, err := semver.NewConstraint(">= 1.2.3")
|
|
||||||
if err != nil {
|
|
||||||
// Handle constraint not being parseable.
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := semver.NewVersion("1.3")
|
|
||||||
if err != nil {
|
|
||||||
// Handle version not being parseable.
|
|
||||||
}
|
|
||||||
// Check if the version meets the constraints. The a variable will be true.
|
|
||||||
a := c.Check(v)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Comparisons
|
|
||||||
|
|
||||||
There are two elements to the comparisons. First, a comparison string is a list
|
|
||||||
of comma separated and comparisons. These are then separated by || separated or
|
|
||||||
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
|
|
||||||
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
|
||||||
greater than or equal to 4.2.3.
|
|
||||||
|
|
||||||
The basic comparisons are:
|
|
||||||
|
|
||||||
* `=`: equal (aliased to no operator)
|
|
||||||
* `!=`: not equal
|
|
||||||
* `>`: greater than
|
|
||||||
* `<`: less than
|
|
||||||
* `>=`: greater than or equal to
|
|
||||||
* `<=`: less than or equal to
|
|
||||||
|
|
||||||
## Working With Pre-release Versions
|
|
||||||
|
|
||||||
Pre-releases, for those not familiar with them, are used for software releases
|
|
||||||
prior to stable or generally available releases. Examples of pre-releases include
|
|
||||||
development, alpha, beta, and release candidate releases. A pre-release may be
|
|
||||||
a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
|
|
||||||
order of precidence, pre-releases come before their associated releases. In this
|
|
||||||
example `1.2.3-beta.1 < 1.2.3`.
|
|
||||||
|
|
||||||
According to the Semantic Version specification pre-releases may not be
|
|
||||||
API compliant with their release counterpart. It says,
|
|
||||||
|
|
||||||
> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
|
|
||||||
|
|
||||||
SemVer comparisons without a pre-release comparator will skip pre-release versions.
|
|
||||||
For example, `>=1.2.3` will skip pre-releases when looking at a list of releases
|
|
||||||
while `>=1.2.3-0` will evaluate and find pre-releases.
|
|
||||||
|
|
||||||
The reason for the `0` as a pre-release version in the example comparison is
|
|
||||||
because pre-releases can only contain ASCII alphanumerics and hyphens (along with
|
|
||||||
`.` separators), per the spec. Sorting happens in ASCII sort order, again per the spec. The lowest character is a `0` in ASCII sort order (see an [ASCII Table](http://www.asciitable.com/))
|
|
||||||
|
|
||||||
Understanding ASCII sort ordering is important because A-Z comes before a-z. That
|
|
||||||
means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
|
|
||||||
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
|
|
||||||
the spec specifies.
|
|
||||||
|
|
||||||
## Hyphen Range Comparisons
|
|
||||||
|
|
||||||
There are multiple methods to handle ranges and the first is hyphens ranges.
|
|
||||||
These look like:
|
|
||||||
|
|
||||||
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
|
||||||
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
|
|
||||||
|
|
||||||
## Wildcards In Comparisons
|
|
||||||
|
|
||||||
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
|
||||||
for all comparison operators. When used on the `=` operator it falls
|
|
||||||
back to the pack level comparison (see tilde below). For example,
|
|
||||||
|
|
||||||
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
|
||||||
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
|
||||||
* `<= 2.x` is equivalent to `< 3`
|
|
||||||
* `*` is equivalent to `>= 0.0.0`
|
|
||||||
|
|
||||||
## Tilde Range Comparisons (Patch)
|
|
||||||
|
|
||||||
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
|
||||||
version is specified and major level changes when the minor number is missing.
|
|
||||||
For example,
|
|
||||||
|
|
||||||
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
|
||||||
* `~1` is equivalent to `>= 1, < 2`
|
|
||||||
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
|
||||||
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
|
||||||
* `~1.x` is equivalent to `>= 1, < 2`
|
|
||||||
|
|
||||||
## Caret Range Comparisons (Major)
|
|
||||||
|
|
||||||
The caret (`^`) comparison operator is for major level changes. This is useful
|
|
||||||
when comparisons of API versions as a major change is API breaking. For example,
|
|
||||||
|
|
||||||
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
|
||||||
* `^0.0.1` is equivalent to `>= 0.0.1, < 1.0.0`
|
|
||||||
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
|
||||||
* `^2.3` is equivalent to `>= 2.3, < 3`
|
|
||||||
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
|
||||||
|
|
||||||
# Validation
|
|
||||||
|
|
||||||
In addition to testing a version against a constraint, a version can be validated
|
|
||||||
against a constraint. When validation fails a slice of errors containing why a
|
|
||||||
version didn't meet the constraint is returned. For example,
|
|
||||||
|
|
||||||
```go
|
|
||||||
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
|
||||||
if err != nil {
|
|
||||||
// Handle constraint not being parseable.
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := semver.NewVersion("1.3")
|
|
||||||
if err != nil {
|
|
||||||
// Handle version not being parseable.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate a version against a constraint.
|
|
||||||
a, msgs := c.Validate(v)
|
|
||||||
// a is false
|
|
||||||
for _, m := range msgs {
|
|
||||||
fmt.Println(m)
|
|
||||||
|
|
||||||
// Loops over the errors which would read
|
|
||||||
// "1.3 is greater than 1.2.3"
|
|
||||||
// "1.3 is less than 1.4"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Fuzzing
|
|
||||||
|
|
||||||
[dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz) is used for fuzzing.
|
|
||||||
|
|
||||||
1. `go-fuzz-build`
|
|
||||||
2. `go-fuzz -workdir=fuzz`
|
|
||||||
|
|
||||||
# Contribute
|
|
||||||
|
|
||||||
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
|
|
||||||
or [create a pull request](https://github.com/Masterminds/semver/pulls).
|
|
@ -1,44 +0,0 @@
|
|||||||
version: build-{build}.{branch}
|
|
||||||
|
|
||||||
clone_folder: C:\gopath\src\github.com\Masterminds\semver
|
|
||||||
shallow_clone: true
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: C:\gopath
|
|
||||||
|
|
||||||
platform:
|
|
||||||
- x64
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
- go get -u gopkg.in/alecthomas/gometalinter.v1
|
|
||||||
- set PATH=%PATH%;%GOPATH%\bin
|
|
||||||
- gometalinter.v1.exe --install
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- go install -v ./...
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- "gometalinter.v1 \
|
|
||||||
--disable-all \
|
|
||||||
--enable deadcode \
|
|
||||||
--severity deadcode:error \
|
|
||||||
--enable gofmt \
|
|
||||||
--enable gosimple \
|
|
||||||
--enable ineffassign \
|
|
||||||
--enable misspell \
|
|
||||||
--enable vet \
|
|
||||||
--tests \
|
|
||||||
--vendor \
|
|
||||||
--deadline 60s \
|
|
||||||
./... || exit_code=1"
|
|
||||||
- "gometalinter.v1 \
|
|
||||||
--disable-all \
|
|
||||||
--enable golint \
|
|
||||||
--vendor \
|
|
||||||
--deadline 60s \
|
|
||||||
./... || :"
|
|
||||||
- go test -v
|
|
||||||
|
|
||||||
deploy: off
|
|
@ -1,423 +0,0 @@
|
|||||||
package semver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Constraints is one or more constraint that a semantic version can be
|
|
||||||
// checked against.
|
|
||||||
type Constraints struct {
|
|
||||||
constraints [][]*constraint
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConstraint returns a Constraints instance that a Version instance can
|
|
||||||
// be checked against. If there is a parse error it will be returned.
|
|
||||||
func NewConstraint(c string) (*Constraints, error) {
|
|
||||||
|
|
||||||
// Rewrite - ranges into a comparison operation.
|
|
||||||
c = rewriteRange(c)
|
|
||||||
|
|
||||||
ors := strings.Split(c, "||")
|
|
||||||
or := make([][]*constraint, len(ors))
|
|
||||||
for k, v := range ors {
|
|
||||||
cs := strings.Split(v, ",")
|
|
||||||
result := make([]*constraint, len(cs))
|
|
||||||
for i, s := range cs {
|
|
||||||
pc, err := parseConstraint(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result[i] = pc
|
|
||||||
}
|
|
||||||
or[k] = result
|
|
||||||
}
|
|
||||||
|
|
||||||
o := &Constraints{constraints: or}
|
|
||||||
return o, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check tests if a version satisfies the constraints.
|
|
||||||
func (cs Constraints) Check(v *Version) bool {
|
|
||||||
// loop over the ORs and check the inner ANDs
|
|
||||||
for _, o := range cs.constraints {
|
|
||||||
joy := true
|
|
||||||
for _, c := range o {
|
|
||||||
if !c.check(v) {
|
|
||||||
joy = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if joy {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks if a version satisfies a constraint. If not a slice of
|
|
||||||
// reasons for the failure are returned in addition to a bool.
|
|
||||||
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
|
||||||
// loop over the ORs and check the inner ANDs
|
|
||||||
var e []error
|
|
||||||
|
|
||||||
// Capture the prerelease message only once. When it happens the first time
|
|
||||||
// this var is marked
|
|
||||||
var prerelesase bool
|
|
||||||
for _, o := range cs.constraints {
|
|
||||||
joy := true
|
|
||||||
for _, c := range o {
|
|
||||||
// Before running the check handle the case there the version is
|
|
||||||
// a prerelease and the check is not searching for prereleases.
|
|
||||||
if c.con.pre == "" && v.pre != "" {
|
|
||||||
if !prerelesase {
|
|
||||||
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
|
||||||
e = append(e, em)
|
|
||||||
prerelesase = true
|
|
||||||
}
|
|
||||||
joy = false
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if !c.check(v) {
|
|
||||||
em := fmt.Errorf(c.msg, v, c.orig)
|
|
||||||
e = append(e, em)
|
|
||||||
joy = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if joy {
|
|
||||||
return true, []error{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, e
|
|
||||||
}
|
|
||||||
|
|
||||||
var constraintOps map[string]cfunc
|
|
||||||
var constraintMsg map[string]string
|
|
||||||
var constraintRegex *regexp.Regexp
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
constraintOps = map[string]cfunc{
|
|
||||||
"": constraintTildeOrEqual,
|
|
||||||
"=": constraintTildeOrEqual,
|
|
||||||
"!=": constraintNotEqual,
|
|
||||||
">": constraintGreaterThan,
|
|
||||||
"<": constraintLessThan,
|
|
||||||
">=": constraintGreaterThanEqual,
|
|
||||||
"=>": constraintGreaterThanEqual,
|
|
||||||
"<=": constraintLessThanEqual,
|
|
||||||
"=<": constraintLessThanEqual,
|
|
||||||
"~": constraintTilde,
|
|
||||||
"~>": constraintTilde,
|
|
||||||
"^": constraintCaret,
|
|
||||||
}
|
|
||||||
|
|
||||||
constraintMsg = map[string]string{
|
|
||||||
"": "%s is not equal to %s",
|
|
||||||
"=": "%s is not equal to %s",
|
|
||||||
"!=": "%s is equal to %s",
|
|
||||||
">": "%s is less than or equal to %s",
|
|
||||||
"<": "%s is greater than or equal to %s",
|
|
||||||
">=": "%s is less than %s",
|
|
||||||
"=>": "%s is less than %s",
|
|
||||||
"<=": "%s is greater than %s",
|
|
||||||
"=<": "%s is greater than %s",
|
|
||||||
"~": "%s does not have same major and minor version as %s",
|
|
||||||
"~>": "%s does not have same major and minor version as %s",
|
|
||||||
"^": "%s does not have same major version as %s",
|
|
||||||
}
|
|
||||||
|
|
||||||
ops := make([]string, 0, len(constraintOps))
|
|
||||||
for k := range constraintOps {
|
|
||||||
ops = append(ops, regexp.QuoteMeta(k))
|
|
||||||
}
|
|
||||||
|
|
||||||
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
|
||||||
`^\s*(%s)\s*(%s)\s*$`,
|
|
||||||
strings.Join(ops, "|"),
|
|
||||||
cvRegex))
|
|
||||||
|
|
||||||
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
|
||||||
`\s*(%s)\s+-\s+(%s)\s*`,
|
|
||||||
cvRegex, cvRegex))
|
|
||||||
}
|
|
||||||
|
|
||||||
// An individual constraint
|
|
||||||
type constraint struct {
|
|
||||||
// The callback function for the restraint. It performs the logic for
|
|
||||||
// the constraint.
|
|
||||||
function cfunc
|
|
||||||
|
|
||||||
msg string
|
|
||||||
|
|
||||||
// The version used in the constraint check. For example, if a constraint
|
|
||||||
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
|
||||||
con *Version
|
|
||||||
|
|
||||||
// The original parsed version (e.g., 4.x from != 4.x)
|
|
||||||
orig string
|
|
||||||
|
|
||||||
// When an x is used as part of the version (e.g., 1.x)
|
|
||||||
minorDirty bool
|
|
||||||
dirty bool
|
|
||||||
patchDirty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a version meets the constraint
|
|
||||||
func (c *constraint) check(v *Version) bool {
|
|
||||||
return c.function(v, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
type cfunc func(v *Version, c *constraint) bool
|
|
||||||
|
|
||||||
func parseConstraint(c string) (*constraint, error) {
|
|
||||||
m := constraintRegex.FindStringSubmatch(c)
|
|
||||||
if m == nil {
|
|
||||||
return nil, fmt.Errorf("improper constraint: %s", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
ver := m[2]
|
|
||||||
orig := ver
|
|
||||||
minorDirty := false
|
|
||||||
patchDirty := false
|
|
||||||
dirty := false
|
|
||||||
if isX(m[3]) {
|
|
||||||
ver = "0.0.0"
|
|
||||||
dirty = true
|
|
||||||
} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
|
|
||||||
minorDirty = true
|
|
||||||
dirty = true
|
|
||||||
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
|
||||||
} else if isX(strings.TrimPrefix(m[5], ".")) {
|
|
||||||
dirty = true
|
|
||||||
patchDirty = true
|
|
||||||
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
|
||||||
}
|
|
||||||
|
|
||||||
con, err := NewVersion(ver)
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
// The constraintRegex should catch any regex parsing errors. So,
|
|
||||||
// we should never get here.
|
|
||||||
return nil, errors.New("constraint Parser Error")
|
|
||||||
}
|
|
||||||
|
|
||||||
cs := &constraint{
|
|
||||||
function: constraintOps[m[1]],
|
|
||||||
msg: constraintMsg[m[1]],
|
|
||||||
con: con,
|
|
||||||
orig: orig,
|
|
||||||
minorDirty: minorDirty,
|
|
||||||
patchDirty: patchDirty,
|
|
||||||
dirty: dirty,
|
|
||||||
}
|
|
||||||
return cs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constraint functions
|
|
||||||
func constraintNotEqual(v *Version, c *constraint) bool {
|
|
||||||
if c.dirty {
|
|
||||||
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.con.Major() != v.Major() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
|
||||||
return true
|
|
||||||
} else if c.minorDirty {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return !v.Equal(c.con)
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraintGreaterThan(v *Version, c *constraint) bool {
|
|
||||||
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Compare(c.con) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraintLessThan(v *Version, c *constraint) bool {
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.dirty {
|
|
||||||
return v.Compare(c.con) < 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Major() > c.con.Major() {
|
|
||||||
return false
|
|
||||||
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
|
||||||
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Compare(c.con) >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraintLessThanEqual(v *Version, c *constraint) bool {
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.dirty {
|
|
||||||
return v.Compare(c.con) <= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Major() > c.con.Major() {
|
|
||||||
return false
|
|
||||||
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~*, ~>* --> >= 0.0.0 (any)
|
|
||||||
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
|
||||||
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
|
||||||
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
|
||||||
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
|
||||||
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
|
||||||
func constraintTilde(v *Version, c *constraint) bool {
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.LessThan(c.con) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~0.0.0 is a special case where all constraints are accepted. It's
|
|
||||||
// equivalent to >= 0.0.0.
|
|
||||||
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 &&
|
|
||||||
!c.minorDirty && !c.patchDirty {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Major() != c.con.Major() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
|
||||||
// it's a straight =
|
|
||||||
func constraintTildeOrEqual(v *Version, c *constraint) bool {
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.dirty {
|
|
||||||
c.msg = constraintMsg["~"]
|
|
||||||
return constraintTilde(v, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Equal(c.con)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ^* --> (any)
|
|
||||||
// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
|
|
||||||
// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
|
|
||||||
// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
|
|
||||||
// ^1.2.3 --> >=1.2.3, <2.0.0
|
|
||||||
// ^1.2.0 --> >=1.2.0, <2.0.0
|
|
||||||
func constraintCaret(v *Version, c *constraint) bool {
|
|
||||||
// If there is a pre-release on the version but the constraint isn't looking
|
|
||||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
|
||||||
// more details.
|
|
||||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.LessThan(c.con) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Major() != c.con.Major() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var constraintRangeRegex *regexp.Regexp
|
|
||||||
|
|
||||||
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
|
||||||
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
|
||||||
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
|
||||||
|
|
||||||
func isX(x string) bool {
|
|
||||||
switch x {
|
|
||||||
case "x", "*", "X":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rewriteRange(i string) string {
|
|
||||||
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
|
||||||
if m == nil {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
o := i
|
|
||||||
for _, v := range m {
|
|
||||||
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
|
||||||
o = strings.Replace(o, v[0], t, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return o
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
|
||||||
|
|
||||||
Specifically it provides the ability to:
|
|
||||||
|
|
||||||
* Parse semantic versions
|
|
||||||
* Sort semantic versions
|
|
||||||
* Check if a semantic version fits within a set of constraints
|
|
||||||
* Optionally work with a `v` prefix
|
|
||||||
|
|
||||||
Parsing Semantic Versions
|
|
||||||
|
|
||||||
To parse a semantic version use the `NewVersion` function. For example,
|
|
||||||
|
|
||||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
|
||||||
|
|
||||||
If there is an error the version wasn't parseable. The version object has methods
|
|
||||||
to get the parts of the version, compare it to other versions, convert the
|
|
||||||
version back into a string, and get the original string. For more details
|
|
||||||
please see the documentation at https://godoc.org/github.com/Masterminds/semver.
|
|
||||||
|
|
||||||
Sorting Semantic Versions
|
|
||||||
|
|
||||||
A set of versions can be sorted using the `sort` package from the standard library.
|
|
||||||
For example,
|
|
||||||
|
|
||||||
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
|
||||||
vs := make([]*semver.Version, len(raw))
|
|
||||||
for i, r := range raw {
|
|
||||||
v, err := semver.NewVersion(r)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing version: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vs[i] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(semver.Collection(vs))
|
|
||||||
|
|
||||||
Checking Version Constraints
|
|
||||||
|
|
||||||
Checking a version against version constraints is one of the most featureful
|
|
||||||
parts of the package.
|
|
||||||
|
|
||||||
c, err := semver.NewConstraint(">= 1.2.3")
|
|
||||||
if err != nil {
|
|
||||||
// Handle constraint not being parseable.
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := semver.NewVersion("1.3")
|
|
||||||
if err != nil {
|
|
||||||
// Handle version not being parseable.
|
|
||||||
}
|
|
||||||
// Check if the version meets the constraints. The a variable will be true.
|
|
||||||
a := c.Check(v)
|
|
||||||
|
|
||||||
Basic Comparisons
|
|
||||||
|
|
||||||
There are two elements to the comparisons. First, a comparison string is a list
|
|
||||||
of comma separated and comparisons. These are then separated by || separated or
|
|
||||||
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
|
|
||||||
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
|
||||||
greater than or equal to 4.2.3.
|
|
||||||
|
|
||||||
The basic comparisons are:
|
|
||||||
|
|
||||||
* `=`: equal (aliased to no operator)
|
|
||||||
* `!=`: not equal
|
|
||||||
* `>`: greater than
|
|
||||||
* `<`: less than
|
|
||||||
* `>=`: greater than or equal to
|
|
||||||
* `<=`: less than or equal to
|
|
||||||
|
|
||||||
Hyphen Range Comparisons
|
|
||||||
|
|
||||||
There are multiple methods to handle ranges and the first is hyphens ranges.
|
|
||||||
These look like:
|
|
||||||
|
|
||||||
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
|
||||||
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
|
|
||||||
|
|
||||||
Wildcards In Comparisons
|
|
||||||
|
|
||||||
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
|
||||||
for all comparison operators. When used on the `=` operator it falls
|
|
||||||
back to the pack level comparison (see tilde below). For example,
|
|
||||||
|
|
||||||
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
|
||||||
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
|
||||||
* `<= 2.x` is equivalent to `<= 3`
|
|
||||||
* `*` is equivalent to `>= 0.0.0`
|
|
||||||
|
|
||||||
Tilde Range Comparisons (Patch)
|
|
||||||
|
|
||||||
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
|
||||||
version is specified and major level changes when the minor number is missing.
|
|
||||||
For example,
|
|
||||||
|
|
||||||
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
|
||||||
* `~1` is equivalent to `>= 1, < 2`
|
|
||||||
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
|
||||||
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
|
||||||
* `~1.x` is equivalent to `>= 1, < 2`
|
|
||||||
|
|
||||||
Caret Range Comparisons (Major)
|
|
||||||
|
|
||||||
The caret (`^`) comparison operator is for major level changes. This is useful
|
|
||||||
when comparisons of API versions as a major change is API breaking. For example,
|
|
||||||
|
|
||||||
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
|
||||||
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
|
||||||
* `^2.3` is equivalent to `>= 2.3, < 3`
|
|
||||||
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
|
||||||
*/
|
|
||||||
package semver
|
|
@ -0,0 +1 @@
|
|||||||
|
_fuzz/
|
@ -0,0 +1,26 @@
|
|||||||
|
run:
|
||||||
|
deadline: 2m
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- deadcode
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- structcheck
|
||||||
|
- unused
|
||||||
|
- varcheck
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
dupl:
|
||||||
|
threshold: 400
|
@ -0,0 +1,194 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 3.1.1 (2020-11-23)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #158: Fixed issue with generated regex operation order that could cause problem
|
||||||
|
|
||||||
|
## 3.1.0 (2020-04-15)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #131: Add support for serializing/deserializing SQL (thanks @ryancurrah)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #148: More accurate validation messages on constraints
|
||||||
|
|
||||||
|
## 3.0.3 (2019-12-13)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #141: Fixed issue with <= comparison
|
||||||
|
|
||||||
|
## 3.0.2 (2019-11-14)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #134: Fixed broken constraint checking with ^0.0 (thanks @krmichelos)
|
||||||
|
|
||||||
|
## 3.0.1 (2019-09-13)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #125: Fixes issue with module path for v3
|
||||||
|
|
||||||
|
## 3.0.0 (2019-09-12)
|
||||||
|
|
||||||
|
This is a major release of the semver package which includes API changes. The Go
|
||||||
|
API is compatible with ^1. The Go API was not changed because many people are using
|
||||||
|
`go get` without Go modules for their applications and API breaking changes cause
|
||||||
|
errors which we have or would need to support.
|
||||||
|
|
||||||
|
The changes in this release are the handling based on the data passed into the
|
||||||
|
functions. These are described in the added and changed sections below.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- StrictNewVersion function. This is similar to NewVersion but will return an
|
||||||
|
error if the version passed in is not a strict semantic version. For example,
|
||||||
|
1.2.3 would pass but v1.2.3 or 1.2 would fail because they are not strictly
|
||||||
|
speaking semantic versions. This function is faster, performs fewer operations,
|
||||||
|
and uses fewer allocations than NewVersion.
|
||||||
|
- Fuzzing has been performed on NewVersion, StrictNewVersion, and NewConstraint.
|
||||||
|
The Makefile contains the operations used. For more information on you can start
|
||||||
|
on Wikipedia at https://en.wikipedia.org/wiki/Fuzzing
|
||||||
|
- Now using Go modules
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- NewVersion has proper prerelease and metadata validation with error messages
|
||||||
|
to signal an issue with either of them
|
||||||
|
- ^ now operates using a similar set of rules to npm/js and Rust/Cargo. If the
|
||||||
|
version is >=1 the ^ ranges works the same as v1. For major versions of 0 the
|
||||||
|
rules have changed. The minor version is treated as the stable version unless
|
||||||
|
a patch is specified and then it is equivalent to =. One difference from npm/js
|
||||||
|
is that prereleases there are only to a specific version (e.g. 1.2.3).
|
||||||
|
Prereleases here look over multiple versions and follow semantic version
|
||||||
|
ordering rules. This pattern now follows along with the expected and requested
|
||||||
|
handling of this packaged by numerous users.
|
||||||
|
|
||||||
|
## 1.5.0 (2019-09-11)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil)
|
||||||
|
- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil)
|
||||||
|
- #72: Adding docs comment pointing to vert for a cli
|
||||||
|
- #71: Update the docs on pre-release comparator handling
|
||||||
|
- #89: Test with new go versions (thanks @thedevsaddam)
|
||||||
|
- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #78: Fix unchecked error in example code (thanks @ravron)
|
||||||
|
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
||||||
|
- #97: Fixed copyright file for proper display on GitHub
|
||||||
|
- #107: Fix handling prerelease when sorting alphanum and num
|
||||||
|
- #109: Fixed where Validate sometimes returns wrong message on error
|
||||||
|
|
||||||
|
## 1.4.2 (2018-04-10)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #72: Updated the docs to point to vert for a console appliaction
|
||||||
|
- #71: Update the docs on pre-release comparator handling
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
||||||
|
|
||||||
|
## 1.4.1 (2018-04-02)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed #64: Fix pre-release precedence issue (thanks @uudashr)
|
||||||
|
|
||||||
|
## 1.4.0 (2017-10-04)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill)
|
||||||
|
|
||||||
|
## 1.3.1 (2017-07-10)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed #57: number comparisons in prerelease sometimes inaccurate
|
||||||
|
|
||||||
|
## 1.3.0 (2017-05-02)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #45: Added json (un)marshaling support (thanks @mh-cbon)
|
||||||
|
- Stability marker. See https://masterminds.github.io/stability/
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #51: Fix handling of single digit tilde constraint (thanks @dgodd)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #55: The godoc icon moved from png to svg
|
||||||
|
|
||||||
|
## 1.2.3 (2017-04-03)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #46: Fixed 0.x.x and 0.0.x in constraints being treated as *
|
||||||
|
|
||||||
|
## Release 1.2.2 (2016-12-13)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #34: Fixed issue where hyphen range was not working with pre-release parsing.
|
||||||
|
|
||||||
|
## Release 1.2.1 (2016-11-28)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha"
|
||||||
|
properly.
|
||||||
|
|
||||||
|
## Release 1.2.0 (2016-11-04)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #20: Added MustParse function for versions (thanks @adamreese)
|
||||||
|
- #15: Added increment methods on versions (thanks @mh-cbon)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and
|
||||||
|
might not satisfy the intended compatibility. The change here ignores pre-releases
|
||||||
|
on constraint checks (e.g., ~ or ^) when a pre-release is not part of the
|
||||||
|
constraint. For example, `^1.2.3` will ignore pre-releases while
|
||||||
|
`^1.2.3-alpha` will include them.
|
||||||
|
|
||||||
|
## Release 1.1.1 (2016-06-30)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Issue #9: Speed up version comparison performance (thanks @sdboyer)
|
||||||
|
- Issue #8: Added benchmarks (thanks @sdboyer)
|
||||||
|
- Updated Go Report Card URL to new location
|
||||||
|
- Updated Readme to add code snippet formatting (thanks @mh-cbon)
|
||||||
|
- Updating tagging to v[SemVer] structure for compatibility with other tools.
|
||||||
|
|
||||||
|
## Release 1.1.0 (2016-03-11)
|
||||||
|
|
||||||
|
- Issue #2: Implemented validation to provide reasons a versions failed a
|
||||||
|
constraint.
|
||||||
|
|
||||||
|
## Release 1.0.1 (2015-12-31)
|
||||||
|
|
||||||
|
- Fixed #1: * constraint failing on valid versions.
|
||||||
|
|
||||||
|
## Release 1.0.0 (2015-10-20)
|
||||||
|
|
||||||
|
- Initial release
|
@ -0,0 +1,37 @@
|
|||||||
|
GOPATH=$(shell go env GOPATH)
|
||||||
|
GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint
|
||||||
|
GOFUZZBUILD = $(GOPATH)/bin/go-fuzz-build
|
||||||
|
GOFUZZ = $(GOPATH)/bin/go-fuzz
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: $(GOLANGCI_LINT)
|
||||||
|
@echo "==> Linting codebase"
|
||||||
|
@$(GOLANGCI_LINT) run
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
@echo "==> Running tests"
|
||||||
|
GO111MODULE=on go test -v
|
||||||
|
|
||||||
|
.PHONY: test-cover
|
||||||
|
test-cover:
|
||||||
|
@echo "==> Running Tests with coverage"
|
||||||
|
GO111MODULE=on go test -cover .
|
||||||
|
|
||||||
|
.PHONY: fuzz
|
||||||
|
fuzz: $(GOFUZZBUILD) $(GOFUZZ)
|
||||||
|
@echo "==> Fuzz testing"
|
||||||
|
$(GOFUZZBUILD)
|
||||||
|
$(GOFUZZ) -workdir=_fuzz
|
||||||
|
|
||||||
|
$(GOLANGCI_LINT):
|
||||||
|
# Install golangci-lint. The configuration for it is in the .golangci.yml
|
||||||
|
# file in the root of the repository
|
||||||
|
echo ${GOPATH}
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1
|
||||||
|
|
||||||
|
$(GOFUZZBUILD):
|
||||||
|
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||||
|
|
||||||
|
$(GOFUZZ):
|
||||||
|
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-dep
|
@ -0,0 +1,244 @@
|
|||||||
|
# SemVer
|
||||||
|
|
||||||
|
The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
[![Stability:
|
||||||
|
Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)
|
||||||
|
[![](https://github.com/Masterminds/semver/workflows/Tests/badge.svg)](https://github.com/Masterminds/semver/actions)
|
||||||
|
[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/Masterminds/semver/v3)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver)
|
||||||
|
|
||||||
|
If you are looking for a command line tool for version comparisons please see
|
||||||
|
[vert](https://github.com/Masterminds/vert) which uses this library.
|
||||||
|
|
||||||
|
## Package Versions
|
||||||
|
|
||||||
|
There are three major versions fo the `semver` package.
|
||||||
|
|
||||||
|
* 3.x.x is the new stable and active version. This version is focused on constraint
|
||||||
|
compatibility for range handling in other tools from other languages. It has
|
||||||
|
a similar API to the v1 releases. The development of this version is on the master
|
||||||
|
branch. The documentation for this version is below.
|
||||||
|
* 2.x was developed primarily for [dep](https://github.com/golang/dep). There are
|
||||||
|
no tagged releases and the development was performed by [@sdboyer](https://github.com/sdboyer).
|
||||||
|
There are API breaking changes from v1. This version lives on the [2.x branch](https://github.com/Masterminds/semver/tree/2.x).
|
||||||
|
* 1.x.x is the most widely used version with numerous tagged releases. This is the
|
||||||
|
previous stable and is still maintained for bug fixes. The development, to fix
|
||||||
|
bugs, occurs on the release-1 branch. You can read the documentation [here](https://github.com/Masterminds/semver/blob/release-1/README.md).
|
||||||
|
|
||||||
|
## Parsing Semantic Versions
|
||||||
|
|
||||||
|
There are two functions that can parse semantic versions. The `StrictNewVersion`
|
||||||
|
function only parses valid version 2 semantic versions as outlined in the
|
||||||
|
specification. The `NewVersion` function attempts to coerce a version into a
|
||||||
|
semantic version and parse it. For example, if there is a leading v or a version
|
||||||
|
listed without all 3 parts (e.g. `v1.2`) it will attempt to coerce it into a valid
|
||||||
|
semantic version (e.g., 1.2.0). In both cases a `Version` object is returned
|
||||||
|
that can be sorted, compared, and used in constraints.
|
||||||
|
|
||||||
|
When parsing a version an error is returned if there is an issue parsing the
|
||||||
|
version. For example,
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
|
||||||
|
The version object has methods to get the parts of the version, compare it to
|
||||||
|
other versions, convert the version back into a string, and get the original
|
||||||
|
string. Getting the original string is useful if the semantic version was coerced
|
||||||
|
into a valid form.
|
||||||
|
|
||||||
|
## Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the `sort` package from the standard library.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checking Version Constraints
|
||||||
|
|
||||||
|
There are two methods for comparing versions. One uses comparison methods on
|
||||||
|
`Version` instances and the other uses `Constraints`. There are some important
|
||||||
|
differences to notes between these two methods of comparison.
|
||||||
|
|
||||||
|
1. When two versions are compared using functions such as `Compare`, `LessThan`,
|
||||||
|
and others it will follow the specification and always include prereleases
|
||||||
|
within the comparison. It will provide an answer that is valid with the
|
||||||
|
comparison section of the spec at https://semver.org/#spec-item-11
|
||||||
|
2. When constraint checking is used for checks or validation it will follow a
|
||||||
|
different set of rules that are common for ranges with tools like npm/js
|
||||||
|
and Rust/Cargo. This includes considering prereleases to be invalid if the
|
||||||
|
ranges does not include one. If you want to have it include pre-releases a
|
||||||
|
simple solution is to include `-0` in your range.
|
||||||
|
3. Constraint ranges can have some complex rules including the shorthand use of
|
||||||
|
~ and ^. For more details on those see the options below.
|
||||||
|
|
||||||
|
There are differences between the two methods or checking versions because the
|
||||||
|
comparison methods on `Version` follow the specification while comparison ranges
|
||||||
|
are not part of the specification. Different packages and tools have taken it
|
||||||
|
upon themselves to come up with range rules. This has resulted in differences.
|
||||||
|
For example, npm/js and Cargo/Rust follow similar patterns while PHP has a
|
||||||
|
different pattern for ^. The comparison features in this package follow the
|
||||||
|
npm/js and Cargo/Rust lead because applications using it have followed similar
|
||||||
|
patters with their versions.
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parsable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parsable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Comparisons
|
||||||
|
|
||||||
|
There are two elements to the comparisons. First, a comparison string is a list
|
||||||
|
of space or comma separated AND comparisons. These are then separated by || (OR)
|
||||||
|
comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a
|
||||||
|
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||||
|
greater than or equal to 4.2.3.
|
||||||
|
|
||||||
|
The basic comparisons are:
|
||||||
|
|
||||||
|
* `=`: equal (aliased to no operator)
|
||||||
|
* `!=`: not equal
|
||||||
|
* `>`: greater than
|
||||||
|
* `<`: less than
|
||||||
|
* `>=`: greater than or equal to
|
||||||
|
* `<=`: less than or equal to
|
||||||
|
|
||||||
|
### Working With Prerelease Versions
|
||||||
|
|
||||||
|
Pre-releases, for those not familiar with them, are used for software releases
|
||||||
|
prior to stable or generally available releases. Examples of prereleases include
|
||||||
|
development, alpha, beta, and release candidate releases. A prerelease may be
|
||||||
|
a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
|
||||||
|
order of precedence, prereleases come before their associated releases. In this
|
||||||
|
example `1.2.3-beta.1 < 1.2.3`.
|
||||||
|
|
||||||
|
According to the Semantic Version specification prereleases may not be
|
||||||
|
API compliant with their release counterpart. It says,
|
||||||
|
|
||||||
|
> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
|
||||||
|
|
||||||
|
SemVer comparisons using constraints without a prerelease comparator will skip
|
||||||
|
prerelease versions. For example, `>=1.2.3` will skip prereleases when looking
|
||||||
|
at a list of releases while `>=1.2.3-0` will evaluate and find prereleases.
|
||||||
|
|
||||||
|
The reason for the `0` as a pre-release version in the example comparison is
|
||||||
|
because pre-releases can only contain ASCII alphanumerics and hyphens (along with
|
||||||
|
`.` separators), per the spec. Sorting happens in ASCII sort order, again per the
|
||||||
|
spec. The lowest character is a `0` in ASCII sort order
|
||||||
|
(see an [ASCII Table](http://www.asciitable.com/))
|
||||||
|
|
||||||
|
Understanding ASCII sort ordering is important because A-Z comes before a-z. That
|
||||||
|
means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
|
||||||
|
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
|
||||||
|
the spec specifies.
|
||||||
|
|
||||||
|
### Hyphen Range Comparisons
|
||||||
|
|
||||||
|
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||||
|
These look like:
|
||||||
|
|
||||||
|
* `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5`
|
||||||
|
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
|
||||||
|
|
||||||
|
### Wildcards In Comparisons
|
||||||
|
|
||||||
|
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||||
|
for all comparison operators. When used on the `=` operator it falls
|
||||||
|
back to the patch level comparison (see tilde below). For example,
|
||||||
|
|
||||||
|
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||||
|
* `<= 2.x` is equivalent to `< 3`
|
||||||
|
* `*` is equivalent to `>= 0.0.0`
|
||||||
|
|
||||||
|
### Tilde Range Comparisons (Patch)
|
||||||
|
|
||||||
|
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||||
|
version is specified and major level changes when the minor number is missing.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
||||||
|
* `~1` is equivalent to `>= 1, < 2`
|
||||||
|
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
||||||
|
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `~1.x` is equivalent to `>= 1, < 2`
|
||||||
|
|
||||||
|
### Caret Range Comparisons (Major)
|
||||||
|
|
||||||
|
The caret (`^`) comparison operator is for major level changes once a stable
|
||||||
|
(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts
|
||||||
|
as the API stability level. This is useful when comparisons of API versions as a
|
||||||
|
major change is API breaking. For example,
|
||||||
|
|
||||||
|
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||||
|
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||||
|
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||||
|
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||||
|
* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
|
||||||
|
* `^0.2` is equivalent to `>=0.2.0 <0.3.0`
|
||||||
|
* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
|
||||||
|
* `^0.0` is equivalent to `>=0.0.0 <0.1.0`
|
||||||
|
* `^0` is equivalent to `>=0.0.0 <1.0.0`
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
In addition to testing a version against a constraint, a version can be validated
|
||||||
|
against a constraint. When validation fails a slice of errors containing why a
|
||||||
|
version didn't meet the constraint is returned. For example,
|
||||||
|
|
||||||
|
```go
|
||||||
|
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate a version against a constraint.
|
||||||
|
a, msgs := c.Validate(v)
|
||||||
|
// a is false
|
||||||
|
for _, m := range msgs {
|
||||||
|
fmt.Println(m)
|
||||||
|
|
||||||
|
// Loops over the errors which would read
|
||||||
|
// "1.3 is greater than 1.2.3"
|
||||||
|
// "1.3 is less than 1.4"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contribute
|
||||||
|
|
||||||
|
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
|
||||||
|
or [create a pull request](https://github.com/Masterminds/semver/pulls).
|
@ -0,0 +1,568 @@
|
|||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constraints is one or more constraint that a semantic version can be
|
||||||
|
// checked against.
|
||||||
|
type Constraints struct {
|
||||||
|
constraints [][]*constraint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstraint returns a Constraints instance that a Version instance can
|
||||||
|
// be checked against. If there is a parse error it will be returned.
|
||||||
|
func NewConstraint(c string) (*Constraints, error) {
|
||||||
|
|
||||||
|
// Rewrite - ranges into a comparison operation.
|
||||||
|
c = rewriteRange(c)
|
||||||
|
|
||||||
|
ors := strings.Split(c, "||")
|
||||||
|
or := make([][]*constraint, len(ors))
|
||||||
|
for k, v := range ors {
|
||||||
|
|
||||||
|
// TODO: Find a way to validate and fetch all the constraints in a simpler form
|
||||||
|
|
||||||
|
// Validate the segment
|
||||||
|
if !validConstraintRegex.MatchString(v) {
|
||||||
|
return nil, fmt.Errorf("improper constraint: %s", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := findConstraintRegex.FindAllString(v, -1)
|
||||||
|
if cs == nil {
|
||||||
|
cs = append(cs, v)
|
||||||
|
}
|
||||||
|
result := make([]*constraint, len(cs))
|
||||||
|
for i, s := range cs {
|
||||||
|
pc, err := parseConstraint(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = pc
|
||||||
|
}
|
||||||
|
or[k] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
o := &Constraints{constraints: or}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tests if a version satisfies the constraints.
|
||||||
|
func (cs Constraints) Check(v *Version) bool {
|
||||||
|
// TODO(mattfarina): For v4 of this library consolidate the Check and Validate
|
||||||
|
// functions as the underlying functions make that possible now.
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if check, _ := c.check(v); !check {
|
||||||
|
joy = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks if a version satisfies a constraint. If not a slice of
|
||||||
|
// reasons for the failure are returned in addition to a bool.
|
||||||
|
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
var e []error
|
||||||
|
|
||||||
|
// Capture the prerelease message only once. When it happens the first time
|
||||||
|
// this var is marked
|
||||||
|
var prerelesase bool
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
// Before running the check handle the case there the version is
|
||||||
|
// a prerelease and the check is not searching for prereleases.
|
||||||
|
if c.con.pre == "" && v.pre != "" {
|
||||||
|
if !prerelesase {
|
||||||
|
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
e = append(e, em)
|
||||||
|
prerelesase = true
|
||||||
|
}
|
||||||
|
joy = false
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if _, err := c.check(v); err != nil {
|
||||||
|
e = append(e, err)
|
||||||
|
joy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true, []error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs Constraints) String() string {
|
||||||
|
buf := make([]string, len(cs.constraints))
|
||||||
|
var tmp bytes.Buffer
|
||||||
|
|
||||||
|
for k, v := range cs.constraints {
|
||||||
|
tmp.Reset()
|
||||||
|
vlen := len(v)
|
||||||
|
for kk, c := range v {
|
||||||
|
tmp.WriteString(c.string())
|
||||||
|
|
||||||
|
// Space separate the AND conditions
|
||||||
|
if vlen > 1 && kk < vlen-1 {
|
||||||
|
tmp.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[k] = tmp.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(buf, " || ")
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintOps map[string]cfunc
|
||||||
|
var constraintRegex *regexp.Regexp
|
||||||
|
var constraintRangeRegex *regexp.Regexp
|
||||||
|
|
||||||
|
// Used to find individual constraints within a multi-constraint string
|
||||||
|
var findConstraintRegex *regexp.Regexp
|
||||||
|
|
||||||
|
// Used to validate an segment of ANDs is valid
|
||||||
|
var validConstraintRegex *regexp.Regexp
|
||||||
|
|
||||||
|
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
constraintOps = map[string]cfunc{
|
||||||
|
"": constraintTildeOrEqual,
|
||||||
|
"=": constraintTildeOrEqual,
|
||||||
|
"!=": constraintNotEqual,
|
||||||
|
">": constraintGreaterThan,
|
||||||
|
"<": constraintLessThan,
|
||||||
|
">=": constraintGreaterThanEqual,
|
||||||
|
"=>": constraintGreaterThanEqual,
|
||||||
|
"<=": constraintLessThanEqual,
|
||||||
|
"=<": constraintLessThanEqual,
|
||||||
|
"~": constraintTilde,
|
||||||
|
"~>": constraintTilde,
|
||||||
|
"^": constraintCaret,
|
||||||
|
}
|
||||||
|
|
||||||
|
ops := `=||!=|>|<|>=|=>|<=|=<|~|~>|\^`
|
||||||
|
|
||||||
|
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`^\s*(%s)\s*(%s)\s*$`,
|
||||||
|
ops,
|
||||||
|
cvRegex))
|
||||||
|
|
||||||
|
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`\s*(%s)\s+-\s+(%s)\s*`,
|
||||||
|
cvRegex, cvRegex))
|
||||||
|
|
||||||
|
findConstraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`(%s)\s*(%s)`,
|
||||||
|
ops,
|
||||||
|
cvRegex))
|
||||||
|
|
||||||
|
validConstraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`^(\s*(%s)\s*(%s)\s*\,?)+$`,
|
||||||
|
ops,
|
||||||
|
cvRegex))
|
||||||
|
}
|
||||||
|
|
||||||
|
// An individual constraint
|
||||||
|
type constraint struct {
|
||||||
|
// The version used in the constraint check. For example, if a constraint
|
||||||
|
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
||||||
|
con *Version
|
||||||
|
|
||||||
|
// The original parsed version (e.g., 4.x from != 4.x)
|
||||||
|
orig string
|
||||||
|
|
||||||
|
// The original operator for the constraint
|
||||||
|
origfunc string
|
||||||
|
|
||||||
|
// When an x is used as part of the version (e.g., 1.x)
|
||||||
|
minorDirty bool
|
||||||
|
dirty bool
|
||||||
|
patchDirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a version meets the constraint
|
||||||
|
func (c *constraint) check(v *Version) (bool, error) {
|
||||||
|
return constraintOps[c.origfunc](v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String prints an individual constraint into a string
|
||||||
|
func (c *constraint) string() string {
|
||||||
|
return c.origfunc + c.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfunc func(v *Version, c *constraint) (bool, error)
|
||||||
|
|
||||||
|
func parseConstraint(c string) (*constraint, error) {
|
||||||
|
if len(c) > 0 {
|
||||||
|
m := constraintRegex.FindStringSubmatch(c)
|
||||||
|
if m == nil {
|
||||||
|
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &constraint{
|
||||||
|
orig: m[2],
|
||||||
|
origfunc: m[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
ver := m[2]
|
||||||
|
minorDirty := false
|
||||||
|
patchDirty := false
|
||||||
|
dirty := false
|
||||||
|
if isX(m[3]) || m[3] == "" {
|
||||||
|
ver = "0.0.0"
|
||||||
|
dirty = true
|
||||||
|
} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
|
||||||
|
minorDirty = true
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||||
|
} else if isX(strings.TrimPrefix(m[5], ".")) || m[5] == "" {
|
||||||
|
dirty = true
|
||||||
|
patchDirty = true
|
||||||
|
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := NewVersion(ver)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// The constraintRegex should catch any regex parsing errors. So,
|
||||||
|
// we should never get here.
|
||||||
|
return nil, errors.New("constraint Parser Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.con = con
|
||||||
|
cs.minorDirty = minorDirty
|
||||||
|
cs.patchDirty = patchDirty
|
||||||
|
cs.dirty = dirty
|
||||||
|
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest is the special case where an empty string was passed in which
|
||||||
|
// is equivalent to * or >=0.0.0
|
||||||
|
con, err := StrictNewVersion("0.0.0")
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// The constraintRegex should catch any regex parsing errors. So,
|
||||||
|
// we should never get here.
|
||||||
|
return nil, errors.New("constraint Parser Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &constraint{
|
||||||
|
con: con,
|
||||||
|
orig: c,
|
||||||
|
origfunc: "",
|
||||||
|
minorDirty: false,
|
||||||
|
patchDirty: false,
|
||||||
|
dirty: true,
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraint functions
|
||||||
|
func constraintNotEqual(v *Version, c *constraint) (bool, error) {
|
||||||
|
if c.dirty {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.con.Major() != v.Major() {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
||||||
|
return true, nil
|
||||||
|
} else if c.minorDirty {
|
||||||
|
return false, fmt.Errorf("%s is equal to %s", v, c.orig)
|
||||||
|
} else if c.con.Patch() != v.Patch() && !c.patchDirty {
|
||||||
|
return true, nil
|
||||||
|
} else if c.patchDirty {
|
||||||
|
// Need to handle prereleases if present
|
||||||
|
if v.Prerelease() != "" || c.con.Prerelease() != "" {
|
||||||
|
eq := comparePrerelease(v.Prerelease(), c.con.Prerelease()) != 0
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := v.Equal(c.con)
|
||||||
|
if eq {
|
||||||
|
return false, fmt.Errorf("%s is equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThan(v *Version, c *constraint) (bool, error) {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var eq bool
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
eq = v.Compare(c.con) == 1
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return true, nil
|
||||||
|
} else if v.Major() < c.con.Major() {
|
||||||
|
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
|
||||||
|
} else if c.minorDirty {
|
||||||
|
// This is a range case such as >11. When the version is something like
|
||||||
|
// 11.1.0 is it not > 11. For that we would need 12 or higher
|
||||||
|
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
|
||||||
|
} else if c.patchDirty {
|
||||||
|
// This is for ranges such as >11.1. A version of 11.1.1 is not greater
|
||||||
|
// which one of 11.2.1 is greater
|
||||||
|
eq = v.Minor() > c.con.Minor()
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have gotten here we are not comparing pre-preleases and can use the
|
||||||
|
// Compare function to accomplish that.
|
||||||
|
eq = v.Compare(c.con) == 1
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThan(v *Version, c *constraint) (bool, error) {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := v.Compare(c.con) < 0
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := v.Compare(c.con) >= 0
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is less than %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThanEqual(v *Version, c *constraint) (bool, error) {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var eq bool
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
eq = v.Compare(c.con) <= 0
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s is greater than %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false, fmt.Errorf("%s is greater than %s", v, c.orig)
|
||||||
|
} else if v.Major() == c.con.Major() && v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false, fmt.Errorf("%s is greater than %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~*, ~>* --> >= 0.0.0 (any)
|
||||||
|
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
||||||
|
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
||||||
|
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
||||||
|
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
||||||
|
func constraintTilde(v *Version, c *constraint) (bool, error) {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false, fmt.Errorf("%s is less than %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~0.0.0 is a special case where all constraints are accepted. It's
|
||||||
|
// equivalent to >= 0.0.0.
|
||||||
|
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 &&
|
||||||
|
!c.minorDirty && !c.patchDirty {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
||||||
|
return false, fmt.Errorf("%s does not have same major and minor version as %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
||||||
|
// it's a straight =
|
||||||
|
func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.dirty {
|
||||||
|
return constraintTilde(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := v.Equal(c.con)
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, fmt.Errorf("%s is not equal to %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^* --> (any)
|
||||||
|
// ^1.2.3 --> >=1.2.3 <2.0.0
|
||||||
|
// ^1.2 --> >=1.2.0 <2.0.0
|
||||||
|
// ^1 --> >=1.0.0 <2.0.0
|
||||||
|
// ^0.2.3 --> >=0.2.3 <0.3.0
|
||||||
|
// ^0.2 --> >=0.2.0 <0.3.0
|
||||||
|
// ^0.0.3 --> >=0.0.3 <0.0.4
|
||||||
|
// ^0.0 --> >=0.0.0 <0.1.0
|
||||||
|
// ^0 --> >=0.0.0 <1.0.0
|
||||||
|
func constraintCaret(v *Version, c *constraint) (bool, error) {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This less than handles prereleases
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false, fmt.Errorf("%s is less than %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
var eq bool
|
||||||
|
|
||||||
|
// ^ when the major > 0 is >=x.y.z < x+1
|
||||||
|
if c.con.Major() > 0 || c.minorDirty {
|
||||||
|
|
||||||
|
// ^ has to be within a major range for > 0. Everything less than was
|
||||||
|
// filtered out with the LessThan call above. This filters out those
|
||||||
|
// that greater but not within the same major range.
|
||||||
|
eq = v.Major() == c.con.Major()
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^ when the major is 0 and minor > 0 is >=0.y.z < 0.y+1
|
||||||
|
if c.con.Major() == 0 && v.Major() > 0 {
|
||||||
|
return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig)
|
||||||
|
}
|
||||||
|
// If the con Minor is > 0 it is not dirty
|
||||||
|
if c.con.Minor() > 0 || c.patchDirty {
|
||||||
|
eq = v.Minor() == c.con.Minor()
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s does not have same minor version as %s. Expected minor versions to match when constraint major version is 0", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the major is 0 and the minor is 0 and not dirty. The patch
|
||||||
|
// is not dirty so we need to check if they are equal. If they are not equal
|
||||||
|
eq = c.con.Patch() == v.Patch()
|
||||||
|
if eq {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("%s does not equal %s. Expect version and constraint to equal when major and minor versions are 0", v, c.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isX(x string) bool {
|
||||||
|
switch x {
|
||||||
|
case "x", "*", "X":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteRange(i string) string {
|
||||||
|
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
||||||
|
if m == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
o := i
|
||||||
|
for _, v := range m {
|
||||||
|
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
||||||
|
o = strings.Replace(o, v[0], t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
||||||
|
|
||||||
|
Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
Parsing Semantic Versions
|
||||||
|
|
||||||
|
There are two functions that can parse semantic versions. The `StrictNewVersion`
|
||||||
|
function only parses valid version 2 semantic versions as outlined in the
|
||||||
|
specification. The `NewVersion` function attempts to coerce a version into a
|
||||||
|
semantic version and parse it. For example, if there is a leading v or a version
|
||||||
|
listed without all 3 parts (e.g. 1.2) it will attempt to coerce it into a valid
|
||||||
|
semantic version (e.g., 1.2.0). In both cases a `Version` object is returned
|
||||||
|
that can be sorted, compared, and used in constraints.
|
||||||
|
|
||||||
|
When parsing a version an optional error can be returned if there is an issue
|
||||||
|
parsing the version. For example,
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+b345")
|
||||||
|
|
||||||
|
The version object has methods to get the parts of the version, compare it to
|
||||||
|
other versions, convert the version back into a string, and get the original
|
||||||
|
string. For more details please see the documentation
|
||||||
|
at https://godoc.org/github.com/Masterminds/semver.
|
||||||
|
|
||||||
|
Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the `sort` package from the standard library.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
|
||||||
|
Checking Version Constraints and Comparing Versions
|
||||||
|
|
||||||
|
There are two methods for comparing versions. One uses comparison methods on
|
||||||
|
`Version` instances and the other is using Constraints. There are some important
|
||||||
|
differences to notes between these two methods of comparison.
|
||||||
|
|
||||||
|
1. When two versions are compared using functions such as `Compare`, `LessThan`,
|
||||||
|
and others it will follow the specification and always include prereleases
|
||||||
|
within the comparison. It will provide an answer valid with the comparison
|
||||||
|
spec section at https://semver.org/#spec-item-11
|
||||||
|
2. When constraint checking is used for checks or validation it will follow a
|
||||||
|
different set of rules that are common for ranges with tools like npm/js
|
||||||
|
and Rust/Cargo. This includes considering prereleases to be invalid if the
|
||||||
|
ranges does not include on. If you want to have it include pre-releases a
|
||||||
|
simple solution is to include `-0` in your range.
|
||||||
|
3. Constraint ranges can have some complex rules including the shorthard use of
|
||||||
|
~ and ^. For more details on those see the options below.
|
||||||
|
|
||||||
|
There are differences between the two methods or checking versions because the
|
||||||
|
comparison methods on `Version` follow the specification while comparison ranges
|
||||||
|
are not part of the specification. Different packages and tools have taken it
|
||||||
|
upon themselves to come up with range rules. This has resulted in differences.
|
||||||
|
For example, npm/js and Cargo/Rust follow similar patterns which PHP has a
|
||||||
|
different pattern for ^. The comparison features in this package follow the
|
||||||
|
npm/js and Cargo/Rust lead because applications using it have followed similar
|
||||||
|
patters with their versions.
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parsable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parsable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
|
||||||
|
Basic Comparisons
|
||||||
|
|
||||||
|
There are two elements to the comparisons. First, a comparison string is a list
|
||||||
|
of comma or space separated AND comparisons. These are then separated by || (OR)
|
||||||
|
comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a
|
||||||
|
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||||
|
greater than or equal to 4.2.3. This can also be written as
|
||||||
|
`">= 1.2, < 3.0.0 || >= 4.2.3"`
|
||||||
|
|
||||||
|
The basic comparisons are:
|
||||||
|
|
||||||
|
* `=`: equal (aliased to no operator)
|
||||||
|
* `!=`: not equal
|
||||||
|
* `>`: greater than
|
||||||
|
* `<`: less than
|
||||||
|
* `>=`: greater than or equal to
|
||||||
|
* `<=`: less than or equal to
|
||||||
|
|
||||||
|
Hyphen Range Comparisons
|
||||||
|
|
||||||
|
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||||
|
These look like:
|
||||||
|
|
||||||
|
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
||||||
|
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
|
||||||
|
|
||||||
|
Wildcards In Comparisons
|
||||||
|
|
||||||
|
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||||
|
for all comparison operators. When used on the `=` operator it falls
|
||||||
|
back to the tilde operation. For example,
|
||||||
|
|
||||||
|
* `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
|
||||||
|
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||||
|
* `<= 2.x` is equivalent to `<= 3`
|
||||||
|
* `*` is equivalent to `>= 0.0.0`
|
||||||
|
|
||||||
|
Tilde Range Comparisons (Patch)
|
||||||
|
|
||||||
|
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||||
|
version is specified and major level changes when the minor number is missing.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
* `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0`
|
||||||
|
* `~1` is equivalent to `>= 1, < 2`
|
||||||
|
* `~2.3` is equivalent to `>= 2.3 < 2.4`
|
||||||
|
* `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
|
||||||
|
* `~1.x` is equivalent to `>= 1 < 2`
|
||||||
|
|
||||||
|
Caret Range Comparisons (Major)
|
||||||
|
|
||||||
|
The caret (`^`) comparison operator is for major level changes once a stable
|
||||||
|
(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts
|
||||||
|
as the API stability level. This is useful when comparisons of API versions as a
|
||||||
|
major change is API breaking. For example,
|
||||||
|
|
||||||
|
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||||
|
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||||
|
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||||
|
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||||
|
* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
|
||||||
|
* `^0.2` is equivalent to `>=0.2.0 <0.3.0`
|
||||||
|
* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
|
||||||
|
* `^0.0` is equivalent to `>=0.0.0 <0.1.0`
|
||||||
|
* `^0` is equivalent to `>=0.0.0 <1.0.0`
|
||||||
|
|
||||||
|
Validation
|
||||||
|
|
||||||
|
In addition to testing a version against a constraint, a version can be validated
|
||||||
|
against a constraint. When validation fails a slice of errors containing why a
|
||||||
|
version didn't meet the constraint is returned. For example,
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate a version against a constraint.
|
||||||
|
a, msgs := c.Validate(v)
|
||||||
|
// a is false
|
||||||
|
for _, m := range msgs {
|
||||||
|
fmt.Println(m)
|
||||||
|
|
||||||
|
// Loops over the errors which would read
|
||||||
|
// "1.3 is greater than 1.2.3"
|
||||||
|
// "1.3 is less than 1.4"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package semver
|
@ -0,0 +1,22 @@
|
|||||||
|
// +build gofuzz
|
||||||
|
|
||||||
|
package semver
|
||||||
|
|
||||||
|
func Fuzz(data []byte) int {
|
||||||
|
d := string(data)
|
||||||
|
|
||||||
|
// Test NewVersion
|
||||||
|
_, _ = NewVersion(d)
|
||||||
|
|
||||||
|
// Test StrictNewVersion
|
||||||
|
_, _ = StrictNewVersion(d)
|
||||||
|
|
||||||
|
// Test NewConstraint
|
||||||
|
_, _ = NewConstraint(d)
|
||||||
|
|
||||||
|
// The return value should be 0 normally, 1 if the priority in future tests
|
||||||
|
// should be increased, and -1 if future tests should skip passing in that
|
||||||
|
// data. We do not have a reason to change priority so 0 is always returned.
|
||||||
|
// There are example tests that do this.
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package semver
|
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
if _, err := NewVersion(string(data)); err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- 1.13.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
# Setting sudo access to false will let Travis CI use containers rather than
|
|
||||||
# VMs to run the tests. For more details see:
|
|
||||||
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
|
||||||
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make setup test
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
webhooks:
|
|
||||||
urls:
|
|
||||||
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
|
|
||||||
on_success: change # options: [always|never|change] default: always
|
|
||||||
on_failure: always # options: [always|never|change] default: always
|
|
||||||
on_start: never # options: [always|never|change] default: always
|
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
HAS_GLIDE := $(shell command -v glide;)
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test:
|
|
||||||
go test -v .
|
|
||||||
|
|
||||||
.PHONY: setup
|
|
||||||
setup:
|
|
||||||
ifndef HAS_GLIDE
|
|
||||||
go get -u github.com/Masterminds/glide
|
|
||||||
endif
|
|
||||||
glide install
|
|
@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
version: build-{build}.{branch}
|
|
||||||
|
|
||||||
clone_folder: C:\gopath\src\github.com\Masterminds\sprig
|
|
||||||
shallow_clone: true
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: C:\gopath
|
|
||||||
|
|
||||||
platform:
|
|
||||||
- x64
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -u github.com/Masterminds/glide
|
|
||||||
- set PATH=%GOPATH%\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- glide install
|
|
||||||
- go install ./...
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- go test -v
|
|
||||||
|
|
||||||
deploy: off
|
|
@ -1,19 +0,0 @@
|
|||||||
package: github.com/Masterminds/sprig
|
|
||||||
import:
|
|
||||||
- package: github.com/Masterminds/goutils
|
|
||||||
version: ^1.0.0
|
|
||||||
- package: github.com/google/uuid
|
|
||||||
version: ^1.0.0
|
|
||||||
- package: golang.org/x/crypto
|
|
||||||
subpackages:
|
|
||||||
- scrypt
|
|
||||||
- package: github.com/Masterminds/semver
|
|
||||||
version: ^v1.2.2
|
|
||||||
- package: github.com/stretchr/testify
|
|
||||||
version: ^v1.2.2
|
|
||||||
- package: github.com/imdario/mergo
|
|
||||||
version: ~0.3.7
|
|
||||||
- package: github.com/huandu/xstrings
|
|
||||||
version: ^1.2
|
|
||||||
- package: github.com/mitchellh/copystructure
|
|
||||||
version: ^1.0.0
|
|
@ -1,169 +0,0 @@
|
|||||||
package sprig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// toFloat64 converts 64-bit floats
|
|
||||||
func toFloat64(v interface{}) float64 {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
iv, err := strconv.ParseFloat(str, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return iv
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.Indirect(reflect.ValueOf(v))
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return float64(val.Int())
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
|
||||||
return float64(val.Uint())
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
return float64(val.Uint())
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return val.Float()
|
|
||||||
case reflect.Bool:
|
|
||||||
if val.Bool() == true {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toInt(v interface{}) int {
|
|
||||||
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
|
||||||
return int(toInt64(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// toInt64 converts integer types to 64-bit integers
|
|
||||||
func toInt64(v interface{}) int64 {
|
|
||||||
if str, ok := v.(string); ok {
|
|
||||||
iv, err := strconv.ParseInt(str, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return iv
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.Indirect(reflect.ValueOf(v))
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return val.Int()
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
|
||||||
return int64(val.Uint())
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
tv := val.Uint()
|
|
||||||
if tv <= math.MaxInt64 {
|
|
||||||
return int64(tv)
|
|
||||||
}
|
|
||||||
// TODO: What is the sensible thing to do here?
|
|
||||||
return math.MaxInt64
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return int64(val.Float())
|
|
||||||
case reflect.Bool:
|
|
||||||
if val.Bool() == true {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a interface{}, i ...interface{}) int64 {
|
|
||||||
aa := toInt64(a)
|
|
||||||
for _, b := range i {
|
|
||||||
bb := toInt64(b)
|
|
||||||
if bb > aa {
|
|
||||||
aa = bb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aa
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(a interface{}, i ...interface{}) int64 {
|
|
||||||
aa := toInt64(a)
|
|
||||||
for _, b := range i {
|
|
||||||
bb := toInt64(b)
|
|
||||||
if bb < aa {
|
|
||||||
aa = bb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aa
|
|
||||||
}
|
|
||||||
|
|
||||||
func until(count int) []int {
|
|
||||||
step := 1
|
|
||||||
if count < 0 {
|
|
||||||
step = -1
|
|
||||||
}
|
|
||||||
return untilStep(0, count, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
func untilStep(start, stop, step int) []int {
|
|
||||||
v := []int{}
|
|
||||||
|
|
||||||
if stop < start {
|
|
||||||
if step >= 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
for i := start; i > stop; i += step {
|
|
||||||
v = append(v, i)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
if step <= 0 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
for i := start; i < stop; i += step {
|
|
||||||
v = append(v, i)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func floor(a interface{}) float64 {
|
|
||||||
aa := toFloat64(a)
|
|
||||||
return math.Floor(aa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ceil(a interface{}) float64 {
|
|
||||||
aa := toFloat64(a)
|
|
||||||
return math.Ceil(aa)
|
|
||||||
}
|
|
||||||
|
|
||||||
func round(a interface{}, p int, r_opt ...float64) float64 {
|
|
||||||
roundOn := .5
|
|
||||||
if len(r_opt) > 0 {
|
|
||||||
roundOn = r_opt[0]
|
|
||||||
}
|
|
||||||
val := toFloat64(a)
|
|
||||||
places := toFloat64(p)
|
|
||||||
|
|
||||||
var round float64
|
|
||||||
pow := math.Pow(10, places)
|
|
||||||
digit := pow * val
|
|
||||||
_, div := math.Modf(digit)
|
|
||||||
if div >= roundOn {
|
|
||||||
round = math.Ceil(digit)
|
|
||||||
} else {
|
|
||||||
round = math.Floor(digit)
|
|
||||||
}
|
|
||||||
return round / pow
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts unix octal to decimal
|
|
||||||
func toDecimal(v interface{}) int64 {
|
|
||||||
result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package sprig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func regexMatch(regex string, s string) bool {
|
|
||||||
match, _ := regexp.MatchString(regex, s)
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
|
|
||||||
func regexFindAll(regex string, s string, n int) []string {
|
|
||||||
r := regexp.MustCompile(regex)
|
|
||||||
return r.FindAllString(s, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func regexFind(regex string, s string) string {
|
|
||||||
r := regexp.MustCompile(regex)
|
|
||||||
return r.FindString(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func regexReplaceAll(regex string, s string, repl string) string {
|
|
||||||
r := regexp.MustCompile(regex)
|
|
||||||
return r.ReplaceAllString(s, repl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func regexReplaceAllLiteral(regex string, s string, repl string) string {
|
|
||||||
r := regexp.MustCompile(regex)
|
|
||||||
return r.ReplaceAllLiteralString(s, repl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func regexSplit(regex string, s string, n int) []string {
|
|
||||||
r := regexp.MustCompile(regex)
|
|
||||||
return r.Split(s, n)
|
|
||||||
}
|
|
@ -1,5 +1,93 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Release 3.2.1 (2021-02-04)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Upgraded `Masterminds/goutils` to `v1.1.1`. see the [Security Advisory](https://github.com/Masterminds/goutils/security/advisories/GHSA-xg2h-wx96-xgxr)
|
||||||
|
|
||||||
|
## Release 3.2.0 (2020-12-14)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #211: Added randInt function (thanks @kochurovro)
|
||||||
|
- #223: Added fromJson and mustFromJson functions (thanks @mholt)
|
||||||
|
- #242: Added a bcrypt function (thanks @robbiet480)
|
||||||
|
- #253: Added randBytes function (thanks @MikaelSmith)
|
||||||
|
- #254: Added dig function for dicts (thanks @nyarly)
|
||||||
|
- #257: Added regexQuoteMeta for quoting regex metadata (thanks @rheaton)
|
||||||
|
- #261: Added filepath functions osBase, osDir, osExt, osClean, osIsAbs (thanks @zugl)
|
||||||
|
- #268: Added and and all functions for testing conditions (thanks @phuslu)
|
||||||
|
- #181: Added float64 arithmetic addf, add1f, subf, divf, mulf, maxf, and minf
|
||||||
|
(thanks @andrewmostello)
|
||||||
|
- #265: Added chunk function to split array into smaller arrays (thanks @karelbilek)
|
||||||
|
- #270: Extend certificate functions to handle non-RSA keys + add support for
|
||||||
|
ed25519 keys (thanks @misberner)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Removed testing and support for Go 1.12. ed25519 support requires Go 1.13 or newer
|
||||||
|
- Using semver 3.1.1 and mergo 0.3.11
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #249: Fix htmlDateInZone example (thanks @spawnia)
|
||||||
|
|
||||||
|
NOTE: The dependency github.com/imdario/mergo reverted the breaking change in
|
||||||
|
0.3.9 via 0.3.10 release.
|
||||||
|
|
||||||
|
## Release 3.1.0 (2020-04-16)
|
||||||
|
|
||||||
|
NOTE: The dependency github.com/imdario/mergo made a behavior change in 0.3.9
|
||||||
|
that impacts sprig functionality. Do not use sprig with a version newer than 0.3.8.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #225: Added support for generating htpasswd hash (thanks @rustycl0ck)
|
||||||
|
- #224: Added duration filter (thanks @frebib)
|
||||||
|
- #205: Added `seq` function (thanks @thadc23)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #203: Unlambda functions with correct signature (thanks @muesli)
|
||||||
|
- #236: Updated the license formatting for GitHub display purposes
|
||||||
|
- #238: Updated package dependency versions. Note, mergo not updated to 0.3.9
|
||||||
|
as it causes a breaking change for sprig. That issue is tracked at
|
||||||
|
https://github.com/imdario/mergo/issues/139
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #229: Fix `seq` example in docs (thanks @kalmant)
|
||||||
|
|
||||||
|
## Release 3.0.2 (2019-12-13)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #220: Updating to semver v3.0.3 to fix issue with <= ranges
|
||||||
|
- #218: fix typo elyptical->elliptic in ecdsa key description (thanks @laverya)
|
||||||
|
|
||||||
|
## Release 3.0.1 (2019-12-08)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #212: Updated semver fixing broken constraint checking with ^0.0
|
||||||
|
|
||||||
|
## Release 3.0.0 (2019-10-02)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- #187: Added durationRound function (thanks @yjp20)
|
||||||
|
- #189: Added numerous template functions that return errors rather than panic (thanks @nrvnrvn)
|
||||||
|
- #193: Added toRawJson support (thanks @Dean-Coakley)
|
||||||
|
- #197: Added get support to dicts (thanks @Dean-Coakley)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- #186: Moving dependency management to Go modules
|
||||||
|
- #186: Updated semver to v3. This has changes in the way ^ is handled
|
||||||
|
- #194: Updated documentation on merging and how it copies. Added example using deepCopy
|
||||||
|
- #196: trunc now supports negative values (thanks @Dean-Coakley)
|
||||||
|
|
||||||
## Release 2.22.0 (2019-10-02)
|
## Release 2.22.0 (2019-10-02)
|
||||||
|
|
||||||
### Added
|
### Added
|
@ -1,5 +1,4 @@
|
|||||||
Sprig
|
Copyright (C) 2013-2020 Masterminds
|
||||||
Copyright (C) 2013 Masterminds
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
@ -0,0 +1,9 @@
|
|||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
@echo "==> Running tests"
|
||||||
|
GO111MODULE=on go test -v
|
||||||
|
|
||||||
|
.PHONY: test-cover
|
||||||
|
test-cover:
|
||||||
|
@echo "==> Running Tests with coverage"
|
||||||
|
GO111MODULE=on go test -cover .
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Sprig: Template functions for Go.
|
Package sprig provides template functions for Go.
|
||||||
|
|
||||||
This package contains a number of utility functions for working with data
|
This package contains a number of utility functions for working with data
|
||||||
inside of Go `html/template` and `text/template` files.
|
inside of Go `html/template` and `text/template` files.
|
215
vendor/github.com/Masterminds/sprig/list.go → vendor/github.com/Masterminds/sprig/v3/list.go
generated
vendored
215
vendor/github.com/Masterminds/sprig/list.go → vendor/github.com/Masterminds/sprig/v3/list.go
generated
vendored
@ -0,0 +1,186 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toFloat64 converts 64-bit floats
|
||||||
|
func toFloat64(v interface{}) float64 {
|
||||||
|
return cast.ToFloat64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toInt(v interface{}) int {
|
||||||
|
return cast.ToInt(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInt64 converts integer types to 64-bit integers
|
||||||
|
func toInt64(v interface{}) int64 {
|
||||||
|
return cast.ToInt64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb > aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxf(a interface{}, i ...interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toFloat64(b)
|
||||||
|
aa = math.Max(aa, bb)
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb < aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func minf(a interface{}, i ...interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toFloat64(b)
|
||||||
|
aa = math.Min(aa, bb)
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func until(count int) []int {
|
||||||
|
step := 1
|
||||||
|
if count < 0 {
|
||||||
|
step = -1
|
||||||
|
}
|
||||||
|
return untilStep(0, count, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
func untilStep(start, stop, step int) []int {
|
||||||
|
v := []int{}
|
||||||
|
|
||||||
|
if stop < start {
|
||||||
|
if step >= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i > stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
if step <= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i < stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func floor(a interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
return math.Floor(aa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ceil(a interface{}) float64 {
|
||||||
|
aa := toFloat64(a)
|
||||||
|
return math.Ceil(aa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func round(a interface{}, p int, rOpt ...float64) float64 {
|
||||||
|
roundOn := .5
|
||||||
|
if len(rOpt) > 0 {
|
||||||
|
roundOn = rOpt[0]
|
||||||
|
}
|
||||||
|
val := toFloat64(a)
|
||||||
|
places := toFloat64(p)
|
||||||
|
|
||||||
|
var round float64
|
||||||
|
pow := math.Pow(10, places)
|
||||||
|
digit := pow * val
|
||||||
|
_, div := math.Modf(digit)
|
||||||
|
if div >= roundOn {
|
||||||
|
round = math.Ceil(digit)
|
||||||
|
} else {
|
||||||
|
round = math.Floor(digit)
|
||||||
|
}
|
||||||
|
return round / pow
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts unix octal to decimal
|
||||||
|
func toDecimal(v interface{}) int64 {
|
||||||
|
result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func seq(params ...int) string {
|
||||||
|
increment := 1
|
||||||
|
switch len(params) {
|
||||||
|
case 0:
|
||||||
|
return ""
|
||||||
|
case 1:
|
||||||
|
start := 1
|
||||||
|
end := params[0]
|
||||||
|
if end < start {
|
||||||
|
increment = -1
|
||||||
|
}
|
||||||
|
return intArrayToString(untilStep(start, end+increment, increment), " ")
|
||||||
|
case 3:
|
||||||
|
start := params[0]
|
||||||
|
end := params[2]
|
||||||
|
step := params[1]
|
||||||
|
if end < start {
|
||||||
|
increment = -1
|
||||||
|
if step > 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return intArrayToString(untilStep(start, end+increment, step), " ")
|
||||||
|
case 2:
|
||||||
|
start := params[0]
|
||||||
|
end := params[1]
|
||||||
|
step := 1
|
||||||
|
if end < start {
|
||||||
|
step = -1
|
||||||
|
}
|
||||||
|
return intArrayToString(untilStep(start, end+step, step), " ")
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func intArrayToString(slice []int, delimeter string) string {
|
||||||
|
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]")
|
||||||
|
}
|
||||||
|
|
||||||
|
// performs a float and subsequent decimal.Decimal conversion on inputs,
|
||||||
|
// and iterates through a and b executing the mathmetical operation f
|
||||||
|
func execDecimalOp(a interface{}, b []interface{}, f func(d1, d2 decimal.Decimal) decimal.Decimal) float64 {
|
||||||
|
prt := decimal.NewFromFloat(toFloat64(a))
|
||||||
|
for _, x := range b {
|
||||||
|
dx := decimal.NewFromFloat(toFloat64(x))
|
||||||
|
prt = f(prt, dx)
|
||||||
|
}
|
||||||
|
rslt, _ := prt.Float64()
|
||||||
|
return rslt
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func regexMatch(regex string, s string) bool {
|
||||||
|
match, _ := regexp.MatchString(regex, s)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexMatch(regex string, s string) (bool, error) {
|
||||||
|
return regexp.MatchString(regex, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexFindAll(regex string, s string, n int) []string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.FindAllString(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexFindAll(regex string, s string, n int) ([]string, error) {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
return r.FindAllString(s, n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexFind(regex string, s string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.FindString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexFind(regex string, s string) (string, error) {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return r.FindString(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexReplaceAll(regex string, s string, repl string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.ReplaceAllString(s, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexReplaceAll(regex string, s string, repl string) (string, error) {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return r.ReplaceAllString(s, repl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexReplaceAllLiteral(regex string, s string, repl string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.ReplaceAllLiteralString(s, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexReplaceAllLiteral(regex string, s string, repl string) (string, error) {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return r.ReplaceAllLiteralString(s, repl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexSplit(regex string, s string, n int) []string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.Split(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRegexSplit(regex string, s string, n int) ([]string, error) {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
return r.Split(s, n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexQuoteMeta(s string) string {
|
||||||
|
return regexp.QuoteMeta(s)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package sprig
|
package sprig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sv2 "github.com/Masterminds/semver"
|
sv2 "github.com/Masterminds/semver/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func semverCompare(constraint, version string) (bool, error) {
|
func semverCompare(constraint, version string) (bool, error) {
|
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2021 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var jsonNull = []byte("null")
|
||||||
|
|
||||||
|
// NullUUID represents a UUID that may be null.
|
||||||
|
// NullUUID implements the SQL driver.Scanner interface so
|
||||||
|
// it can be used as a scan destination:
|
||||||
|
//
|
||||||
|
// var u uuid.NullUUID
|
||||||
|
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
|
||||||
|
// ...
|
||||||
|
// if u.Valid {
|
||||||
|
// // use u.UUID
|
||||||
|
// } else {
|
||||||
|
// // NULL value
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type NullUUID struct {
|
||||||
|
UUID UUID
|
||||||
|
Valid bool // Valid is true if UUID is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the SQL driver.Scanner interface.
|
||||||
|
func (nu *NullUUID) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
nu.UUID, nu.Valid = Nil, false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := nu.UUID.Scan(value)
|
||||||
|
if err != nil {
|
||||||
|
nu.Valid = false
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nu.Valid = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (nu NullUUID) Value() (driver.Value, error) {
|
||||||
|
if !nu.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// Delegate to UUID Value function
|
||||||
|
return nu.UUID.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||||
|
func (nu NullUUID) MarshalBinary() ([]byte, error) {
|
||||||
|
if nu.Valid {
|
||||||
|
return nu.UUID[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||||
|
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
|
||||||
|
if len(data) != 16 {
|
||||||
|
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||||
|
}
|
||||||
|
copy(nu.UUID[:], data)
|
||||||
|
nu.Valid = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (nu NullUUID) MarshalText() ([]byte, error) {
|
||||||
|
if nu.Valid {
|
||||||
|
return nu.UUID.MarshalText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonNull, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (nu *NullUUID) UnmarshalText(data []byte) error {
|
||||||
|
id, err := ParseBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
nu.Valid = false
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nu.UUID = id
|
||||||
|
nu.Valid = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (nu NullUUID) MarshalJSON() ([]byte, error) {
|
||||||
|
if nu.Valid {
|
||||||
|
return json.Marshal(nu.UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonNull, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
|
||||||
|
if bytes.Equal(data, jsonNull) {
|
||||||
|
*nu = NullUUID{}
|
||||||
|
return nil // valid null UUID
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(data, &nu.UUID)
|
||||||
|
nu.Valid = err == nil
|
||||||
|
return err
|
||||||
|
}
|
129
vendor/github.com/hashicorp/terraform-plugin-docs/internal/provider/generate.go
generated
vendored
129
vendor/github.com/hashicorp/terraform-plugin-docs/internal/provider/generate.go
generated
vendored
48
vendor/github.com/hashicorp/terraform-plugin-docs/internal/provider/template.go
generated
vendored
48
vendor/github.com/hashicorp/terraform-plugin-docs/internal/provider/template.go
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
|
|
||||||
language: go
|
|
||||||
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
go:
|
|
||||||
- "1.14"
|
|
||||||
- "1.15"
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
script: make updatedeps test testrace
|
|
@ -1,2 +1,4 @@
|
|||||||
.idea
|
.idea
|
||||||
coverage.txt
|
coverage.txt
|
||||||
|
gocomplete/gocomplete
|
||||||
|
example/self/self
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
language: go
|
language: go
|
||||||
sudo: false
|
|
||||||
go:
|
go:
|
||||||
- 1.9
|
- tip
|
||||||
- 1.8
|
- 1.12.x
|
||||||
|
- 1.11.x
|
||||||
before_install:
|
- 1.10.x
|
||||||
- go get -u -t ./...
|
|
||||||
- go get -u gopkg.in/alecthomas/gometalinter.v1
|
|
||||||
- gometalinter.v1 --install
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- gometalinter.v1 --config metalinter.json ./...
|
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
- ./test.sh
|
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Package complete provides a tool for bash writing bash completion in go, and bash completion for the go command line.
|
||||||
|
|
||||||
|
Writing bash completion scripts is a hard work. This package provides an easy way
|
||||||
|
to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||||
|
the completion of the command.
|
||||||
|
|
||||||
|
Go Command Bash Completion
|
||||||
|
|
||||||
|
In ./cmd/gocomplete there is an example for bash completion for the `go` command line.
|
||||||
|
|
||||||
|
This is an example that uses the `complete` package on the `go` command - the `complete` package
|
||||||
|
can also be used to implement any completions, see #usage.
|
||||||
|
|
||||||
|
Install
|
||||||
|
|
||||||
|
1. Type in your shell:
|
||||||
|
|
||||||
|
go get -u github.com/posener/complete/gocomplete
|
||||||
|
gocomplete -install
|
||||||
|
|
||||||
|
2. Restart your shell
|
||||||
|
|
||||||
|
Uninstall by `gocomplete -uninstall`
|
||||||
|
|
||||||
|
Features
|
||||||
|
|
||||||
|
- Complete `go` command, including sub commands and all flags.
|
||||||
|
- Complete packages names or `.go` files when necessary.
|
||||||
|
- Complete test names after `-run` flag.
|
||||||
|
|
||||||
|
Complete package
|
||||||
|
|
||||||
|
Supported shells:
|
||||||
|
|
||||||
|
- [x] bash
|
||||||
|
- [x] zsh
|
||||||
|
- [x] fish
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
Assuming you have program called `run` and you want to have bash completion
|
||||||
|
for it, meaning, if you type `run` then space, then press the `Tab` key,
|
||||||
|
the shell will suggest relevant complete options.
|
||||||
|
|
||||||
|
In that case, we will create a program called `runcomplete`, a go program,
|
||||||
|
with a `func main()` and so, that will make the completion of the `run`
|
||||||
|
program. Once the `runcomplete` will be in a binary form, we could
|
||||||
|
`runcomplete -install` and that will add to our shell all the bash completion
|
||||||
|
options for `run`.
|
||||||
|
|
||||||
|
So here it is:
|
||||||
|
|
||||||
|
import "github.com/posener/complete"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// create a Command object, that represents the command we want
|
||||||
|
// to complete.
|
||||||
|
run := complete.Command{
|
||||||
|
|
||||||
|
// Sub defines a list of sub commands of the program,
|
||||||
|
// this is recursive, since every command is of type command also.
|
||||||
|
Sub: complete.Commands{
|
||||||
|
|
||||||
|
// add a build sub command
|
||||||
|
"build": complete.Command {
|
||||||
|
|
||||||
|
// define flags of the build sub command
|
||||||
|
Flags: complete.Flags{
|
||||||
|
// build sub command has a flag '-cpus', which
|
||||||
|
// expects number of cpus after it. in that case
|
||||||
|
// anything could complete this flag.
|
||||||
|
"-cpus": complete.PredictAnything,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// define flags of the 'run' main command
|
||||||
|
Flags: complete.Flags{
|
||||||
|
// a flag -o, which expects a file ending with .out after
|
||||||
|
// it, the tab completion will auto complete for files matching
|
||||||
|
// the given pattern.
|
||||||
|
"-o": complete.PredictFiles("*.out"),
|
||||||
|
},
|
||||||
|
|
||||||
|
// define global flags of the 'run' main command
|
||||||
|
// those will show up also when a sub command was entered in the
|
||||||
|
// command line
|
||||||
|
GlobalFlags: complete.Flags{
|
||||||
|
|
||||||
|
// a flag '-h' which does not expects anything after it
|
||||||
|
"-h": complete.PredictNothing,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the command completion, as part of the main() function.
|
||||||
|
// this triggers the autocompletion when needed.
|
||||||
|
// name must be exactly as the binary that we want to complete.
|
||||||
|
complete.New("run", run).Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
Self completing program
|
||||||
|
|
||||||
|
In case that the program that we want to complete is written in go we
|
||||||
|
can make it self completing.
|
||||||
|
Here is an example: ./example/self/main.go .
|
||||||
|
|
||||||
|
*/
|
||||||
|
package complete
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"badges": {
|
||||||
|
"travis_ci": true,
|
||||||
|
"code_cov": true,
|
||||||
|
"golang_ci": true,
|
||||||
|
"go_doc": true,
|
||||||
|
"goreadme": true
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package match
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// File returns true if prefix can match the file
|
|
||||||
func File(file, prefix string) bool {
|
|
||||||
// special case for current directory completion
|
|
||||||
if file == "./" && (prefix == "." || prefix == "") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if prefix == "." && strings.HasPrefix(file, ".") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
file = strings.TrimPrefix(file, "./")
|
|
||||||
prefix = strings.TrimPrefix(prefix, "./")
|
|
||||||
|
|
||||||
return strings.HasPrefix(file, prefix)
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package match
|
|
||||||
|
|
||||||
// Match matches two strings
|
|
||||||
// it is used for comparing a term to the last typed
|
|
||||||
// word, the prefix, and see if it is a possible auto complete option.
|
|
||||||
type Match func(term, prefix string) bool
|
|
@ -1,9 +0,0 @@
|
|||||||
package match
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// Prefix is a simple Matcher, if the word is it's prefix, there is a match
|
|
||||||
// Match returns true if a has the prefix as prefix
|
|
||||||
func Prefix(long, prefix string) bool {
|
|
||||||
return strings.HasPrefix(long, prefix)
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"Vendor": true,
|
|
||||||
"DisableAll": true,
|
|
||||||
"Enable": [
|
|
||||||
"gofmt",
|
|
||||||
"goimports",
|
|
||||||
"interfacer",
|
|
||||||
"goconst",
|
|
||||||
"misspell",
|
|
||||||
"unconvert",
|
|
||||||
"gosimple",
|
|
||||||
"golint",
|
|
||||||
"structcheck",
|
|
||||||
"deadcode",
|
|
||||||
"vet"
|
|
||||||
],
|
|
||||||
"Exclude": [
|
|
||||||
"initTests is unused"
|
|
||||||
],
|
|
||||||
"Deadline": "2m"
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
echo "" > coverage.txt
|
|
||||||
|
|
||||||
for d in $(go list ./... | grep -v vendor); do
|
|
||||||
go test -v -race -coverprofile=profile.out -covermode=atomic $d
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out >> coverage.txt
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
done
|
|
@ -1,46 +0,0 @@
|
|||||||
package complete
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fixPathForm changes a file name to a relative name
|
|
||||||
func fixPathForm(last string, file string) string {
|
|
||||||
// get wording directory for relative name
|
|
||||||
workDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
abs, err := filepath.Abs(file)
|
|
||||||
if err != nil {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// if last is absolute, return path as absolute
|
|
||||||
if filepath.IsAbs(last) {
|
|
||||||
return fixDirPath(abs)
|
|
||||||
}
|
|
||||||
|
|
||||||
rel, err := filepath.Rel(workDir, abs)
|
|
||||||
if err != nil {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix ./ prefix of path
|
|
||||||
if rel != "." && strings.HasPrefix(last, ".") {
|
|
||||||
rel = "./" + rel
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixDirPath(rel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixDirPath(path string) string {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err == nil && info.IsDir() && !strings.HasSuffix(path, "/") {
|
|
||||||
path += "/"
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
@ -0,0 +1,9 @@
|
|||||||
|
.git
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# VS code
|
||||||
|
*.code-workspace
|
@ -0,0 +1,19 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- ppc64le
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.14.x
|
||||||
|
- 1.15.x
|
||||||
|
- 1.16.x
|
||||||
|
- 1.17.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go build .
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v
|
@ -0,0 +1,49 @@
|
|||||||
|
## Decimal v1.3.1
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Reduce memory allocation in case of initialization from big.Int [#252](https://github.com/shopspring/decimal/pull/252)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Fix binary marshalling of decimal zero value [#253](https://github.com/shopspring/decimal/pull/253)
|
||||||
|
|
||||||
|
## Decimal v1.3.0
|
||||||
|
|
||||||
|
#### FEATURES
|
||||||
|
- Add NewFromFormattedString initializer [#184](https://github.com/shopspring/decimal/pull/184)
|
||||||
|
- Add NewNullDecimal initializer [#234](https://github.com/shopspring/decimal/pull/234)
|
||||||
|
- Add implementation of natural exponent function (Taylor, Hull-Abraham) [#229](https://github.com/shopspring/decimal/pull/229)
|
||||||
|
- Add RoundUp, RoundDown, RoundCeil, RoundFloor methods [#196](https://github.com/shopspring/decimal/pull/196) [#202](https://github.com/shopspring/decimal/pull/202) [#220](https://github.com/shopspring/decimal/pull/220)
|
||||||
|
- Add XML support for NullDecimal [#192](https://github.com/shopspring/decimal/pull/192)
|
||||||
|
- Add IsInteger method [#179](https://github.com/shopspring/decimal/pull/179)
|
||||||
|
- Add Copy helper method [#123](https://github.com/shopspring/decimal/pull/123)
|
||||||
|
- Add InexactFloat64 helper method [#205](https://github.com/shopspring/decimal/pull/205)
|
||||||
|
- Add CoefficientInt64 helper method [#244](https://github.com/shopspring/decimal/pull/244)
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Performance optimization of NewFromString init method [#198](https://github.com/shopspring/decimal/pull/198)
|
||||||
|
- Performance optimization of Abs and Round methods [#240](https://github.com/shopspring/decimal/pull/240)
|
||||||
|
- Additional tests (CI) for ppc64le architecture [#188](https://github.com/shopspring/decimal/pull/188)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Fix rounding in FormatFloat fallback path (roundShortest method, fix taken from Go main repository) [#161](https://github.com/shopspring/decimal/pull/161)
|
||||||
|
- Add slice range checks to UnmarshalBinary method [#232](https://github.com/shopspring/decimal/pull/232)
|
||||||
|
|
||||||
|
## Decimal v1.2.0
|
||||||
|
|
||||||
|
#### BREAKING
|
||||||
|
- Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172)
|
||||||
|
|
||||||
|
#### FEATURES
|
||||||
|
- Add NewFromInt and NewFromInt32 initializers [#72](https://github.com/shopspring/decimal/pull/72)
|
||||||
|
- Add support for Go modules [#157](https://github.com/shopspring/decimal/pull/157)
|
||||||
|
- Add BigInt, BigFloat helper methods [#171](https://github.com/shopspring/decimal/pull/171)
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Memory usage optimization [#160](https://github.com/shopspring/decimal/pull/160)
|
||||||
|
- Updated travis CI golang versions [#156](https://github.com/shopspring/decimal/pull/156)
|
||||||
|
- Update documentation [#173](https://github.com/shopspring/decimal/pull/173)
|
||||||
|
- Improve code quality [#174](https://github.com/shopspring/decimal/pull/174)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Revert remove insignificant digits [#159](https://github.com/shopspring/decimal/pull/159)
|
||||||
|
- Remove 15 interval for RoundCash [#166](https://github.com/shopspring/decimal/pull/166)
|
@ -0,0 +1,45 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Spring, Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
- Based on https://github.com/oguzbilgic/fpd, which has the following license:
|
||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Oguz Bilgic
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
@ -0,0 +1,130 @@
|
|||||||
|
# decimal
|
||||||
|
|
||||||
|
[![Build Status](https://app.travis-ci.com/shopspring/decimal.svg?branch=master)](https://app.travis-ci.com/shopspring/decimal) [![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)
|
||||||
|
|
||||||
|
Arbitrary-precision fixed-point decimal numbers in go.
|
||||||
|
|
||||||
|
_Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* The zero-value is 0, and is safe to use without initialization
|
||||||
|
* Addition, subtraction, multiplication with no loss of precision
|
||||||
|
* Division with specified precision
|
||||||
|
* Database/sql serialization/deserialization
|
||||||
|
* JSON and XML serialization/deserialization
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Run `go get github.com/shopspring/decimal`
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Decimal library requires Go version `>=1.7`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
price, err := decimal.NewFromString("136.02")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
quantity := decimal.NewFromInt(3)
|
||||||
|
|
||||||
|
fee, _ := decimal.NewFromString(".035")
|
||||||
|
taxRate, _ := decimal.NewFromString(".08875")
|
||||||
|
|
||||||
|
subtotal := price.Mul(quantity)
|
||||||
|
|
||||||
|
preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))
|
||||||
|
|
||||||
|
total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))
|
||||||
|
|
||||||
|
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
|
||||||
|
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
|
||||||
|
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
|
||||||
|
fmt.Println("Total:", total) // Total: 459.824961375
|
||||||
|
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
http://godoc.org/github.com/shopspring/decimal
|
||||||
|
|
||||||
|
## Production Usage
|
||||||
|
|
||||||
|
* [Spring](https://shopspring.com/), since August 14, 2014.
|
||||||
|
* If you are using this in production, please let us know!
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
#### Why don't you just use float64?
|
||||||
|
|
||||||
|
Because float64 (or any binary floating point type, actually) can't represent
|
||||||
|
numbers such as `0.1` exactly.
|
||||||
|
|
||||||
|
Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that
|
||||||
|
it prints out `10`, but it actually prints `9.999999999999831`. Over time,
|
||||||
|
these small errors can really add up!
|
||||||
|
|
||||||
|
#### Why don't you just use big.Rat?
|
||||||
|
|
||||||
|
big.Rat is fine for representing rational numbers, but Decimal is better for
|
||||||
|
representing money. Why? Here's a (contrived) example:
|
||||||
|
|
||||||
|
Let's say you use big.Rat, and you have two numbers, x and y, both
|
||||||
|
representing 1/3, and you have `z = 1 - x - y = 1/3`. If you print each one
|
||||||
|
out, the string output has to stop somewhere (let's say it stops at 3 decimal
|
||||||
|
digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did
|
||||||
|
the other 0.001 go?
|
||||||
|
|
||||||
|
Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE
|
||||||
|
|
||||||
|
With Decimal, the strings being printed out represent the number exactly. So,
|
||||||
|
if you have `x = y = 1/3` (with precision 3), they will actually be equal to
|
||||||
|
0.333, and when you do `z = 1 - x - y`, `z` will be equal to .334. No money is
|
||||||
|
unaccounted for!
|
||||||
|
|
||||||
|
You still have to be careful. If you want to split a number `N` 3 ways, you
|
||||||
|
can't just send `N/3` to three different people. You have to pick one to send
|
||||||
|
`N - (2/3*N)` to. That person will receive the fraction of a penny remainder.
|
||||||
|
|
||||||
|
But, it is much easier to be careful with Decimal than with big.Rat.
|
||||||
|
|
||||||
|
#### Why isn't the API similar to big.Int's?
|
||||||
|
|
||||||
|
big.Int's API is built to reduce the number of memory allocations for maximal
|
||||||
|
performance. This makes sense for its use-case, but the trade-off is that the
|
||||||
|
API is awkward and easy to misuse.
|
||||||
|
|
||||||
|
For example, to add two big.Ints, you do: `z := new(big.Int).Add(x, y)`. A
|
||||||
|
developer unfamiliar with this API might try to do `z := a.Add(a, b)`. This
|
||||||
|
modifies `a` and sets `z` as an alias for `a`, which they might not expect. It
|
||||||
|
also modifies any other aliases to `a`.
|
||||||
|
|
||||||
|
Here's an example of the subtle bugs you can introduce with big.Int's API:
|
||||||
|
https://play.golang.org/p/x2R_78pa8r
|
||||||
|
|
||||||
|
In contrast, it's difficult to make such mistakes with decimal. Decimals
|
||||||
|
behave like other go numbers types: even though `a = b` will not deep copy
|
||||||
|
`b` into `a`, it is impossible to modify a Decimal, since all Decimal methods
|
||||||
|
return new Decimals and do not modify the originals. The downside is that
|
||||||
|
this causes extra allocations, so Decimal is less performant. My assumption
|
||||||
|
is that if you're using Decimals, you probably care more about correctness
|
||||||
|
than performance.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
This is a heavily modified fork of [fpd.Decimal](https://github.com/oguzbilgic/fpd), which was also released under the MIT License.
|
@ -0,0 +1,415 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Multiprecision decimal numbers.
|
||||||
|
// For floating-point formatting only; not general purpose.
|
||||||
|
// Only operations are assign and (binary) left/right shift.
|
||||||
|
// Can do binary floating point in multiprecision decimal precisely
|
||||||
|
// because 2 divides 10; cannot do decimal floating point
|
||||||
|
// in multiprecision binary precisely.
|
||||||
|
|
||||||
|
package decimal
|
||||||
|
|
||||||
|
type decimal struct {
|
||||||
|
d [800]byte // digits, big-endian representation
|
||||||
|
nd int // number of digits used
|
||||||
|
dp int // decimal point
|
||||||
|
neg bool // negative flag
|
||||||
|
trunc bool // discarded nonzero digits beyond d[:nd]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *decimal) String() string {
|
||||||
|
n := 10 + a.nd
|
||||||
|
if a.dp > 0 {
|
||||||
|
n += a.dp
|
||||||
|
}
|
||||||
|
if a.dp < 0 {
|
||||||
|
n += -a.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, n)
|
||||||
|
w := 0
|
||||||
|
switch {
|
||||||
|
case a.nd == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
case a.dp <= 0:
|
||||||
|
// zeros fill space between decimal point and digits
|
||||||
|
buf[w] = '0'
|
||||||
|
w++
|
||||||
|
buf[w] = '.'
|
||||||
|
w++
|
||||||
|
w += digitZero(buf[w : w+-a.dp])
|
||||||
|
w += copy(buf[w:], a.d[0:a.nd])
|
||||||
|
|
||||||
|
case a.dp < a.nd:
|
||||||
|
// decimal point in middle of digits
|
||||||
|
w += copy(buf[w:], a.d[0:a.dp])
|
||||||
|
buf[w] = '.'
|
||||||
|
w++
|
||||||
|
w += copy(buf[w:], a.d[a.dp:a.nd])
|
||||||
|
|
||||||
|
default:
|
||||||
|
// zeros fill space between digits and decimal point
|
||||||
|
w += copy(buf[w:], a.d[0:a.nd])
|
||||||
|
w += digitZero(buf[w : w+a.dp-a.nd])
|
||||||
|
}
|
||||||
|
return string(buf[0:w])
|
||||||
|
}
|
||||||
|
|
||||||
|
func digitZero(dst []byte) int {
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = '0'
|
||||||
|
}
|
||||||
|
return len(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing zeros from number.
|
||||||
|
// (They are meaningless; the decimal point is tracked
|
||||||
|
// independent of the number of digits.)
|
||||||
|
func trim(a *decimal) {
|
||||||
|
for a.nd > 0 && a.d[a.nd-1] == '0' {
|
||||||
|
a.nd--
|
||||||
|
}
|
||||||
|
if a.nd == 0 {
|
||||||
|
a.dp = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign v to a.
|
||||||
|
func (a *decimal) Assign(v uint64) {
|
||||||
|
var buf [24]byte
|
||||||
|
|
||||||
|
// Write reversed decimal in buf.
|
||||||
|
n := 0
|
||||||
|
for v > 0 {
|
||||||
|
v1 := v / 10
|
||||||
|
v -= 10 * v1
|
||||||
|
buf[n] = byte(v + '0')
|
||||||
|
n++
|
||||||
|
v = v1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse again to produce forward decimal in a.d.
|
||||||
|
a.nd = 0
|
||||||
|
for n--; n >= 0; n-- {
|
||||||
|
a.d[a.nd] = buf[n]
|
||||||
|
a.nd++
|
||||||
|
}
|
||||||
|
a.dp = a.nd
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum shift that we can do in one pass without overflow.
|
||||||
|
// A uint has 32 or 64 bits, and we have to be able to accommodate 9<<k.
|
||||||
|
const uintSize = 32 << (^uint(0) >> 63)
|
||||||
|
const maxShift = uintSize - 4
|
||||||
|
|
||||||
|
// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow.
|
||||||
|
func rightShift(a *decimal, k uint) {
|
||||||
|
r := 0 // read pointer
|
||||||
|
w := 0 // write pointer
|
||||||
|
|
||||||
|
// Pick up enough leading digits to cover first shift.
|
||||||
|
var n uint
|
||||||
|
for ; n>>k == 0; r++ {
|
||||||
|
if r >= a.nd {
|
||||||
|
if n == 0 {
|
||||||
|
// a == 0; shouldn't get here, but handle anyway.
|
||||||
|
a.nd = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for n>>k == 0 {
|
||||||
|
n = n * 10
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := uint(a.d[r])
|
||||||
|
n = n*10 + c - '0'
|
||||||
|
}
|
||||||
|
a.dp -= r - 1
|
||||||
|
|
||||||
|
var mask uint = (1 << k) - 1
|
||||||
|
|
||||||
|
// Pick up a digit, put down a digit.
|
||||||
|
for ; r < a.nd; r++ {
|
||||||
|
c := uint(a.d[r])
|
||||||
|
dig := n >> k
|
||||||
|
n &= mask
|
||||||
|
a.d[w] = byte(dig + '0')
|
||||||
|
w++
|
||||||
|
n = n*10 + c - '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put down extra digits.
|
||||||
|
for n > 0 {
|
||||||
|
dig := n >> k
|
||||||
|
n &= mask
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(dig + '0')
|
||||||
|
w++
|
||||||
|
} else if dig > 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = n * 10
|
||||||
|
}
|
||||||
|
|
||||||
|
a.nd = w
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cheat sheet for left shift: table indexed by shift count giving
|
||||||
|
// number of new digits that will be introduced by that shift.
|
||||||
|
//
|
||||||
|
// For example, leftcheats[4] = {2, "625"}. That means that
|
||||||
|
// if we are shifting by 4 (multiplying by 16), it will add 2 digits
|
||||||
|
// when the string prefix is "625" through "999", and one fewer digit
|
||||||
|
// if the string prefix is "000" through "624".
|
||||||
|
//
|
||||||
|
// Credit for this trick goes to Ken.
|
||||||
|
|
||||||
|
type leftCheat struct {
|
||||||
|
delta int // number of new digits
|
||||||
|
cutoff string // minus one digit if original < a.
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftcheats = []leftCheat{
|
||||||
|
// Leading digits of 1/2^i = 5^i.
|
||||||
|
// 5^23 is not an exact 64-bit floating point number,
|
||||||
|
// so have to use bc for the math.
|
||||||
|
// Go up to 60 to be large enough for 32bit and 64bit platforms.
|
||||||
|
/*
|
||||||
|
seq 60 | sed 's/^/5^/' | bc |
|
||||||
|
awk 'BEGIN{ print "\t{ 0, \"\" }," }
|
||||||
|
{
|
||||||
|
log2 = log(2)/log(10)
|
||||||
|
printf("\t{ %d, \"%s\" },\t// * %d\n",
|
||||||
|
int(log2*NR+1), $0, 2**NR)
|
||||||
|
}'
|
||||||
|
*/
|
||||||
|
{0, ""},
|
||||||
|
{1, "5"}, // * 2
|
||||||
|
{1, "25"}, // * 4
|
||||||
|
{1, "125"}, // * 8
|
||||||
|
{2, "625"}, // * 16
|
||||||
|
{2, "3125"}, // * 32
|
||||||
|
{2, "15625"}, // * 64
|
||||||
|
{3, "78125"}, // * 128
|
||||||
|
{3, "390625"}, // * 256
|
||||||
|
{3, "1953125"}, // * 512
|
||||||
|
{4, "9765625"}, // * 1024
|
||||||
|
{4, "48828125"}, // * 2048
|
||||||
|
{4, "244140625"}, // * 4096
|
||||||
|
{4, "1220703125"}, // * 8192
|
||||||
|
{5, "6103515625"}, // * 16384
|
||||||
|
{5, "30517578125"}, // * 32768
|
||||||
|
{5, "152587890625"}, // * 65536
|
||||||
|
{6, "762939453125"}, // * 131072
|
||||||
|
{6, "3814697265625"}, // * 262144
|
||||||
|
{6, "19073486328125"}, // * 524288
|
||||||
|
{7, "95367431640625"}, // * 1048576
|
||||||
|
{7, "476837158203125"}, // * 2097152
|
||||||
|
{7, "2384185791015625"}, // * 4194304
|
||||||
|
{7, "11920928955078125"}, // * 8388608
|
||||||
|
{8, "59604644775390625"}, // * 16777216
|
||||||
|
{8, "298023223876953125"}, // * 33554432
|
||||||
|
{8, "1490116119384765625"}, // * 67108864
|
||||||
|
{9, "7450580596923828125"}, // * 134217728
|
||||||
|
{9, "37252902984619140625"}, // * 268435456
|
||||||
|
{9, "186264514923095703125"}, // * 536870912
|
||||||
|
{10, "931322574615478515625"}, // * 1073741824
|
||||||
|
{10, "4656612873077392578125"}, // * 2147483648
|
||||||
|
{10, "23283064365386962890625"}, // * 4294967296
|
||||||
|
{10, "116415321826934814453125"}, // * 8589934592
|
||||||
|
{11, "582076609134674072265625"}, // * 17179869184
|
||||||
|
{11, "2910383045673370361328125"}, // * 34359738368
|
||||||
|
{11, "14551915228366851806640625"}, // * 68719476736
|
||||||
|
{12, "72759576141834259033203125"}, // * 137438953472
|
||||||
|
{12, "363797880709171295166015625"}, // * 274877906944
|
||||||
|
{12, "1818989403545856475830078125"}, // * 549755813888
|
||||||
|
{13, "9094947017729282379150390625"}, // * 1099511627776
|
||||||
|
{13, "45474735088646411895751953125"}, // * 2199023255552
|
||||||
|
{13, "227373675443232059478759765625"}, // * 4398046511104
|
||||||
|
{13, "1136868377216160297393798828125"}, // * 8796093022208
|
||||||
|
{14, "5684341886080801486968994140625"}, // * 17592186044416
|
||||||
|
{14, "28421709430404007434844970703125"}, // * 35184372088832
|
||||||
|
{14, "142108547152020037174224853515625"}, // * 70368744177664
|
||||||
|
{15, "710542735760100185871124267578125"}, // * 140737488355328
|
||||||
|
{15, "3552713678800500929355621337890625"}, // * 281474976710656
|
||||||
|
{15, "17763568394002504646778106689453125"}, // * 562949953421312
|
||||||
|
{16, "88817841970012523233890533447265625"}, // * 1125899906842624
|
||||||
|
{16, "444089209850062616169452667236328125"}, // * 2251799813685248
|
||||||
|
{16, "2220446049250313080847263336181640625"}, // * 4503599627370496
|
||||||
|
{16, "11102230246251565404236316680908203125"}, // * 9007199254740992
|
||||||
|
{17, "55511151231257827021181583404541015625"}, // * 18014398509481984
|
||||||
|
{17, "277555756156289135105907917022705078125"}, // * 36028797018963968
|
||||||
|
{17, "1387778780781445675529539585113525390625"}, // * 72057594037927936
|
||||||
|
{18, "6938893903907228377647697925567626953125"}, // * 144115188075855872
|
||||||
|
{18, "34694469519536141888238489627838134765625"}, // * 288230376151711744
|
||||||
|
{18, "173472347597680709441192448139190673828125"}, // * 576460752303423488
|
||||||
|
{19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the leading prefix of b lexicographically less than s?
|
||||||
|
func prefixIsLessThan(b []byte, s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if i >= len(b) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b[i] != s[i] {
|
||||||
|
return b[i] < s[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow.
|
||||||
|
func leftShift(a *decimal, k uint) {
|
||||||
|
delta := leftcheats[k].delta
|
||||||
|
if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) {
|
||||||
|
delta--
|
||||||
|
}
|
||||||
|
|
||||||
|
r := a.nd // read index
|
||||||
|
w := a.nd + delta // write index
|
||||||
|
|
||||||
|
// Pick up a digit, put down a digit.
|
||||||
|
var n uint
|
||||||
|
for r--; r >= 0; r-- {
|
||||||
|
n += (uint(a.d[r]) - '0') << k
|
||||||
|
quo := n / 10
|
||||||
|
rem := n - 10*quo
|
||||||
|
w--
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(rem + '0')
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = quo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put down extra digits.
|
||||||
|
for n > 0 {
|
||||||
|
quo := n / 10
|
||||||
|
rem := n - 10*quo
|
||||||
|
w--
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(rem + '0')
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = quo
|
||||||
|
}
|
||||||
|
|
||||||
|
a.nd += delta
|
||||||
|
if a.nd >= len(a.d) {
|
||||||
|
a.nd = len(a.d)
|
||||||
|
}
|
||||||
|
a.dp += delta
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary shift left (k > 0) or right (k < 0).
|
||||||
|
func (a *decimal) Shift(k int) {
|
||||||
|
switch {
|
||||||
|
case a.nd == 0:
|
||||||
|
// nothing to do: a == 0
|
||||||
|
case k > 0:
|
||||||
|
for k > maxShift {
|
||||||
|
leftShift(a, maxShift)
|
||||||
|
k -= maxShift
|
||||||
|
}
|
||||||
|
leftShift(a, uint(k))
|
||||||
|
case k < 0:
|
||||||
|
for k < -maxShift {
|
||||||
|
rightShift(a, maxShift)
|
||||||
|
k += maxShift
|
||||||
|
}
|
||||||
|
rightShift(a, uint(-k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we chop a at nd digits, should we round up?
|
||||||
|
func shouldRoundUp(a *decimal, nd int) bool {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
|
||||||
|
// if we truncated, a little higher than what's recorded - always round up
|
||||||
|
if a.trunc {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return nd > 0 && (a.d[nd-1]-'0')%2 != 0
|
||||||
|
}
|
||||||
|
// not halfway - digit tells all
|
||||||
|
return a.d[nd] >= '5'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a to nd digits (or fewer).
|
||||||
|
// If nd is zero, it means we're rounding
|
||||||
|
// just to the left of the digits, as in
|
||||||
|
// 0.09 -> 0.1.
|
||||||
|
func (a *decimal) Round(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if shouldRoundUp(a, nd) {
|
||||||
|
a.RoundUp(nd)
|
||||||
|
} else {
|
||||||
|
a.RoundDown(nd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a down to nd digits (or fewer).
|
||||||
|
func (a *decimal) RoundDown(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.nd = nd
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a up to nd digits (or fewer).
|
||||||
|
func (a *decimal) RoundUp(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// round up
|
||||||
|
for i := nd - 1; i >= 0; i-- {
|
||||||
|
c := a.d[i]
|
||||||
|
if c < '9' { // can stop after this digit
|
||||||
|
a.d[i]++
|
||||||
|
a.nd = i + 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number is all 9s.
|
||||||
|
// Change to single 1 with adjusted decimal point.
|
||||||
|
a.d[0] = '1'
|
||||||
|
a.nd = 1
|
||||||
|
a.dp++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract integer part, rounded appropriately.
|
||||||
|
// No guarantees about overflow.
|
||||||
|
func (a *decimal) RoundedInteger() uint64 {
|
||||||
|
if a.dp > 20 {
|
||||||
|
return 0xFFFFFFFFFFFFFFFF
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
n := uint64(0)
|
||||||
|
for i = 0; i < a.dp && i < a.nd; i++ {
|
||||||
|
n = n*10 + uint64(a.d[i]-'0')
|
||||||
|
}
|
||||||
|
for ; i < a.dp; i++ {
|
||||||
|
n *= 10
|
||||||
|
}
|
||||||
|
if shouldRoundUp(a, a.dp) {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Multiprecision decimal numbers.
|
||||||
|
// For floating-point formatting only; not general purpose.
|
||||||
|
// Only operations are assign and (binary) left/right shift.
|
||||||
|
// Can do binary floating point in multiprecision decimal precisely
|
||||||
|
// because 2 divides 10; cannot do decimal floating point
|
||||||
|
// in multiprecision binary precisely.
|
||||||
|
|
||||||
|
package decimal
|
||||||
|
|
||||||
|
type floatInfo struct {
|
||||||
|
mantbits uint
|
||||||
|
expbits uint
|
||||||
|
bias int
|
||||||
|
}
|
||||||
|
|
||||||
|
var float32info = floatInfo{23, 8, -127}
|
||||||
|
var float64info = floatInfo{52, 11, -1023}
|
||||||
|
|
||||||
|
// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
|
||||||
|
// that will let the original floating point value be precisely reconstructed.
|
||||||
|
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
|
||||||
|
// If mantissa is zero, the number is zero; stop now.
|
||||||
|
if mant == 0 {
|
||||||
|
d.nd = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute upper and lower such that any decimal number
|
||||||
|
// between upper and lower (possibly inclusive)
|
||||||
|
// will round to the original floating point number.
|
||||||
|
|
||||||
|
// We may see at once that the number is already shortest.
|
||||||
|
//
|
||||||
|
// Suppose d is not denormal, so that 2^exp <= d < 10^dp.
|
||||||
|
// The closest shorter number is at least 10^(dp-nd) away.
|
||||||
|
// The lower/upper bounds computed below are at distance
|
||||||
|
// at most 2^(exp-mantbits).
|
||||||
|
//
|
||||||
|
// So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
|
||||||
|
// or equivalently log2(10)*(dp-nd) > exp-mantbits.
|
||||||
|
// It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
|
||||||
|
minexp := flt.bias + 1 // minimum possible exponent
|
||||||
|
if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
|
||||||
|
// The number is already shortest.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// d = mant << (exp - mantbits)
|
||||||
|
// Next highest floating point number is mant+1 << exp-mantbits.
|
||||||
|
// Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
|
||||||
|
upper := new(decimal)
|
||||||
|
upper.Assign(mant*2 + 1)
|
||||||
|
upper.Shift(exp - int(flt.mantbits) - 1)
|
||||||
|
|
||||||
|
// d = mant << (exp - mantbits)
|
||||||
|
// Next lowest floating point number is mant-1 << exp-mantbits,
|
||||||
|
// unless mant-1 drops the significant bit and exp is not the minimum exp,
|
||||||
|
// in which case the next lowest is mant*2-1 << exp-mantbits-1.
|
||||||
|
// Either way, call it mantlo << explo-mantbits.
|
||||||
|
// Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
|
||||||
|
var mantlo uint64
|
||||||
|
var explo int
|
||||||
|
if mant > 1<<flt.mantbits || exp == minexp {
|
||||||
|
mantlo = mant - 1
|
||||||
|
explo = exp
|
||||||
|
} else {
|
||||||
|
mantlo = mant*2 - 1
|
||||||
|
explo = exp - 1
|
||||||
|
}
|
||||||
|
lower := new(decimal)
|
||||||
|
lower.Assign(mantlo*2 + 1)
|
||||||
|
lower.Shift(explo - int(flt.mantbits) - 1)
|
||||||
|
|
||||||
|
// The upper and lower bounds are possible outputs only if
|
||||||
|
// the original mantissa is even, so that IEEE round-to-even
|
||||||
|
// would round to the original mantissa and not the neighbors.
|
||||||
|
inclusive := mant%2 == 0
|
||||||
|
|
||||||
|
// As we walk the digits we want to know whether rounding up would fall
|
||||||
|
// within the upper bound. This is tracked by upperdelta:
|
||||||
|
//
|
||||||
|
// If upperdelta == 0, the digits of d and upper are the same so far.
|
||||||
|
//
|
||||||
|
// If upperdelta == 1, we saw a difference of 1 between d and upper on a
|
||||||
|
// previous digit and subsequently only 9s for d and 0s for upper.
|
||||||
|
// (Thus rounding up may fall outside the bound, if it is exclusive.)
|
||||||
|
//
|
||||||
|
// If upperdelta == 2, then the difference is greater than 1
|
||||||
|
// and we know that rounding up falls within the bound.
|
||||||
|
var upperdelta uint8
|
||||||
|
|
||||||
|
// Now we can figure out the minimum number of digits required.
|
||||||
|
// Walk along until d has distinguished itself from upper and lower.
|
||||||
|
for ui := 0; ; ui++ {
|
||||||
|
// lower, d, and upper may have the decimal points at different
|
||||||
|
// places. In this case upper is the longest, so we iterate from
|
||||||
|
// ui==0 and start li and mi at (possibly) -1.
|
||||||
|
mi := ui - upper.dp + d.dp
|
||||||
|
if mi >= d.nd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
li := ui - upper.dp + lower.dp
|
||||||
|
l := byte('0') // lower digit
|
||||||
|
if li >= 0 && li < lower.nd {
|
||||||
|
l = lower.d[li]
|
||||||
|
}
|
||||||
|
m := byte('0') // middle digit
|
||||||
|
if mi >= 0 {
|
||||||
|
m = d.d[mi]
|
||||||
|
}
|
||||||
|
u := byte('0') // upper digit
|
||||||
|
if ui < upper.nd {
|
||||||
|
u = upper.d[ui]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay to round down (truncate) if lower has a different digit
|
||||||
|
// or if lower is inclusive and is exactly the result of rounding
|
||||||
|
// down (i.e., and we have reached the final digit of lower).
|
||||||
|
okdown := l != m || inclusive && li+1 == lower.nd
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case upperdelta == 0 && m+1 < u:
|
||||||
|
// Example:
|
||||||
|
// m = 12345xxx
|
||||||
|
// u = 12347xxx
|
||||||
|
upperdelta = 2
|
||||||
|
case upperdelta == 0 && m != u:
|
||||||
|
// Example:
|
||||||
|
// m = 12345xxx
|
||||||
|
// u = 12346xxx
|
||||||
|
upperdelta = 1
|
||||||
|
case upperdelta == 1 && (m != '9' || u != '0'):
|
||||||
|
// Example:
|
||||||
|
// m = 1234598x
|
||||||
|
// u = 1234600x
|
||||||
|
upperdelta = 2
|
||||||
|
}
|
||||||
|
// Okay to round up if upper has a different digit and either upper
|
||||||
|
// is inclusive or upper is bigger than the result of rounding up.
|
||||||
|
okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd)
|
||||||
|
|
||||||
|
// If it's okay to do either, then round to the nearest one.
|
||||||
|
// If it's okay to do only one, do it.
|
||||||
|
switch {
|
||||||
|
case okdown && okup:
|
||||||
|
d.Round(mi + 1)
|
||||||
|
return
|
||||||
|
case okdown:
|
||||||
|
d.RoundDown(mi + 1)
|
||||||
|
return
|
||||||
|
case okup:
|
||||||
|
d.RoundUp(mi + 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
|
||||||
|
*.bench
|
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Steve Francia
|
||||||
|
|
||||||
|
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.
|
@ -0,0 +1,40 @@
|
|||||||
|
GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2)
|
||||||
|
|
||||||
|
.PHONY: check fmt lint test test-race vet test-cover-html help
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
check: test-race fmt vet lint ## Run tests and linters
|
||||||
|
|
||||||
|
test: ## Run tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
test-race: ## Run tests with race detector
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
fmt: ## Run gofmt linter
|
||||||
|
ifeq "$(GOVERSION)" "12"
|
||||||
|
@for d in `go list` ; do \
|
||||||
|
if [ "`gofmt -l -s $$GOPATH/src/$$d | tee /dev/stderr`" ]; then \
|
||||||
|
echo "^ improperly formatted go files" && echo && exit 1; \
|
||||||
|
fi \
|
||||||
|
done
|
||||||
|
endif
|
||||||
|
|
||||||
|
lint: ## Run golint linter
|
||||||
|
@for d in `go list` ; do \
|
||||||
|
if [ "`golint $$d | tee /dev/stderr`" ]; then \
|
||||||
|
echo "^ golint errors!" && echo && exit 1; \
|
||||||
|
fi \
|
||||||
|
done
|
||||||
|
|
||||||
|
vet: ## Run go vet linter
|
||||||
|
@if [ "`go vet | tee /dev/stderr`" ]; then \
|
||||||
|
echo "^ go vet errors!" && echo && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
test-cover-html: ## Generate test coverage report
|
||||||
|
go test -coverprofile=coverage.out -covermode=count
|
||||||
|
go tool cover -func=coverage.out
|
||||||
|
|
||||||
|
help:
|
||||||
|
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue