diff --git a/README.md b/README.md
index 0d68056..2b9d36c 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,176 @@
+
+
# comver
+
+
+
+
+
+[![Go](https://github.com/typisttech/comver/actions/workflows/go.yml/badge.svg)](https://github.com/typisttech/comver/actions/workflows/go.yml)
+[![Go Report Card](https://goreportcard.com/badge/github.com/typisttech/comver)](https://goreportcard.com/report/github.com/typisttech/comver)
+[![GitHub Release](https://img.shields.io/github/v/release/typisttech/comver?style=flat-square&)](https://github.com/typisttech/comver/releases/latest)
+[![Go Reference](https://pkg.go.dev/badge/github.com/typisttech/comver.svg)](https://pkg.go.dev/github.com/typisttech/comver)
+[![license](https://img.shields.io/github/license/typisttech/comver.svg?style=flat-square)](https://github.com/typisttech/comver/blob/master/LICENSE)
+[![X Follow @TangRufus](https://img.shields.io/badge/Follow-%40TangRufus-black?style=flat-square&logo=x&logoColor=white)](https://x.com/tangrufus)
+[![Hire Typist Tech](https://img.shields.io/badge/Hire-Typist%20Tech-ff69b4.svg?style=flat-square)](https://typist.tech/contact/)
+
+
+
+
+ Package comver
provides the ability to work with composer supported versions in Go.
+
+
+ Built with ♥ by Typist Tech
+
+
+---
+
+## Usage
+
+> [!NOTE]
+> See full API documentation at [pkg.go.dev](https://pkg.go.dev/github.com/typisttech/comver).
+
+### `Version`
+
+[`NewVersion`](https://pkg.go.dev/github.com/typisttech/comver#NewVersion) parses a given version string, attempts to coerce a version string into a [`Version`](https://pkg.go.dev/github.com/typisttech/comver#Version) object or return an error if unable to parse the version string.
+
+If there is a leading **v** or a version listed without all parts (e.g. **v1.2.p5+foo**) it will attempt to coerce it into a valid composer version (e.g. **1.2.0.0-patch5**). In both cases a [`Version`](https://pkg.go.dev/github.com/typisttech/comver#Version) object is returned that can be sorted, compared, and used in constraints.
+
+
+> [!WARNING]
+> Due to implementation complexity, it only supports a subset of [composer versioning](https://github.com/composer/semver/).
+>
+> Refer to the [`version_test.go`](version_test.go) for examples.
+
+
+```go
+ss := []string{
+ "1.2.3",
+ "v1.2.p5+foo",
+ "v1.2.3.4.p5+foo",
+ "2010-01-02",
+ "2010-01-02.5",
+ "not a version",
+ "1.0.0-meh",
+ "20100102.0.3.4",
+ "1.0.0-alpha.beta",
+}
+
+for _, s := range ss {
+ v, err := comver.NewVersion(s)
+ if err != nil {
+ fmt.Println(s, " => ", err)
+ continue
+ }
+ fmt.Println(s, " => ", v)
+}
+
+// Output:
+// 1.2.3 => 1.2.3.0
+// v1.2.p5+foo => 1.2.0.0-patch5
+// v1.2.3.4.p5+foo => 1.2.3.4-patch5
+// 2010-01-02 => 2010.1.2.0
+// 2010-01-02.5 => 2010.1.2.5
+// not a version => error parsing version string "not a version"
+// 1.0.0-meh => error parsing version string "1.0.0-meh"
+// 20100102.0.3.4 => error parsing version string "20100102.0.3.4"
+// 1.0.0-alpha.beta => error parsing version string "1.0.0-alpha.beta"
+```
+
+### `constraint`
+
+```go
+v1, _ := comver.NewVersion("1")
+v2, _ := comver.NewVersion("2")
+v3, _ := comver.NewVersion("3")
+v4, _ := comver.NewVersion("4")
+
+cs := []any{
+ comver.NewGreaterThanConstraint(v1),
+ comver.NewGreaterThanOrEqualToConstraint(v2),
+ comver.NewLessThanOrEqualToConstraint(v3),
+ comver.NewLessThanConstraint(v4),
+}
+
+for _, c := range cs {
+ fmt.Println(c)
+}
+
+// Output:
+// >1
+// >=2
+// <=3
+// <4
+```
+
+### `interval`
+
+`interval` represents the intersection (logical AND) of two constraints.
+
+```go
+v1, _ := comver.NewVersion("1")
+v2, _ := comver.NewVersion("2")
+v3, _ := comver.NewVersion("3")
+
+g1l3, _ := comver.NewInterval(
+ comver.NewGreaterThanConstraint(v1),
+ comver.NewLessThanConstraint(v3),
+)
+
+if g1l3.Check(v2) {
+ fmt.Println(v2.Short(), "satisfies", g1l3)
+}
+
+if !g1l3.Check(v3) {
+ fmt.Println(v2.Short(), "doesn't satisfy", g1l3)
+}
+
+// Output:
+// 2 satisfies >1 <3
+// 2 doesn't satisfy >1 <3
+```
+
+### `Intervals`
+
+[`Intervals`](https://pkg.go.dev/github.com/typisttech/comver#Intervals) represent the union (logical OR) of multiple intervals.
+
+```go
+v1, _ := comver.NewVersion("1")
+v2, _ := comver.NewVersion("2")
+v3, _ := comver.NewVersion("3")
+v4, _ := comver.NewVersion("4")
+
+g1l3, _ := comver.NewInterval(
+comver.NewGreaterThanConstraint(v1),
+comver.NewLessThanConstraint(v3),
+)
+
+ge2le4, _ := comver.NewInterval(
+comver.NewGreaterThanOrEqualToConstraint(v2),
+comver.NewLessThanOrEqualToConstraint(v4),
+)
+
+is := comver.Intervals{g1l3, ge2le4}
+fmt.Println(is)
+
+is = comver.Compact(is)
+fmt.Println(is)
+
+// Output:
+// >1 <3 || >=2 <=4
+// >1 <=4
+```
+
+## Credits
+
+[`comver`](https://github.com/typisttech/comver) is a [Typist Tech](https://typist.tech) project and maintained by [Tang Rufus](https://x.com/TangRufus), freelance developer for [hire](https://typist.tech/contact/).
+
+Full list of contributors can be found [here](https://github.com/typisttech/comver/graphs/contributors).
+
+## Copyright and License
+
+This project is a [free software](https://www.gnu.org/philosophy/free-sw.en.html) distributed under the terms of the MIT license. For the full license, see [LICENSE](./LICENSE).
+
+## Contribute
+
+Feedbacks / bug reports / pull requests are welcome.
diff --git a/constraint.go b/constraint.go
index 6d05730..9f3eacb 100644
--- a/constraint.go
+++ b/constraint.go
@@ -80,7 +80,7 @@ func (c *constraint) upperbounded() bool {
return c.op == lessThanOrEqualTo || c.op == lessThan
}
-// Check tests if a [Version] satisfies the constraints.
+// Check reports whether a [Version] satisfies the constraint.
func (c *constraint) Check(v Version) bool {
if c == nil {
// this should never happen
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..450d5b5
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,6 @@
+/*
+Package comver provides the ability to work with [composer supported versions] in Go.
+
+[composer supported versions]: https://github.com/composer/semver/
+*/
+package comver
diff --git a/doc_examle_test.go b/doc_examle_test.go
new file mode 100644
index 0000000..e265409
--- /dev/null
+++ b/doc_examle_test.go
@@ -0,0 +1,115 @@
+package comver_test
+
+import (
+ "fmt"
+
+ "github.com/typisttech/comver"
+)
+
+func Example_version() {
+ ss := []string{
+ "1.2.3",
+ "v1.2.p5+foo",
+ "v1.2.3.4.p5+foo",
+ "2010-01-02",
+ "2010-01-02.5",
+ "not a version",
+ "1.0.0-meh",
+ "20100102.0.3.4",
+ "1.0.0-alpha.beta",
+ }
+
+ for _, s := range ss {
+ v, err := comver.NewVersion(s)
+ if err != nil {
+ fmt.Println(s, " => ", err)
+ continue
+ }
+ fmt.Println(s, " => ", v)
+ }
+
+ // Output:
+ // 1.2.3 => 1.2.3.0
+ // v1.2.p5+foo => 1.2.0.0-patch5
+ // v1.2.3.4.p5+foo => 1.2.3.4-patch5
+ // 2010-01-02 => 2010.1.2.0
+ // 2010-01-02.5 => 2010.1.2.5
+ // not a version => error parsing version string "not a version"
+ // 1.0.0-meh => error parsing version string "1.0.0-meh"
+ // 20100102.0.3.4 => error parsing version string "20100102.0.3.4"
+ // 1.0.0-alpha.beta => error parsing version string "1.0.0-alpha.beta"
+}
+
+func Example_constraint() {
+ v1, _ := comver.NewVersion("1")
+ v2, _ := comver.NewVersion("2")
+ v3, _ := comver.NewVersion("3")
+ v4, _ := comver.NewVersion("4")
+
+ cs := []any{
+ comver.NewGreaterThanConstraint(v1),
+ comver.NewGreaterThanOrEqualToConstraint(v2),
+ comver.NewLessThanOrEqualToConstraint(v3),
+ comver.NewLessThanConstraint(v4),
+ }
+
+ for _, c := range cs {
+ fmt.Println(c)
+ }
+
+ // Output:
+ // >1
+ // >=2
+ // <=3
+ // <4
+}
+
+func Example_interval() {
+ v1, _ := comver.NewVersion("1")
+ v2, _ := comver.NewVersion("2")
+ v3, _ := comver.NewVersion("3")
+
+ g1l3, _ := comver.NewInterval(
+ comver.NewGreaterThanConstraint(v1),
+ comver.NewLessThanConstraint(v3),
+ )
+
+ if g1l3.Check(v2) {
+ fmt.Println(v2.Short(), "satisfies", g1l3)
+ }
+
+ if !g1l3.Check(v3) {
+ fmt.Println(v2.Short(), "doesn't satisfy", g1l3)
+ }
+
+ // Output:
+ // 2 satisfies >1 <3
+ // 2 doesn't satisfy >1 <3
+}
+
+func Example_intervals() {
+ v1, _ := comver.NewVersion("1")
+ v2, _ := comver.NewVersion("2")
+ v3, _ := comver.NewVersion("3")
+ v4, _ := comver.NewVersion("4")
+
+ g1l3, _ := comver.NewInterval(
+ comver.NewGreaterThanConstraint(v1),
+ comver.NewLessThanConstraint(v3),
+ )
+
+ ge2le4, _ := comver.NewInterval(
+ comver.NewGreaterThanOrEqualToConstraint(v2),
+ comver.NewLessThanOrEqualToConstraint(v4),
+ )
+
+ is := comver.Intervals{g1l3, ge2le4}
+ fmt.Println(is)
+
+ is = comver.Compact(is)
+ fmt.Println(is)
+
+ // Output:
+ // >1 <3 || >=2 <=4
+ // >1 <=4
+}
diff --git a/interval.go b/interval.go
index 11474be..36e77d2 100644
--- a/interval.go
+++ b/interval.go
@@ -1,6 +1,6 @@
package comver
-// interval represents the intersection of two constraints.
+// interval represents the intersection (logical AND) of two constraints.
type interval [2]*constraint
const (
@@ -44,7 +44,7 @@ func NewInterval(c1, c2 *constraint) (interval, error) { //nolint:cyclop
}
}
-// Check tests if a [Version] lies within the interval.
+// Check reports whether a [Version] satisfies the interval.
func (i interval) Check(v Version) bool {
for _, c := range i {
if c != nil && !c.Check(v) {
diff --git a/version.go b/version.go
index f47db5b..31d104b 100644
--- a/version.go
+++ b/version.go
@@ -30,8 +30,18 @@ type Version struct {
original string `exhaustruct:"optional"`
}
-// NewVersion parses a given version string and returns an instance of [Version] or
-// an error if unable to parse the version.
+// NewVersion parses a given version string, attempts to coerce a version string into
+// a [Version] object or return an error if unable to parse the version string.
+//
+// If there is a leading v or a version listed without all parts (e.g. v1.2.p5+foo) it will
+// attempt to coerce it into a valid composer version (e.g. 1.2.0.0-patch5). In both cases
+// a [Version] object is returned that can be sorted, compared, and used in constraints.
+//
+// Due to implementation complexity, it only supports a subset of [composer versioning].
+// Refer to the [version_test.go] for examples.
+//
+// [composer versioning]: https://github.com/composer/semver/
+// [version_test.go]: https://github.com/typisttech/comver/blob/main/version_test.go
func NewVersion(v string) (Version, error) { //nolint:cyclop,funlen
original := v
@@ -152,6 +162,7 @@ func (v Version) String() string {
return s
}
+// Short returns the shortest string representation of the version.
func (v Version) Short() string {
s := fmt.Sprintf("%d.%d.%d.%d", v.major, v.minor, v.patch, v.tweak)