Skip to content

Commit

Permalink
support RFC 9651
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo82148 committed Dec 1, 2024
1 parent e4ef2fd commit b112e8d
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 36 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/shogo82148/go-sfv.svg)](https://pkg.go.dev/github.com/shogo82148/go-sfv)
[![Test](https://github.com/shogo82148/go-sfv/actions/workflows/test.yml/badge.svg)](https://github.com/shogo82148/go-sfv/actions/workflows/test.yml)

Go implementation for [RFC 8941 Structured Field Values for HTTP](https://www.rfc-editor.org/rfc/rfc8941.html) (SFV).
It also supports Dates and Display Strings defined by [draft-ietf-httpbis-sfbis-03](https://datatracker.ietf.org/doc/draft-ietf-httpbis-sfbis/03/).
Go implementation for [RFC 9651 Structured Field Values for HTTP](https://www.rfc-editor.org/rfc/rfc8941.html) (SFV).

## Synopsis

Expand Down Expand Up @@ -54,7 +53,7 @@ Note that only **Lists**(`sfv.List`), **Dictionaries**(`sfv.Dictionary`), and **
The `sfv.Value` is defined as the following:

```go
type Value interface{}
type Value any
```

The actual type might be one of them:
Expand Down Expand Up @@ -128,7 +127,6 @@ type Dictionary []DictMember

## References

- [RFC 8941 Structured Field Values for HTTP](https://www.rfc-editor.org/rfc/rfc8941.html)
- [draft-ietf-httpbis-sfbis-03](https://datatracker.ietf.org/doc/draft-ietf-httpbis-sfbis/03/)
- [RFC 9651 Structured Field Values for HTTP](https://www.rfc-editor.org/rfc/rfc9651.html)
- [Structured Field Values による Header Field の構造化](https://blog.jxck.io/entries/2021-01-31/structured-field-values.html)
- [IETF RFC における ABNF と Parsing Algorithm の関係](https://blog.jxck.io/entries/2023-05-17/abnf-or-algorithm-in-rfc.html)
24 changes: 10 additions & 14 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (s *decodeState) errUnexpectedCharacter() error {
return fmt.Errorf("sfv: unexpected character: %q", ch)
}

// decodeItem parses an Item according to RFC 8941 Section 4.2.3.
// decodeItem parses an Item according to RFC 9651 Section 4.2.3.
func (s *decodeState) decodeItem() (Item, error) {
v, err := s.decodeBareItem()
if err != nil {
Expand All @@ -331,7 +331,7 @@ func (s *decodeState) decodeItem() (Item, error) {
}, nil
}

// decodeBareItem parses a bare item according to RFC 8941 Section 4.2.3.1.
// decodeBareItem parses a bare item according to RFC 9651 Section 4.2.3.1.
func (s *decodeState) decodeBareItem() (Value, error) {
ch := s.peek()
switch {
Expand Down Expand Up @@ -366,7 +366,7 @@ func (s *decodeState) decodeBareItem() (Value, error) {
return nil, s.errUnexpectedCharacter()
}

// decodeIntegerOrDecimal parses an Integer or Decimal according to RFC 8941 Section 4.2.4.
// decodeIntegerOrDecimal parses an Integer or Decimal according to RFC 9651 Section 4.2.4.
func (s *decodeState) decodeIntegerOrDecimal() (Value, error) {
ch := s.peek()
neg := false
Expand Down Expand Up @@ -450,7 +450,7 @@ func (s *decodeState) decodeIntegerOrDecimal() (Value, error) {
return nil, errors.New("sfv: decimal has too long fractional part")
}

// decodeString parses a String according to RFC 8941 Section 4.2.5.
// decodeString parses a String according to RFC 9651 Section 4.2.5.
func (s *decodeState) decodeString() (Value, error) {
if ch := s.peek(); ch != '"' {
return nil, s.errUnexpectedCharacter()
Expand Down Expand Up @@ -485,7 +485,7 @@ func (s *decodeState) decodeString() (Value, error) {
}
}

// decodeToken parses a Token according to RFC 8941 Section 4.2.6.
// decodeToken parses a Token according to RFC 9651 Section 4.2.6.
func (s *decodeState) decodeToken() (Value, error) {
var buf strings.Builder
for {
Expand All @@ -502,7 +502,7 @@ func (s *decodeState) decodeToken() (Value, error) {
}
}

// decodeBytesSequence parses a Byte Sequence according to RFC 8941 Section 4.2.7.
// decodeBytesSequence parses a Byte Sequence according to RFC 9651 Section 4.2.7.
func (s *decodeState) decodeByteSequence() (Value, error) {
if ch := s.peek(); ch != ':' {
return nil, s.errUnexpectedCharacter()
Expand All @@ -519,7 +519,7 @@ func (s *decodeState) decodeByteSequence() (Value, error) {
s.next() // skip ':'

// add missing "=" padding
// RFC 8941 says that parsers SHOULD NOT fail when "=" padding is not present.
// RFC 9651 says that parsers SHOULD NOT fail when "=" padding is not present.
switch s.buf.Len() % 4 {
case 0:
case 1:
Expand Down Expand Up @@ -548,7 +548,7 @@ func (s *decodeState) decodeByteSequence() (Value, error) {
}
}

// decodeBoolean parses a Boolean according to RFC 8941 Section 4.2.8.
// decodeBoolean parses a Boolean according to RFC 9651 Section 4.2.8.
func (s *decodeState) decodeBoolean() (Value, error) {
if ch := s.peek(); ch != '?' {
return nil, s.errUnexpectedCharacter()
Expand All @@ -566,9 +566,7 @@ func (s *decodeState) decodeBoolean() (Value, error) {
}
}

// decodeDate parses a Date according to [sfbis-03 4.2.9. Parsing a Date]
//
// [sfbis-03 4.2.9. Parsing a Date]: https://www.ietf.org/archive/id/draft-ietf-httpbis-sfbis-03.html#name-parsing-a-date
// decodeDate parses a Date according to RFC 9651 Section 4.2.9.
func (s *decodeState) decodeDate() (Value, error) {
if ch := s.peek(); ch != '@' {
return nil, s.errUnexpectedCharacter()
Expand Down Expand Up @@ -611,9 +609,7 @@ func (s *decodeState) decodeDate() (Value, error) {
return time.Unix(num, 0), nil
}

// decodeDate parses a Date according to [sfbis-03 4.2.10. Parsing a Display String]
//
// [sfbis-03 4.2.10. Parsing a Display String]: https://www.ietf.org/archive/id/draft-ietf-httpbis-sfbis-03.html#name-parsing-a-display-string
// decodeDisplayString parses a Date according to RFC 9651 Section 4.2.10.
func (s *decodeState) decodeDisplayString() (Value, error) {
if ch := s.peek(); ch != '%' {
return nil, s.errUnexpectedCharacter()
Expand Down
20 changes: 9 additions & 11 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type encodeState struct {
buf bytes.Buffer
}

// encodeItem serializes an item according to RFC 8941 Section 4.1.3.
// encodeItem serializes an item according to RFC 9651 Section 4.1.3.
func (s *encodeState) encodeItem(item Item) error {
if err := s.encodeBareItem(item.Value); err != nil {
return err
Expand All @@ -50,7 +50,7 @@ func (s *encodeState) encodeItem(item Item) error {
return nil
}

// encodeInteger serializes an integer according to RFC 8941 Section 4.1.4.
// encodeInteger serializes an integer according to RFC 9651 Section 4.1.4.
func (s *encodeState) encodeInteger(v int64) error {
if v > MaxInteger || v < MinInteger {
return fmt.Errorf("sfv: integer %d is out of range", v)
Expand All @@ -61,7 +61,7 @@ func (s *encodeState) encodeInteger(v int64) error {
return nil
}

// encodeDecimal serializes an decimal according to RFC 8941 Section 4.1.5.
// encodeDecimal serializes an decimal according to RFC 9651 Section 4.1.5.
func (s *encodeState) encodeDecimal(v float64) error {
i := int64(math.RoundToEven(v * 1000))
if i > MaxInteger || i < MinInteger {
Expand Down Expand Up @@ -97,7 +97,7 @@ func (s *encodeState) encodeDecimal(v float64) error {
return nil
}

// encodeBinary serializes a byte sequence according to RFC 8941 Section 4.1.8.
// encodeBinary serializes a byte sequence according to RFC 9651 Section 4.1.8.
func (s *encodeState) encodeByteSequence(v []byte) error {
// allocate a buffer
l := base64.StdEncoding.EncodedLen(len(v)) + 2
Expand All @@ -120,9 +120,7 @@ func (s *encodeState) encodeByteSequence(v []byte) error {
return nil
}

// encodeDisplayString serializes a display string according to [sfbis-03 Section 4.1.11].
//
// [sfbis-03 Section 4.1.11]: https://www.ietf.org/archive/id/draft-ietf-httpbis-sfbis-03.html#name-serializing-a-display-strin
// encodeDisplayString serializes a display string according to RFC 9651 Section 4.1.11.
func (s *encodeState) encodeDisplayString(v string) error {
if !utf8.ValidString(v) {
return fmt.Errorf("sfv: display string %q has invalid characters", v)
Expand All @@ -141,7 +139,7 @@ func (s *encodeState) encodeDisplayString(v string) error {
return nil
}

// encodeBareItem serializes a bare item according to RFC 8941 Section 4.1.3.1.
// encodeBareItem serializes a bare item according to RFC 9651 Section 4.1.3.1.
func (s *encodeState) encodeBareItem(v Value) error {
switch v := v.(type) {
case int8:
Expand Down Expand Up @@ -268,7 +266,7 @@ func (s *encodeState) encodeBareItemOrInnerList(value Value) error {
return s.encodeBareItem(value)
}

// encodeInnerList serializes an inner list according to RFC 8941 Section 4.1.1.1.
// encodeInnerList serializes an inner list according to RFC 9651 Section 4.1.1.1.
func (s *encodeState) encodeInnerList(list InnerList) error {
s.buf.WriteByte('(')
for i, item := range list {
Expand All @@ -283,7 +281,7 @@ func (s *encodeState) encodeInnerList(list InnerList) error {
return nil
}

// encodeList serializes a list according to RFC 8941 Section 4.1.1.
// encodeList serializes a list according to RFC 9651 Section 4.1.1.
func (s *encodeState) encodeList(list List) error {
for i, item := range list {
if err := s.encodeBareItemOrInnerList(item.Value); err != nil {
Expand All @@ -299,7 +297,7 @@ func (s *encodeState) encodeList(list List) error {
return nil
}

// encodeDictionary serializes a dictionary according to RFC 8941 Section 4.1.2.
// encodeDictionary serializes a dictionary according to RFC 9651 Section 4.1.2.
func (s *encodeState) encodeDictionary(dict Dictionary) error {
for i, item := range dict {
if err := s.encodeKey(item.Key); err != nil {
Expand Down
12 changes: 6 additions & 6 deletions sfv.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
MinDecimal = -0x1.d1a94a1fffffbp+39 // = -999999999999.9993896484375
)

// Token is a token defined in RFC 8941 Section 3.3.4. Tokens.
// Token is a token defined in RFC 9651 Section 3.3.4. Tokens.
// The token must match the following regular expression:
//
// [a-zA-Z*][a-zA-Z0-9:/!#$%&'*+_.^_`|~-]*
Expand Down Expand Up @@ -81,7 +81,7 @@ type Parameter struct {
Value Value
}

// Parameters are an ordered map of key-value pairs defined in RFC 8941 Section 3.1.2. Parameters.
// Parameters are an ordered map of key-value pairs defined in RFC 9651 Section 3.1.2. Parameters.
type Parameters []Parameter

// Get returns the last value associated with the given key.
Expand All @@ -102,16 +102,16 @@ func (param Parameters) Len() int {
return len(param)
}

// Item is an item defined RFC 8941 Section 3.3. Items.
// Item is an item defined RFC 9651 Section 3.3. Items.
type Item struct {
Value Value
Parameters Parameters
}

// InnerList is an array defined in RFC 8941 Section 3.1.1. Inner Lists.
// InnerList is an array defined in RFC 9651 Section 3.1.1. Inner Lists.
type InnerList []Item

// List is an array defined in RFC 8941 Section 3.1. Lists.
// List is an array defined in RFC 9651 Section 3.1. Lists.
type List []Item

// DictMember is a key-value pair of Dictionary.
Expand All @@ -120,7 +120,7 @@ type DictMember struct {
Item Item
}

// Dictionary is an ordered map of key-value pairs defined in RFC 8941 Section 3.2. Dictionaries.
// Dictionary is an ordered map of key-value pairs defined in RFC 9651 Section 3.2. Dictionaries.
type Dictionary []DictMember

// Get returns the last item associated with the given key.
Expand Down

0 comments on commit b112e8d

Please sign in to comment.