Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 9b21ed5

Browse files
author
Edward Muller
authored
Merge pull request #16 from sdboyer/ml-marshalers
JSON marshalers for manifest and lock
2 parents b17dca4 + d1c74a8 commit 9b21ed5

File tree

4 files changed

+211
-38
lines changed

4 files changed

+211
-38
lines changed

lock.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,43 @@ func (l *lock) InputHash() []byte {
8282
func (l *lock) Projects() []gps.LockedProject {
8383
return l.P
8484
}
85+
86+
func (l *lock) MarshalJSON() ([]byte, error) {
87+
raw := rawLock{
88+
Memo: hex.EncodeToString(l.Memo),
89+
P: make([]lockedDep, len(l.P)),
90+
}
91+
92+
for k, lp := range l.P {
93+
id := lp.Ident()
94+
ld := lockedDep{
95+
Name: string(id.ProjectRoot),
96+
Repository: id.NetworkName,
97+
Packages: lp.Packages(),
98+
}
99+
100+
v := lp.Version()
101+
// Figure out how to get the underlying revision
102+
switch tv := v.(type) {
103+
case gps.UnpairedVersion:
104+
// TODO we could error here, if we want to be very defensive about not
105+
// allowing a lock to be written if without an immmutable revision
106+
case gps.Revision:
107+
ld.Revision = tv.String()
108+
case gps.PairedVersion:
109+
ld.Revision = tv.Underlying().String()
110+
}
111+
112+
switch v.Type() {
113+
case "branch":
114+
ld.Branch = v.String()
115+
case "semver", "version":
116+
ld.Version = v.String()
117+
}
118+
119+
raw.P[k] = ld
120+
}
121+
122+
// TODO sort output - #15
123+
return json.Marshal(raw)
124+
}

lock_test.go

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,46 @@
55
package main
66

77
import (
8+
"bytes"
89
"encoding/hex"
10+
"encoding/json"
911
"reflect"
1012
"strings"
1113
"testing"
1214

1315
"github.com/sdboyer/gps"
1416
)
1517

16-
func TestReadLock(t *testing.T) {
17-
const le = `{
18-
"memo": "2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e",
19-
"projects": [
20-
{
21-
"name": "github.com/sdboyer/gps",
22-
"branch": "master",
18+
const le = `{
19+
"memo": "2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e",
20+
"projects": [
21+
{
22+
"name": "github.com/sdboyer/gps",
23+
"branch": "master",
2324
"version": "v0.12.0",
24-
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
25-
"packages": ["."]
26-
}
27-
]
25+
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
26+
"packages": [
27+
"."
28+
]
29+
}
30+
]
2831
}`
29-
const lg = `{
30-
"memo": "2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e",
31-
"projects": [
32-
{
33-
"name": "github.com/sdboyer/gps",
34-
"branch": "master",
35-
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
36-
"packages": ["."]
37-
}
38-
]
32+
33+
const lg = `{
34+
"memo": "2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e",
35+
"projects": [
36+
{
37+
"name": "github.com/sdboyer/gps",
38+
"branch": "master",
39+
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
40+
"packages": [
41+
"."
42+
]
43+
}
44+
]
3945
}`
4046

47+
func TestReadLock(t *testing.T) {
4148
_, err := readLock(strings.NewReader(le))
4249
if err == nil {
4350
t.Error("Reading lock with invalid props should have caused error, but did not")
@@ -66,3 +73,30 @@ func TestReadLock(t *testing.T) {
6673
t.Error("Valid lock did not parse as expected")
6774
}
6875
}
76+
77+
func TestWriteLock(t *testing.T) {
78+
memo, _ := hex.DecodeString("2252a285ab27944a4d7adcba8dbd03980f59ba652f12db39fa93b927c345593e")
79+
l := &lock{
80+
Memo: memo,
81+
P: []gps.LockedProject{
82+
gps.NewLockedProject(
83+
gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/gps")},
84+
gps.NewBranch("master").Is(gps.Revision("d05d5aca9f895d19e9265839bffeadd74a2d2ecb")),
85+
[]string{"."},
86+
),
87+
},
88+
}
89+
90+
b, err := json.Marshal(l)
91+
if err != nil {
92+
t.Fatalf("Error while marshaling valid lock to JSON: %q", err)
93+
}
94+
95+
var out bytes.Buffer
96+
json.Indent(&out, b, "", "\t")
97+
98+
s := out.String()
99+
if s != lg {
100+
t.Errorf("Valid lock did not marshal to JSON as expected:\n\t(GOT): %s\n\t(WNT): %s", s, lg)
101+
}
102+
}

manifest.go

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package main
66

77
import (
8+
"bytes"
89
"encoding/json"
910
"fmt"
1011
"io"
@@ -19,16 +20,16 @@ type manifest struct {
1920
}
2021

2122
type rawManifest struct {
22-
Dependencies map[string]possibleProps `json:"dependencies"`
23-
Overrides map[string]possibleProps `json:"overrides"`
24-
Ignores []string `json:"ignores"`
23+
Dependencies map[string]possibleProps `json:"dependencies,omitempty"`
24+
Overrides map[string]possibleProps `json:"overrides,omitempty"`
25+
Ignores []string `json:"ignores,omitempty"`
2526
}
2627

2728
type possibleProps struct {
2829
Branch string `json:"branch,omitempty"`
2930
Revision string `json:"revision,omitempty"`
3031
Version string `json:"version,omitempty"`
31-
NetworkName string `json:"network_name,omitempty"`
32+
NetworkName string `json:"source,omitempty"`
3233
}
3334

3435
func newRawManifest() rawManifest {
@@ -69,6 +70,10 @@ func readManifest(r io.Reader) (*manifest, error) {
6970
return m, nil
7071
}
7172

73+
// toProps interprets the string representations of project information held in
74+
// a possibleProps, converting them into a proper gps.ProjectProperties. An
75+
// error is returned if the possibleProps contains some invalid combination -
76+
// for example, if both a branch and version constraint are specified.
7277
func toProps(n string, p possibleProps) (pp gps.ProjectProperties, err error) {
7378
if p.Branch != "" {
7479
if p.Version != "" || p.Revision != "" {
@@ -98,6 +103,60 @@ func toProps(n string, p possibleProps) (pp gps.ProjectProperties, err error) {
98103
return pp, nil
99104
}
100105

106+
func (m *manifest) MarshalJSON() ([]byte, error) {
107+
raw := rawManifest{
108+
Dependencies: make(map[string]possibleProps, len(m.Dependencies)),
109+
Overrides: make(map[string]possibleProps, len(m.Ovr)),
110+
Ignores: m.Ignores,
111+
}
112+
113+
for n, pp := range m.Dependencies {
114+
raw.Dependencies[string(n)] = toPossible(pp)
115+
}
116+
117+
for n, pp := range m.Ovr {
118+
raw.Overrides[string(n)] = toPossible(pp)
119+
}
120+
121+
b, err := json.Marshal(raw)
122+
if err != nil {
123+
return nil, err
124+
}
125+
126+
// Semver range ops, > and <, get turned into unicode code points. This is a
127+
// nice example of why using JSON for files like this is not the best
128+
b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
129+
b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
130+
return b, nil
131+
}
132+
133+
func toPossible(pp gps.ProjectProperties) (p possibleProps) {
134+
p.NetworkName = pp.NetworkName
135+
136+
if v, ok := pp.Constraint.(gps.Version); ok {
137+
switch v.Type() {
138+
case "rev": // will be changed to revision upstream soon
139+
p.Revision = v.String()
140+
case "branch":
141+
p.Branch = v.String()
142+
case "semver", "version":
143+
p.Version = v.String()
144+
}
145+
} else {
146+
// We simply don't allow for a case where the user could directly
147+
// express a 'none' constraint, so we can ignore it here. We also ignore
148+
// the 'any' case, because that's the other possibility, and it's what
149+
// we interpret not having any constraint expressions at all to mean.
150+
//if !gps.IsAny(pp.Constraint) && !gps.IsNone(pp.Constraint) {
151+
if !gps.IsAny(pp.Constraint) {
152+
// Has to be a semver range.
153+
p.Version = pp.Constraint.String()
154+
}
155+
}
156+
157+
return
158+
}
159+
101160
func (m *manifest) DependencyConstraints() gps.ProjectConstraints {
102161
return m.Dependencies
103162
}

manifest_test.go

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,72 @@
55
package main
66

77
import (
8+
"bytes"
9+
"encoding/json"
810
"reflect"
911
"strings"
1012
"testing"
1113

1214
"github.com/sdboyer/gps"
1315
)
1416

15-
func TestReadManifest(t *testing.T) {
16-
const je = `{
17+
const je = `{
1718
"dependencies": {
1819
"github.com/sdboyer/gps": {
1920
"branch": "master",
2021
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
2122
"version": "^v0.12.0",
22-
"network_name": "https://github.com/sdboyer/gps"
23+
"source": "https://github.com/sdboyer/gps"
2324
}
2425
},
2526
"overrides": {
2627
"github.com/sdboyer/gps": {
2728
"branch": "master",
2829
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb",
2930
"version": "^v0.12.0",
30-
"network_name": "https://github.com/sdboyer/gps"
31+
"source": "https://github.com/sdboyer/gps"
3132
}
3233
},
3334
"ignores": [
3435
"github.com/foo/bar"
3536
]
3637
}`
3738

38-
const jg = `{
39+
const jg = `{
3940
"dependencies": {
40-
"github.com/sdboyer/gps": {
41-
"version": "^v0.12.0"
42-
},
4341
"github.com/babble/brook": {
4442
"revision": "d05d5aca9f895d19e9265839bffeadd74a2d2ecb"
43+
},
44+
"github.com/sdboyer/gps": {
45+
"version": ">=0.12.0, <1.0.0"
4546
}
4647
},
4748
"overrides": {
4849
"github.com/sdboyer/gps": {
4950
"branch": "master",
50-
"network_name": "https://github.com/sdboyer/gps"
51+
"source": "https://github.com/sdboyer/gps"
5152
}
5253
},
5354
"ignores": [
5455
"github.com/foo/bar"
5556
]
5657
}`
5758

58-
_, err := ReadManifest(strings.NewReader(je))
59+
func TestReadManifest(t *testing.T) {
60+
_, err := readManifest(strings.NewReader(je))
5961
if err == nil {
6062
t.Error("Reading manifest with invalid props should have caused error, but did not")
6163
} else if !strings.Contains(err.Error(), "multiple constraints") {
6264
t.Errorf("Unexpected error %q; expected multiple constraint error", err)
6365
}
6466

65-
m2, err := ReadManifest(strings.NewReader(jg))
67+
m2, err := readManifest(strings.NewReader(jg))
6668
if err != nil {
6769
t.Fatalf("Should have read Manifest correctly, but got err %q", err)
6870
}
6971

70-
c, _ := gps.NewSemverConstraint("^v0.12.0")
71-
em := Manifest{
72+
c, _ := gps.NewSemverConstraint(">=0.12.0, <1.0.0")
73+
em := manifest{
7274
Dependencies: map[gps.ProjectRoot]gps.ProjectProperties{
7375
gps.ProjectRoot("github.com/sdboyer/gps"): {
7476
Constraint: c,
@@ -96,3 +98,41 @@ func TestReadManifest(t *testing.T) {
9698
t.Error("Valid manifest's ignores did not parse as expected")
9799
}
98100
}
101+
102+
func TestWriteManifest(t *testing.T) {
103+
c, _ := gps.NewSemverConstraint("^v0.12.0")
104+
m := &manifest{
105+
Dependencies: map[gps.ProjectRoot]gps.ProjectProperties{
106+
gps.ProjectRoot("github.com/sdboyer/gps"): {
107+
Constraint: c,
108+
},
109+
gps.ProjectRoot("github.com/babble/brook"): {
110+
Constraint: gps.Revision("d05d5aca9f895d19e9265839bffeadd74a2d2ecb"),
111+
},
112+
},
113+
Ovr: map[gps.ProjectRoot]gps.ProjectProperties{
114+
gps.ProjectRoot("github.com/sdboyer/gps"): {
115+
NetworkName: "https://github.com/sdboyer/gps",
116+
Constraint: gps.NewBranch("master"),
117+
},
118+
},
119+
Ignores: []string{"github.com/foo/bar"},
120+
}
121+
122+
b, err := json.Marshal(m)
123+
if err != nil {
124+
t.Fatalf("Error while marshaling valid manifest to JSON: %q", err)
125+
}
126+
127+
var out bytes.Buffer
128+
json.Indent(&out, b, "", " ")
129+
b = out.Bytes()
130+
// uuuuuughhhhh
131+
b = bytes.Replace(b, []byte("\\u003c"), []byte("<"), -1)
132+
b = bytes.Replace(b, []byte("\\u003e"), []byte(">"), -1)
133+
134+
s := string(b)
135+
if s != jg {
136+
t.Errorf("Valid manifest did not marshal to JSON as expected:\n\t(GOT): %s\n\t(WNT): %s", s, jg)
137+
}
138+
}

0 commit comments

Comments
 (0)