Skip to content

Commit

Permalink
Adding text marshaling and unmarshaling
Browse files Browse the repository at this point in the history
Note, this removes the JSON specific handling as the general handling
covers the JSON case.

If this is used with JSON and > or < are used than a custom encoder
is required. In Go json.Marshal will escape those. For example...

    cs, _ := NewConstraint(tc.constraint)
    buf := new(bytes.Buffer)
    enc := json.NewEncoder(buf)
    enc.SetEscapeHTML(false)
    err = enc.Encode(cs)
    jsonVal = buf.String()

Signed-off-by: Matt Farina <matt@mattfarina.com>
  • Loading branch information
mattfarina committed Nov 28, 2022
1 parent 00300c4 commit 3c1d0f2
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 35 deletions.
27 changes: 8 additions & 19 deletions constraints.go
Expand Up @@ -2,7 +2,6 @@ package semver

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"regexp"
Expand Down Expand Up @@ -135,31 +134,21 @@ func (cs Constraints) String() string {
return strings.Join(buf, " || ")
}

// UnmarshalJSON implements JSON.Unmarshaler interface.
func (cs *Constraints) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
temp, err := NewConstraint(s)
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (cs *Constraints) UnmarshalText(text []byte) error {
temp, err := NewConstraint(string(text))
if err != nil {
return err
}

*cs = *temp

return nil
}

// MarshalJSON implements JSON.Marshaler interface.
func (cs Constraints) MarshalJSON() ([]byte, error) {
// we need our own encoder so we don't escape '<' and '>' which json.Marshal does
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)

if err := enc.Encode(cs.String()); err != nil {
return nil, err
}
return bytes.TrimRight(buf.Bytes(), "\n"), nil
// MarshalText implements the encoding.TextMarshaler interface.
func (cs Constraints) MarshalText() ([]byte, error) {
return []byte(cs.String()), nil
}

var constraintOps map[string]cfunc
Expand Down
68 changes: 52 additions & 16 deletions constraints_test.go
Expand Up @@ -680,55 +680,91 @@ func TestConstraintString(t *testing.T) {
}
}

func TestJsonMarshalConstraints(t *testing.T) {
func TestTextMarshalConstraints(t *testing.T) {
tests := []struct {
sCs string
want string
constraint string
want string
}{
{"1.1.1", "1.1.1"},
{">=1.1.1", ">=1.1.1"},
{"<=1.1.1", "<=1.1.1"},
{"1.2.3", "1.2.3"},
{">=1.2.3", ">=1.2.3"},
{"<=1.2.3", "<=1.2.3"},
{"1 <=1.2.3", "1 <=1.2.3"},
{"1, <=1.2.3", "1 <=1.2.3"},
{">1, <=1.2.3", ">1 <=1.2.3"},
{"> 1 , <=1.2.3", ">1 <=1.2.3"},
}

for _, tc := range tests {
cs, err := NewConstraint(tc.sCs)
cs, err := NewConstraint(tc.constraint)
if err != nil {
t.Errorf("Error creating constraints: %s", err)
}

out, err2 := cs.MarshalText()
if err2 != nil {
t.Errorf("Error constraint version: %s", err2)
}

got := string(out)
if got != tc.want {
t.Errorf("Error marshaling constraint, unexpected marshaled content: got=%q want=%q", got, tc.want)
}

// Test that this works for JSON as well as text. When JSON marshaling
// functions are missing it falls through to TextMarshal.
// NOTE: To not escape the < and > (which json.Marshal does) you need
// a custom encoder where html escaping is disabled. This must be done
// in the top level encoder being used to marshal the constraints.
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
err = enc.Encode(cs)
if err != nil {
t.Errorf("Error unmarshaling version: %s", err)
t.Errorf("Error unmarshaling constraint: %s", err)
}
got := buf.String()
got = buf.String()
// The encoder used here adds a newline so we add that to what we want
// so they align. The newline is an artifact of the testing.
want := fmt.Sprintf("%q\n", tc.want)
if got != want {
t.Errorf("Error marshaling unexpected marshaled content: got=%q want=%q", got, want)
t.Errorf("Error marshaling constraint, unexpected marshaled content: got=%q want=%q", got, want)
}
}
}

func TestJsonUnmarshalConstraints(t *testing.T) {
func TestTextUnmarshalConstraints(t *testing.T) {
tests := []struct {
sCs string
want string
constraint string
want string
}{
{"1.1.1", "1.1.1"},
{"1.2.3", "1.2.3"},
{">=1.2.3", ">=1.2.3"},
{"<=1.2.3", "<=1.2.3"},
{">1 <=1.2.3", ">1 <=1.2.3"},
{"> 1 <=1.2.3", ">1 <=1.2.3"},
{">1, <=1.2.3", ">1 <=1.2.3"},
}

for _, tc := range tests {
cs := Constraints{}
err := json.Unmarshal([]byte(fmt.Sprintf("%q", tc.sCs)), &cs)
err := cs.UnmarshalText([]byte(tc.constraint))
if err != nil {
t.Errorf("Error unmarshaling constraints: %s", err)
}
got := cs.String()
if got != tc.want {
t.Errorf("Error unmarshaling unexpected object content: got=%q want=%q", got, tc.want)
t.Errorf("Error unmarshaling constraint, unexpected object content: got=%q want=%q", got, tc.want)
}

// Test that this works for JSON as well as text. When JSON unmarshaling
// functions are missing it falls through to TextUnmarshal.
err = json.Unmarshal([]byte(fmt.Sprintf("%q", tc.constraint)), &cs)
if err != nil {
t.Errorf("Error unmarshaling constraints: %s", err)
}
got = cs.String()
if got != tc.want {
t.Errorf("Error unmarshaling constraint, unexpected object content: got=%q want=%q", got, tc.want)
}
}
}

0 comments on commit 3c1d0f2

Please sign in to comment.