mirror of
https://github.com/luxfi/utils.git
synced 2026-07-26 22:49:03 +00:00
add json subpackage — canonical JSON-RPC numeric types + lowercase codec
Moves the JSON-RPC numeric wrappers (Uint8/16/32/64, Float32/64) and the gorilla/rpc lowercase Codec helper into the canonical luxfi/utils module. Replaces github.com/luxfi/codec/jsonrpc which has been retained only as a historical artefact of the codec module rip. All Lux modules that need JSON-RPC numeric quoting now have one canonical import: import "github.com/luxfi/utils/json" Identical API to codec/jsonrpc: Uint64.MarshalJSON quotes as decimal string, NewCodec returns a gorilla/rpc Codec that uppercases the first method letter.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package json provides JSON serialization utilities for numeric types and
|
||||
// a gorilla/rpc Codec that normalizes lowercase method names.
|
||||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/rpc/v2"
|
||||
"github.com/gorilla/rpc/v2/json2"
|
||||
)
|
||||
|
||||
// Null is the string representation of the JSON null literal.
|
||||
const Null = "null"
|
||||
|
||||
var (
|
||||
errUppercaseMethod = errors.New("method must start with a non-uppercase letter")
|
||||
errInvalidArg = errors.New("couldn't unmarshal an argument. Ensure arguments are valid and properly formatted. See documentation for example calls")
|
||||
)
|
||||
|
||||
// NewCodec returns a new json codec that requires the first character of the
|
||||
// method name to be lowercase and upper-cases it for dispatch.
|
||||
func NewCodec() rpc.Codec {
|
||||
return lowercase{json2.NewCodec()}
|
||||
}
|
||||
|
||||
type lowercase struct{ *json2.Codec }
|
||||
|
||||
func (lc lowercase) NewRequest(r *http.Request) rpc.CodecRequest {
|
||||
return &request{lc.Codec.NewRequest(r).(*json2.CodecRequest)}
|
||||
}
|
||||
|
||||
type request struct{ *json2.CodecRequest }
|
||||
|
||||
func (r *request) Method() (string, error) {
|
||||
method, err := r.CodecRequest.Method()
|
||||
methodSections := strings.SplitN(method, ".", 2)
|
||||
if len(methodSections) != 2 || err != nil {
|
||||
return method, err
|
||||
}
|
||||
class, function := methodSections[0], methodSections[1]
|
||||
firstRune, runeLen := utf8.DecodeRuneInString(function)
|
||||
if firstRune == utf8.RuneError {
|
||||
return method, nil
|
||||
}
|
||||
if unicode.IsUpper(firstRune) {
|
||||
return method, errUppercaseMethod
|
||||
}
|
||||
uppercaseRune := string(unicode.ToUpper(firstRune))
|
||||
return fmt.Sprintf("%s.%s%s", class, uppercaseRune, function[runeLen:]), nil
|
||||
}
|
||||
|
||||
func (r *request) ReadRequest(args interface{}) error {
|
||||
if err := r.CodecRequest.ReadRequest(args); err != nil {
|
||||
return errInvalidArg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Float32 is a float32 that marshals/unmarshals as a JSON string with 4 digit
|
||||
// fixed-point precision.
|
||||
type Float32 float32
|
||||
|
||||
func (f Float32) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatFloat(float64(f), 'f', 4, 32) + `"`), nil
|
||||
}
|
||||
|
||||
func (f *Float32) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 32)
|
||||
*f = Float32(val)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Float64 is a float64 that marshals/unmarshals as a JSON string.
|
||||
type Float64 float64
|
||||
|
||||
func (f Float64) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatFloat(float64(f), 'f', -1, 64) + `"`), nil
|
||||
}
|
||||
|
||||
func (f *Float64) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseFloat(str, 64)
|
||||
*f = Float64(val)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Uint16 is a uint16 that marshals/unmarshals as a JSON string.
|
||||
type Uint16 uint16
|
||||
|
||||
func (u Uint16) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatUint(uint64(u), 10) + `"`), nil
|
||||
}
|
||||
|
||||
func (u *Uint16) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseUint(str, 10, 16)
|
||||
*u = Uint16(val)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Uint32 is a uint32 that marshals/unmarshals as a JSON string.
|
||||
type Uint32 uint32
|
||||
|
||||
func (u Uint32) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatUint(uint64(u), 10) + `"`), nil
|
||||
}
|
||||
|
||||
func (u *Uint32) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseUint(str, 10, 32)
|
||||
*u = Uint32(val)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Uint64 is a uint64 that marshals/unmarshals as a JSON string.
|
||||
type Uint64 uint64
|
||||
|
||||
func (u Uint64) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatUint(uint64(u), 10) + `"`), nil
|
||||
}
|
||||
|
||||
func (u *Uint64) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseUint(str, 10, 64)
|
||||
*u = Uint64(val)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package json
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Uint8 is a uint8 that marshals/unmarshals as a JSON string.
|
||||
type Uint8 uint8
|
||||
|
||||
func (u Uint8) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + strconv.FormatUint(uint64(u), 10) + `"`), nil
|
||||
}
|
||||
|
||||
func (u *Uint8) UnmarshalJSON(b []byte) error {
|
||||
str := string(b)
|
||||
if str == Null {
|
||||
return nil
|
||||
}
|
||||
if len(str) >= 2 {
|
||||
if lastIndex := len(str) - 1; str[0] == '"' && str[lastIndex] == '"' {
|
||||
str = str[1:lastIndex]
|
||||
}
|
||||
}
|
||||
val, err := strconv.ParseUint(str, 10, 8)
|
||||
*u = Uint8(val)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user