Skip to content

Commit 358e806

Browse files
smyrmanrs
authored andcommitted
jsonschema: #51 - split into multiple files (#70)
* jsonschema: #51 - split into multiple files This change splits the encoder logic into a separate file per supported schema.FieldValidator type. It also provides some minor clenaups to the documentation and adds some FIXME statements. Functions / type defintions are declared in the order in which they are called, which should make the code "read like an article from top to bottom". * jsonschema: #51 Cosnsistent function names This commit renames all encoder functions to have consistent names.
1 parent cbf74b0 commit 358e806

File tree

13 files changed

+393
-279
lines changed

13 files changed

+393
-279
lines changed

schema/encoding/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package encoding is the intended hierarchy location for encoding Schema to other formats.
2+
package encoding

schema/encoding/encoding.go

Lines changed: 0 additions & 3 deletions
This file was deleted.

schema/encoding/jsonschema/array.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package jsonschema
2+
3+
import (
4+
"io"
5+
"strconv"
6+
7+
"github.com/rs/rest-layer/schema"
8+
)
9+
10+
func encodeArray(w io.Writer, v *schema.Array) error {
11+
ew := errWriter{w: w}
12+
13+
ew.writeString(`"type": "array"`)
14+
if v.MinLen > 0 {
15+
ew.writeFormat(`, "minItems": %s`, strconv.FormatInt(int64(v.MinLen), 10))
16+
}
17+
if v.MaxLen > 0 {
18+
ew.writeFormat(`, "maxItems": %s`, strconv.FormatInt(int64(v.MaxLen), 10))
19+
}
20+
if v.ValuesValidator != nil {
21+
ew.writeString(`, "items": {`)
22+
if ew.err == nil {
23+
ew.err = encodeValidator(w, v.ValuesValidator)
24+
}
25+
ew.writeString("}")
26+
}
27+
return ew.err
28+
}

schema/encoding/jsonschema/bool.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package jsonschema
2+
3+
import (
4+
"io"
5+
6+
"github.com/rs/rest-layer/schema"
7+
)
8+
9+
func encodeBool(w io.Writer, v *schema.Bool) error {
10+
ew := errWriter{w: w}
11+
ew.writeString(`"type": "boolean"`)
12+
return ew.err
13+
}

schema/encoding/jsonschema/doc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Package jsonschema provides JSON Schema Draft 4 encoding support for schema.Schema. Note that the current
2+
// implementation is incomlete, and not all FieldValidator types are yet supported. Custom validators are also not
3+
// supported at the momenet.
4+
package jsonschema

schema/encoding/jsonschema/encoder.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package jsonschema
2+
3+
import (
4+
"fmt"
5+
"io"
6+
7+
"github.com/rs/rest-layer/schema"
8+
)
9+
10+
// Encoder writes the JSON Schema representation of a schema.Schema to an output stream. Note that only a sub-set of the
11+
// FieldValidator types in the schema package is supported at the moment. Custom validators are also not yet handled.
12+
// Attempting to encode a schema containing such fields will result in a ErrNotImplemented error.
13+
type Encoder struct {
14+
io.Writer
15+
}
16+
17+
// NewEncoder returns a new JSONSchema Encoder that writes to w.
18+
func NewEncoder(w io.Writer) *Encoder {
19+
return &Encoder{w}
20+
}
21+
22+
// Encode writes the JSON Schema representation of s to the stream, followed by a newline character.
23+
func (e *Encoder) Encode(s *schema.Schema) error {
24+
ew := errWriter{w: e.Writer}
25+
ew.writeString("{")
26+
if ew.err == nil {
27+
ew.err = encodeSchema(e.Writer, s)
28+
}
29+
ew.writeString("}\n")
30+
return ew.err
31+
}
32+
33+
// Wrap IO writer so we can consolidate error handling in a single place. Also track properties written so we know when
34+
// to emit a separator.
35+
type errWriter struct {
36+
w io.Writer // writer instance
37+
err error // track errors
38+
}
39+
40+
// Write ensures compatibility with the io.Writer interface.
41+
func (ew errWriter) Write(p []byte) (int, error) {
42+
if ew.err != nil {
43+
return 0, ew.err
44+
}
45+
return ew.w.Write(p)
46+
}
47+
48+
func (ew errWriter) writeFormat(format string, a ...interface{}) {
49+
if ew.err != nil {
50+
return
51+
}
52+
_, ew.err = fmt.Fprintf(ew.w, format, a...)
53+
}
54+
55+
func (ew errWriter) writeString(s string) {
56+
if ew.err != nil {
57+
return
58+
}
59+
_, ew.err = ew.w.Write([]byte(s))
60+
}
61+
62+
func (ew errWriter) writeBytes(b []byte) {
63+
if ew.err != nil {
64+
return
65+
}
66+
_, ew.err = ew.w.Write(b)
67+
}

schema/encoding/jsonschema/float.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package jsonschema
2+
3+
import (
4+
"io"
5+
"math"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/rs/rest-layer/schema"
10+
)
11+
12+
func encodeFloat(w io.Writer, v *schema.Float) error {
13+
ew := errWriter{w: w}
14+
15+
ew.writeString(`"type": "number"`)
16+
if len(v.Allowed) > 0 {
17+
var allowed []string
18+
for _, value := range v.Allowed {
19+
allowed = append(allowed, strconv.FormatFloat(value, 'E', -1, 64))
20+
}
21+
ew.writeFormat(`, "enum": [%s]`, strings.Join(allowed, ","))
22+
}
23+
if ew.err == nil {
24+
ew.err = boundariesToJSONSchema(w, v.Boundaries)
25+
}
26+
return ew.err
27+
}
28+
29+
// boundariesToJSONSchema writes JSON Schema keys and values based on b, prefixed by a comma and without curly braces,
30+
// to w. The prefixing comma is only written if at least one key/value pair is also written.
31+
func boundariesToJSONSchema(w io.Writer, b *schema.Boundaries) error {
32+
if b == nil {
33+
return nil
34+
}
35+
ew := errWriter{w: w}
36+
37+
if !math.IsNaN(b.Min) && !math.IsInf(b.Min, -1) {
38+
ew.writeFormat(`, "minimum": %s`, strconv.FormatFloat(b.Min, 'E', -1, 64))
39+
}
40+
if !math.IsNaN(b.Max) && !math.IsInf(b.Max, 1) {
41+
ew.writeFormat(`, "maximum": %s`, strconv.FormatFloat(b.Max, 'E', -1, 64))
42+
}
43+
return ew.err
44+
}

schema/encoding/jsonschema/integer.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package jsonschema
2+
3+
import (
4+
"io"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/rs/rest-layer/schema"
9+
)
10+
11+
func encodeInteger(w io.Writer, v *schema.Integer) error {
12+
ew := errWriter{w: w}
13+
14+
ew.writeString(`"type": "integer"`)
15+
if len(v.Allowed) > 0 {
16+
var allowed []string
17+
for _, value := range v.Allowed {
18+
allowed = append(allowed, strconv.FormatInt(int64(value), 10))
19+
}
20+
ew.writeFormat(`, "enum": [%s]`, strings.Join(allowed, ","))
21+
}
22+
if ew.err == nil {
23+
ew.err = boundariesToJSONSchema(w, v.Boundaries)
24+
}
25+
return ew.err
26+
}

0 commit comments

Comments
 (0)