Skip to content

Commit

Permalink
init semver package
Browse files Browse the repository at this point in the history
fmt table package
  • Loading branch information
SimonWaldherr committed Jul 16, 2024
1 parent b3f7fcd commit 1387f72
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 180 deletions.
128 changes: 128 additions & 0 deletions semver/semver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package semver

import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)

var (
semverRegex = regexp.MustCompile(`^v?(\d+)\.(\d+)\.(\d+)(?:-([\w\.\-]+))?(?:\+([\w\.\-]+))?$`)
)

type Version struct {
Major int
Minor int
Patch int
PreRelease string
BuildMetadata string
}

func Parse(version string) (*Version, error) {
matches := semverRegex.FindStringSubmatch(version)
if matches == nil {
return nil, errors.New("invalid semantic version")
}

major, _ := strconv.Atoi(matches[1])
minor, _ := strconv.Atoi(matches[2])
patch, _ := strconv.Atoi(matches[3])

return &Version{
Major: major,
Minor: minor,
Patch: patch,
PreRelease: matches[4],
BuildMetadata: matches[5],
}, nil
}

func (v *Version) String() string {
ver := fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
if v.PreRelease != "" {
ver += "-" + v.PreRelease
}
if v.BuildMetadata != "" {
ver += "+" + v.BuildMetadata
}
return ver
}

func (v *Version) Compare(other *Version) int {
if v.Major != other.Major {
return compareInt(v.Major, other.Major)
}
if v.Minor != other.Minor {
return compareInt(v.Minor, other.Minor)
}
if v.Patch != other.Patch {
return compareInt(v.Patch, other.Patch)
}
return comparePreRelease(v.PreRelease, other.PreRelease)
}

func compareInt(a, b int) int {
if a < b {
return -1
}
if a > b {
return 1
}
return 0
}

func comparePreRelease(a, b string) int {
if a == b {
return 0
}
if a == "" {
return 1
}
if b == "" {
return -1
}
return strings.Compare(a, b)
}

type Constraint struct {
Operator string
Version *Version
}

func ParseConstraint(constraint string) (*Constraint, error) {
parts := strings.Fields(constraint)
if len(parts) != 2 {
return nil, errors.New("invalid constraint format")
}

version, err := Parse(parts[1])
if err != nil {
return nil, err
}

return &Constraint{
Operator: parts[0],
Version: version,
}, nil
}

func (c *Constraint) Matches(v *Version) bool {
switch c.Operator {
case "=":
return v.Compare(c.Version) == 0
case "!=":
return v.Compare(c.Version) != 0
case "<":
return v.Compare(c.Version) < 0
case "<=":
return v.Compare(c.Version) <= 0
case ">":
return v.Compare(c.Version) > 0
case ">=":
return v.Compare(c.Version) >= 0
default:
return false
}
}
41 changes: 41 additions & 0 deletions semver/semver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package semver

import (
"testing"
)

func TestParse(t *testing.T) {
version, err := Parse("1.2.3-alpha+001")
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if version.Major != 1 || version.Minor != 2 || version.Patch != 3 || version.PreRelease != "alpha" || version.BuildMetadata != "001" {
t.Fatalf("parsed version incorrect: %v", version)
}
}

func TestString(t *testing.T) {
version, _ := Parse("1.2.3-alpha+001")
if version.String() != "v1.2.3-alpha+001" {
t.Fatalf("expected version string 'v1.2.3-alpha+001', got %s", version.String())
}
}

func TestCompare(t *testing.T) {
v1, _ := Parse("1.2.3")
v2, _ := Parse("2.0.0")
if v1.Compare(v2) >= 0 {
t.Fatalf("expected v1 < v2")
}
}

func TestConstraint(t *testing.T) {
constraint, err := ParseConstraint(">= 1.2.3")
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
version, _ := Parse("1.2.3")
if !constraint.Matches(version) {
t.Fatalf("expected version to match constraint")
}
}
90 changes: 45 additions & 45 deletions table/example_test.go
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
package table

import (
"fmt"
"fmt"
)

// Beispiel-Datenstrukturen
type Person struct {
Name string
Age int
Email string
Name string
Age int
Email string
}

type Product struct {
ID int
Name string
Price float64
ID int
Name string
Price float64
}

// Beispiel: ASCII-Tabelle ohne Rotation
func ExampleRenderASCII_noRotation() {
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}

asciiTable, _ := RenderASCII(people, TableOption{Rotate: false})
fmt.Println(asciiTable)
// Output:
// +------+-----+------------------+
// | Name | Age | Email |
// +------+-----+------------------+
// | John | 42 | john@example.com |
// | Jane | 32 | jane@example.com |
// +------+-----+------------------+
asciiTable, _ := RenderASCII(people, TableOption{Rotate: false})
fmt.Println(asciiTable)
// Output:
// +------+-----+------------------+
// | Name | Age | Email |
// +------+-----+------------------+
// | John | 42 | john@example.com |
// | Jane | 32 | jane@example.com |
// +------+-----+------------------+
}

// Beispiel: ASCII-Tabelle mit Rotation
func ExampleRenderASCII_withRotation() {
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}

rotatedASCIITable, _ := RenderASCII(people, TableOption{Rotate: true})
fmt.Println(rotatedASCIITable)
// Output:
// +-------+------------------+------------------+
// | | Row 1 | Row 2 |
// +-------+------------------+------------------+
// | Name | John | Jane |
// | Age | 42 | 32 |
// | Email | john@example.com | jane@example.com |
// +-------+------------------+------------------+
rotatedASCIITable, _ := RenderASCII(people, TableOption{Rotate: true})
fmt.Println(rotatedASCIITable)
// Output:
// +-------+------------------+------------------+
// | | Row 1 | Row 2 |
// +-------+------------------+------------------+
// | Name | John | Jane |
// | Age | 42 | 32 |
// | Email | john@example.com | jane@example.com |
// +-------+------------------+------------------+
}

// Beispiel: Markdown-Tabelle ohne Rotation
func ExampleRenderMarkdown_noRotation() {
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}
people := []Person{
{Name: "John", Age: 42, Email: "john@example.com"},
{Name: "Jane", Age: 32, Email: "jane@example.com"},
}

markdownTable, _ := RenderMarkdown(people, TableOption{Rotate: false})
fmt.Println(markdownTable)
// Output:
// | Name | Age | Email |
// |------|-----|------------------|
// | John | 42 | john@example.com |
// | Jane | 32 | jane@example.com |
markdownTable, _ := RenderMarkdown(people, TableOption{Rotate: false})
fmt.Println(markdownTable)
// Output:
// | Name | Age | Email |
// |------|-----|------------------|
// | John | 42 | john@example.com |
// | Jane | 32 | jane@example.com |
}
Loading

0 comments on commit 1387f72

Please sign in to comment.