initial commits
parent
2bb703cc30
commit
fe4aafd2ce
|
@ -0,0 +1,64 @@
|
||||||
|
package rfc8259naturalnumber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-opt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NaturalNumber represents the numbers:
|
||||||
|
// ..., -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7,....
|
||||||
|
//
|
||||||
|
// I.e., the set of positive-integers, negative-integers, and zero.
|
||||||
|
//
|
||||||
|
// From IETF RFC-8259 NaturalNumber represents the following:
|
||||||
|
//
|
||||||
|
// [ minus ] int
|
||||||
|
//
|
||||||
|
// This is part of the definition of:
|
||||||
|
//
|
||||||
|
// number = [ minus ] int [ frac ] [ exp ]
|
||||||
|
//
|
||||||
|
// Since a NaturalNumber is a common usage of a JSON number, NaturalNumber exists.
|
||||||
|
type NaturalNumber struct {
|
||||||
|
opt.Optional[string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Nothing() NaturalNumber {
|
||||||
|
return NaturalNumber{opt.Nothing[string]()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NegativeOne() NaturalNumber {
|
||||||
|
return Something("-1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Zero() NaturalNumber {
|
||||||
|
return Something("0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func One() NaturalNumber {
|
||||||
|
return Something("1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Something(value string) NaturalNumber {
|
||||||
|
return NaturalNumber{opt.Something(value)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver NaturalNumber) GoString() string {
|
||||||
|
switch receiver {
|
||||||
|
case Nothing():
|
||||||
|
return "rfc8259naturalnumber.Nothing()"
|
||||||
|
case NegativeOne():
|
||||||
|
return "rfc8259naturalnumber.NegativeOne()"
|
||||||
|
case Zero():
|
||||||
|
return "rfc8259naturalnumber.Zero()"
|
||||||
|
case One():
|
||||||
|
return "rfc8259naturalnumber.One()"
|
||||||
|
default:
|
||||||
|
value, found := receiver.Get()
|
||||||
|
if !found {
|
||||||
|
return fmt.Sprintf("--INTERNAL-ERROR--")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("rfc8259naturalnumber.Something(%#v)", value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package rfc8259naturalnumber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-opt"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-rfc8259/errors"
|
||||||
|
"sourcecode.social/reiver/go-rfc8259/wholenumber"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse tries to parse a JSON natural-number literal.
|
||||||
|
// If it succeeds, then it return nil, and sets ‘dst’ to the parsed value.
|
||||||
|
// If it failed, it returns an error.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// var rs io.RuneScaner
|
||||||
|
//
|
||||||
|
// // ...
|
||||||
|
//
|
||||||
|
// var value rfc8259naturalnumber.NaturalNumber
|
||||||
|
// err := rfc8259naturalnumber.Parse(rs, &value)
|
||||||
|
//
|
||||||
|
// if nil != err {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fmt.Printf("value = %#v\n", value)
|
||||||
|
func Parse(runescanner io.RuneScanner, dst *NaturalNumber) error {
|
||||||
|
if nil == runescanner {
|
||||||
|
return rfc8259errors.ErrNilRuneScanner
|
||||||
|
}
|
||||||
|
if nil == dst {
|
||||||
|
return rfc8259errors.ErrNilDestination
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rune
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
|
||||||
|
r, _, err = runescanner.ReadRune()
|
||||||
|
if nil != err {
|
||||||
|
if io.EOF == err {
|
||||||
|
return rfc8259errors.ErrUnexpectedEndOfFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return rfc8259errors.ErrProblemReadingRune(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var minusPart opt.Optional[rune]
|
||||||
|
var intPart rfc8259wholenumber.WholeNumber
|
||||||
|
|
||||||
|
switch r {
|
||||||
|
case '-':
|
||||||
|
minusPart = opt.Something(r)
|
||||||
|
default:
|
||||||
|
if err := runescanner.UnreadRune(); nil != err {
|
||||||
|
return rfc8259errors.ErrProblemUnreadingRune(err, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rfc8259wholenumber.Parse(runescanner, &intPart)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
{
|
||||||
|
var buffer [256]byte
|
||||||
|
var p []byte = buffer[0:0]
|
||||||
|
|
||||||
|
minusPart.WhenSomething(func(value rune){
|
||||||
|
p = append(p, string(value)...)
|
||||||
|
})
|
||||||
|
|
||||||
|
intPart.WhenSomething(func(value string){
|
||||||
|
p = append(p, value...)
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(p) < 1 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
value = string(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = Something(value)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
package rfc8259naturalnumber_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-utf8"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-rfc8259/naturalnumber"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse_success(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct{
|
||||||
|
Value []byte
|
||||||
|
Expected rfc8259naturalnumber.NaturalNumber
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Value: []byte("0"),
|
||||||
|
Expected: rfc8259naturalnumber.Zero(),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("1"),
|
||||||
|
Expected: rfc8259naturalnumber.One(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-1"),
|
||||||
|
Expected: rfc8259naturalnumber.NegativeOne(),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("2"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-2"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-2"),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("3"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("3"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-3"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-3"),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("13"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("13"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-13"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-13"),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("13256278887989457651018865901401704640"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("13256278887989457651018865901401704640"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-13256278887989457651018865901401704640"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-13256278887989457651018865901401704640"),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("123.45"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("123 "),
|
||||||
|
Expected: rfc8259naturalnumber.Something("123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("123,"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("123e"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("123E"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("123"),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
Value: []byte("-123.45"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-123 "),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-123,"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-123e"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-123"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: []byte("-123E"),
|
||||||
|
Expected: rfc8259naturalnumber.Something("-123"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for testNumber, test := range tests {
|
||||||
|
|
||||||
|
var reader io.Reader = bytes.NewReader(test.Value)
|
||||||
|
var runescanner io.RuneScanner = utf8.NewRuneScanner(reader)
|
||||||
|
|
||||||
|
var actual rfc8259naturalnumber.NaturalNumber
|
||||||
|
err := rfc8259naturalnumber.Parse(runescanner, &actual)
|
||||||
|
|
||||||
|
if nil != err {
|
||||||
|
t.Errorf("For test #%d, did not expect 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)
|
||||||
|
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("-"),
|
||||||
|
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("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("\"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 rfc8259naturalnumber.NaturalNumber
|
||||||
|
err := rfc8259naturalnumber.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)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
expected := rfc8259naturalnumber.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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue