initial commits
parent
e688238608
commit
2bb703cc30
|
@ -0,0 +1,19 @@
|
|||
package rfc8259wholenumber
|
||||
|
||||
import (
|
||||
"sourcecode.social/reiver/go-erorr"
|
||||
)
|
||||
|
||||
const (
|
||||
errNilDestination = erorr.Error("rfc8259: nil destination")
|
||||
errNilRuneScanner = erorr.Error("rfc8259: nil rune-scanner")
|
||||
errUnexpectedEndOfFile = erorr.Error("rfc8259: unexpected end-of-file")
|
||||
)
|
||||
|
||||
func errProblemReadingRune(err error) error {
|
||||
return erorr.Errorf("rfc8259: the JSON parser had a problem — problem reading rune: %w", err)
|
||||
}
|
||||
|
||||
func errProblemUnreadingRune(err error, r rune) error {
|
||||
return erorr.Errorf("rfc8259: the JSON parser had an internal-error — problem unreading rune %q (%U): %w", r, r, err)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package rfc8259wholenumber
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"sourcecode.social/reiver/go-erorr"
|
||||
)
|
||||
|
||||
// Parse tries to parse the JSON whole-number literal.
|
||||
// If it succeeds, then it return nil.
|
||||
// If it failed, it returns an error.
|
||||
//
|
||||
// IETF RFC-8259 calls a whole number an "int" with this definition:
|
||||
//
|
||||
// digit1-9 = %x31-39 ; 1-9
|
||||
//
|
||||
// int = zero / ( digit1-9 *DIGIT )
|
||||
//
|
||||
// zero = %x30 ; 0
|
||||
//
|
||||
// To avoid confusion, rather than calling this "Int" as IETF RFC-8259 does (as in other contexts
|
||||
// often "int" and "integer" include negative numbers) we call this "WholeNumber".
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// var runescanner io.RuneScanner
|
||||
//
|
||||
// // ...
|
||||
//
|
||||
// var dst rfc8259wholenumber.WholeNumber
|
||||
// err := rfc8259wholenumber.Parse(runescanner, &dst)
|
||||
//
|
||||
// if nil != err {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// fmt.Printf("dst = %#v\n", dst)
|
||||
func Parse(runescanner io.RuneScanner, dst *WholeNumber) error {
|
||||
if nil == runescanner {
|
||||
return errNilRuneScanner
|
||||
}
|
||||
if nil == dst {
|
||||
return errNilDestination
|
||||
}
|
||||
|
||||
var buffer [256]byte
|
||||
var p []byte = buffer[0:0]
|
||||
|
||||
{
|
||||
var r rune
|
||||
{
|
||||
var err error
|
||||
|
||||
r, _, err = runescanner.ReadRune()
|
||||
if nil != err {
|
||||
if io.EOF == err {
|
||||
return errUnexpectedEndOfFile
|
||||
}
|
||||
return errProblemReadingRune(err)
|
||||
}
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '0':
|
||||
*dst = Zero()
|
||||
return nil
|
||||
case '1','2','3','4','5','6','7','8','9':
|
||||
p = append(p, string(r)...)
|
||||
default:
|
||||
return erorr.Errorf("rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was %q (%U)", r, r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
loop: for {
|
||||
var r rune
|
||||
{
|
||||
var err error
|
||||
|
||||
r, _, err = runescanner.ReadRune()
|
||||
if nil != err && io.EOF != err {
|
||||
return errProblemReadingRune(err)
|
||||
}
|
||||
if io.EOF == err {
|
||||
/////////////////////// BREAK
|
||||
break loop
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '0','1','2','3','4','5','6','7','8','9':
|
||||
p = append(p, string(r)...)
|
||||
default:
|
||||
if err := runescanner.UnreadRune(); nil != err {
|
||||
return errProblemUnreadingRune(err, r)
|
||||
}
|
||||
/////////////////////// BREAK
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Something(string(p))
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
package rfc8259wholenumber_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"sourcecode.social/reiver/go-utf8"
|
||||
|
||||
"sourcecode.social/reiver/go-rfc8259/wholenumber"
|
||||
)
|
||||
|
||||
func TestParse_success(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Value []byte
|
||||
Expected rfc8259wholenumber.WholeNumber
|
||||
}{
|
||||
{
|
||||
Value: []byte("0"),
|
||||
Expected: rfc8259wholenumber.Zero(),
|
||||
},
|
||||
{
|
||||
Value: []byte("1"),
|
||||
Expected: rfc8259wholenumber.One(),
|
||||
},
|
||||
{
|
||||
Value: []byte("2"),
|
||||
Expected: rfc8259wholenumber.Something("2"),
|
||||
},
|
||||
{
|
||||
Value: []byte("3"),
|
||||
Expected: rfc8259wholenumber.Something("3"),
|
||||
},
|
||||
{
|
||||
Value: []byte("4"),
|
||||
Expected: rfc8259wholenumber.Something("4"),
|
||||
},
|
||||
{
|
||||
Value: []byte("5"),
|
||||
Expected: rfc8259wholenumber.Something("5"),
|
||||
},
|
||||
{
|
||||
Value: []byte("6"),
|
||||
Expected: rfc8259wholenumber.Something("6"),
|
||||
},
|
||||
{
|
||||
Value: []byte("7"),
|
||||
Expected: rfc8259wholenumber.Something("7"),
|
||||
},
|
||||
{
|
||||
Value: []byte("8"),
|
||||
Expected: rfc8259wholenumber.Something("8"),
|
||||
},
|
||||
{
|
||||
Value: []byte("9"),
|
||||
Expected: rfc8259wholenumber.Something("9"),
|
||||
},
|
||||
{
|
||||
Value: []byte("10"),
|
||||
Expected: rfc8259wholenumber.Something("10"),
|
||||
},
|
||||
{
|
||||
Value: []byte("11"),
|
||||
Expected: rfc8259wholenumber.Something("11"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("127"),
|
||||
Expected: rfc8259wholenumber.Something("127"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7867"),
|
||||
Expected: rfc8259wholenumber.Something("7867"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7873"),
|
||||
Expected: rfc8259wholenumber.Something("7873"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7877"),
|
||||
Expected: rfc8259wholenumber.Something("7877"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7879"),
|
||||
Expected: rfc8259wholenumber.Something("7879"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7883"),
|
||||
Expected: rfc8259wholenumber.Something("7883"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7901"),
|
||||
Expected: rfc8259wholenumber.Something("7901"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7907"),
|
||||
Expected: rfc8259wholenumber.Something("7907"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("7919"),
|
||||
Expected: rfc8259wholenumber.Something("7919"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("999331"),
|
||||
Expected: rfc8259wholenumber.Something("999331"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("8683317618811886495518194401279999999"),
|
||||
Expected: rfc8259wholenumber.Something("8683317618811886495518194401279999999"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("13256278887989457651018865901401704640"),
|
||||
Expected: rfc8259wholenumber.Something("13256278887989457651018865901401704640"),
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("123.45"),
|
||||
Expected: rfc8259wholenumber.Something("123"),
|
||||
},
|
||||
{
|
||||
Value: []byte("123 "),
|
||||
Expected: rfc8259wholenumber.Something("123"),
|
||||
},
|
||||
{
|
||||
Value: []byte("123,"),
|
||||
Expected: rfc8259wholenumber.Something("123"),
|
||||
},
|
||||
{
|
||||
Value: []byte("123e"),
|
||||
Expected: rfc8259wholenumber.Something("123"),
|
||||
},
|
||||
{
|
||||
Value: []byte("123E"),
|
||||
Expected: rfc8259wholenumber.Something("123"),
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
var reader io.Reader = bytes.NewReader(test.Value)
|
||||
var runescanner io.RuneScanner = utf8.NewRuneScanner(reader)
|
||||
|
||||
var actual rfc8259wholenumber.WholeNumber
|
||||
err := rfc8259wholenumber.Parse(runescanner, &actual)
|
||||
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect to get an error but actually got one.", testNumber)
|
||||
t.Logf("ERROR: (%T) %s", err, err)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
t.Logf("VALUE: %#v", test.Value)
|
||||
t.Logf("EXPECTED: %#v", test.Expected)
|
||||
continue
|
||||
}
|
||||
|
||||
{
|
||||
expected := test.Expected
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("For test #%d, the actual value is not what was expected.", testNumber)
|
||||
t.Logf("EXPECTED: %#v", expected)
|
||||
t.Logf("ACTUAL: %#v", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
t.Logf("VALUE: %#v", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_failure(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Value []byte
|
||||
ExpectedError string
|
||||
}{
|
||||
{
|
||||
Value: []byte(nil),
|
||||
ExpectedError: "rfc8259: unexpected end-of-file",
|
||||
},
|
||||
{
|
||||
Value: []byte(""),
|
||||
ExpectedError: "rfc8259: unexpected end-of-file",
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("\t"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '\t' (U+0009)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("\n"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '\n' (U+000A)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("\r"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '\r' (U+000D)`,
|
||||
},
|
||||
{
|
||||
Value: []byte(" "),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was ' ' (U+0020)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("\""),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '"' (U+0022)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("-"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '-' (U+002D)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("f"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'f' (U+0066)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("n"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'n' (U+006E)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("t"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 't' (U+0074)`,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("false"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'f' (U+0066)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("null"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'n' (U+006E)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("true"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 't' (U+0074)`,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("-1"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '-' (U+002D)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("-2"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '-' (U+002D)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("-3"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '-' (U+002D)`,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("\"name\""),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '"' (U+0022)`,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("apple"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'a' (U+0061)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("banana"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'b' (U+0062)`,
|
||||
},
|
||||
{
|
||||
Value: []byte("cherry"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'c' (U+0063)`,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("ONCE TWICE THRICE FOURCE"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was 'O' (U+004F)`,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Value: []byte("😈"),
|
||||
ExpectedError: `rfc8259: JSON parser encountered a problem — when trying to parse a whole-number, expected the first character to be '0', '1', '2', '3', '4', '5', '6', '7', '8', ot '9', but actually was '😈' (U+1F608)`,
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
var reader io.Reader = bytes.NewReader(test.Value)
|
||||
var runescanner io.RuneScanner = utf8.NewRuneScanner(reader)
|
||||
|
||||
var actual rfc8259wholenumber.WholeNumber
|
||||
err := rfc8259wholenumber.Parse(runescanner, &actual)
|
||||
|
||||
if nil == err {
|
||||
t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
t.Logf("VALUE: %#v", test.Value)
|
||||
t.Logf("EXPECTED-ERROR: %#v", test.ExpectedError)
|
||||
continue
|
||||
}
|
||||
|
||||
{
|
||||
expected := rfc8259wholenumber.Nothing()
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("For test #%d, the actual value is not what was expected.", testNumber)
|
||||
t.Logf("EXPECTED: %#v", expected)
|
||||
t.Logf("ACTUAL: %#v", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
t.Logf("VALUE: %#v", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
expected := test.ExpectedError
|
||||
actual := err.Error()
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("For test #%d, the actual error value is not what was expected.", testNumber)
|
||||
t.Logf("EXPECTED: %#v", expected)
|
||||
t.Logf("ACTUAL: %#v", actual)
|
||||
t.Logf("VALUE: %q", test.Value)
|
||||
t.Logf("VALUE: %#v", test.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package rfc8259wholenumber
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sourcecode.social/reiver/go-opt"
|
||||
)
|
||||
|
||||
// WholeNumber represents the numbers:
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,....
|
||||
//
|
||||
// I.e., the set of positive-integers with zero.
|
||||
//
|
||||
// From IETF RFC-8259 WholeNumber represents the following:
|
||||
//
|
||||
// int = zero / ( digit1-9 *DIGIT )
|
||||
type WholeNumber struct {
|
||||
opt.Optional[string]
|
||||
}
|
||||
|
||||
func Nothing() WholeNumber {
|
||||
return WholeNumber{opt.Nothing[string]()}
|
||||
}
|
||||
|
||||
func Zero() WholeNumber {
|
||||
return Something("0")
|
||||
}
|
||||
|
||||
func One() WholeNumber {
|
||||
return Something("1")
|
||||
}
|
||||
|
||||
func Something(value string) WholeNumber {
|
||||
return WholeNumber{opt.Something(value)}
|
||||
}
|
||||
|
||||
func (receiver WholeNumber) GoString() string {
|
||||
switch receiver {
|
||||
case Nothing():
|
||||
return "rfc8259wholenumber.Nothing()"
|
||||
case Zero():
|
||||
return "rfc8259wholenumber.Zero()"
|
||||
case One():
|
||||
return "rfc8259wholenumber.One()"
|
||||
default:
|
||||
value, found := receiver.Get()
|
||||
if !found {
|
||||
return fmt.Sprintf("--INTERNAL-ERROR--")
|
||||
}
|
||||
return fmt.Sprintf("rfc8259wholenumber.Something(%#v)", value)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue