22// Use of this source code is governed by a BSD-style
33// license that can be found in the LICENSE file.
44
5- // Package httpsfv provide functionality for dealing with HTTP Structured Field
6- // Values.
5+ // Package httpsfv provides functionality for dealing with HTTP Structured
6+ // Field Values.
77package httpsfv
88
99import (
1010 "slices"
11+ "strconv"
12+ "strings"
1113 "unicode/utf8"
1214)
1315
@@ -70,15 +72,14 @@ func decOctetHex(ch1, ch2 byte) (ch byte, ok bool) {
7072 return ch1 << 4 | ch2 , true
7173}
7274
73- // TODO(nsh): Implement corresponding parse functions for all consume functions
74- // that exists .
75+ // TODO(nsh): Implement parse functions for date and display string to make
76+ // this package fully support parsing RFC 9651-compliant HTTP SFV .
7577
76- // ParseList is used to parse a string that represents a list in an
77- // HTTP Structured Field Values.
78+ // ParseList parses a list from a given HTTP Structured Field Values.
7879//
79- // Given a string that represents a list, it will call the given function using
80- // each of the members and parameters contained in the list. This allows the
81- // caller to extract information out of the list.
80+ // Given an HTTP SFV string that represents a list, it will call the given
81+ // function using each of the members and parameters contained in the list.
82+ // This allows the caller to extract information out of the list.
8283//
8384// This function will return once it encounters the end of the string, or
8485// something that is not a list. If it cannot consume the entire given
@@ -123,7 +124,7 @@ func ParseList(s string, f func(member, param string)) (ok bool) {
123124// consumeBareInnerList consumes an inner list
124125// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
125126// except for the inner list's top-most parameter.
126- // For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`
127+ // For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`.
127128func consumeBareInnerList (s string , f func (bareItem , param string )) (consumed , rest string , ok bool ) {
128129 if len (s ) == 0 || s [0 ] != '(' {
129130 return "" , s , false
@@ -152,18 +153,18 @@ func consumeBareInnerList(s string, f func(bareItem, param string)) (consumed, r
152153 return s [:len (s )- len (rest )], rest , true
153154}
154155
155- // ParseBareInnerList is used to parse a string that represents a bare inner
156- // list in an HTTP Structured Field Values.
156+ // ParseBareInnerList parses a bare inner list from a given HTTP Structured
157+ // Field Values.
157158//
158159// We define a bare inner list as an inner list
159160// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
160161// without the top-most parameter of the inner list. For example, given the
161162// inner list `(a;b c;d);e`, the bare inner list would be `(a;b c;d)`.
162163//
163- // Given a string that represents a bare inner list, it will call the given
164- // function using each of the bare item and parameter within the bare inner
165- // list. This allows the caller to extract information out of the bare inner
166- // list.
164+ // Given an HTTP SFV string that represents a bare inner list, it will call the
165+ // given function using each of the bare item and parameter within the bare
166+ // inner list. This allows the caller to extract information out of the bare
167+ // inner list.
167168//
168169// This function will return once it encounters the end of the bare inner list,
169170// or something that is not a bare inner list. If it cannot consume the entire
@@ -188,12 +189,11 @@ func consumeItem(s string, f func(bareItem, param string)) (consumed, rest strin
188189 return s [:len (s )- len (rest )], rest , true
189190}
190191
191- // ParseItem is used to parse a string that represents an item in an HTTP
192- // Structured Field Values.
192+ // ParseItem parses an item from a given HTTP Structured Field Values.
193193//
194- // Given a string that represents an item, it will call the given function
195- // once, with the bare item and the parameter of the item. This allows the
196- // caller to extract information out of the parameter .
194+ // Given an HTTP SFV string that represents an item, it will call the given
195+ // function once, with the bare item and the parameter of the item. This allows
196+ // the caller to extract information out of the item .
197197//
198198// This function will return once it encounters the end of the string, or
199199// something that is not an item. If it cannot consume the entire given
@@ -205,12 +205,13 @@ func ParseItem(s string, f func(bareItem, param string)) (ok bool) {
205205 return rest == "" && ok
206206}
207207
208- // ParseDictionary is used to parse a string that represents a dictionary in an
209- // HTTP Structured Field Values.
208+ // ParseDictionary parses a dictionary from a given HTTP Structured Field
209+ // Values.
210210//
211- // Given a string that represents a dictionary, it will call the given function
212- // using each of the keys, values, and parameters contained in the dictionary.
213- // This allows the caller to extract information out of the dictionary.
211+ // Given an HTTP SFV string that represents a dictionary, it will call the
212+ // given function using each of the keys, values, and parameters contained in
213+ // the dictionary. This allows the caller to extract information out of the
214+ // dictionary.
214215//
215216// This function will return once it encounters the end of the string, or
216217// something that is not a dictionary. If it cannot consume the entire given
@@ -286,12 +287,11 @@ func consumeParameter(s string, f func(key, val string)) (consumed, rest string,
286287 return s [:len (s )- len (rest )], rest , true
287288}
288289
289- // ParseParameter is used to parse a string that represents a parameter in an
290- // HTTP Structured Field Values.
290+ // ParseParameter parses a parameter from a given HTTP Structured Field Values.
291291//
292- // Given a string that represents a parameter, it will call the given function
293- // using each of the keys and values contained in the parameter. This allows
294- // the caller to extract information out of the parameter.
292+ // Given an HTTP SFV string that represents a parameter, it will call the given
293+ // function using each of the keys and values contained in the parameter. This
294+ // allows the caller to extract information out of the parameter.
295295//
296296// This function will return once it encounters the end of the string, or
297297// something that is not a parameter. If it cannot consume the entire given
@@ -366,6 +366,41 @@ func consumeIntegerOrDecimal(s string) (consumed, rest string, ok bool) {
366366 return s [:i ], s [i :], true
367367}
368368
369+ // ParseInteger parses an integer from a given HTTP Structured Field Values.
370+ //
371+ // The entire HTTP SFV string must consist of a valid integer. It returns the
372+ // parsed integer and an ok boolean value, indicating success or not.
373+ //
374+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
375+ func ParseInteger (s string ) (parsed int64 , ok bool ) {
376+ if _ , rest , ok := consumeIntegerOrDecimal (s ); ! ok || rest != "" {
377+ return 0 , false
378+ }
379+ if n , err := strconv .ParseInt (s , 10 , 64 ); err == nil {
380+ return n , true
381+ }
382+ return 0 , false
383+ }
384+
385+ // ParseDecimal parses a decimal from a given HTTP Structured Field Values.
386+ //
387+ // The entire HTTP SFV string must consist of a valid decimal. It returns the
388+ // parsed decimal and an ok boolean value, indicating success or not.
389+ //
390+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
391+ func ParseDecimal (s string ) (parsed float64 , ok bool ) {
392+ if _ , rest , ok := consumeIntegerOrDecimal (s ); ! ok || rest != "" {
393+ return 0 , false
394+ }
395+ if ! strings .Contains (s , "." ) {
396+ return 0 , false
397+ }
398+ if n , err := strconv .ParseFloat (s , 64 ); err == nil {
399+ return n , true
400+ }
401+ return 0 , false
402+ }
403+
369404// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
370405func consumeString (s string ) (consumed , rest string , ok bool ) {
371406 if len (s ) == 0 || s [0 ] != '"' {
@@ -392,6 +427,19 @@ func consumeString(s string) (consumed, rest string, ok bool) {
392427 return "" , s , false
393428}
394429
430+ // ParseString parses a Go string from a given HTTP Structured Field Values.
431+ //
432+ // The entire HTTP SFV string must consist of a valid string. It returns the
433+ // parsed string and an ok boolean value, indicating success or not.
434+ //
435+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
436+ func ParseString (s string ) (parsed string , ok bool ) {
437+ if _ , rest , ok := consumeString (s ); ! ok || rest != "" {
438+ return "" , false
439+ }
440+ return s [1 : len (s )- 1 ], true
441+ }
442+
395443// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
396444func consumeToken (s string ) (consumed , rest string , ok bool ) {
397445 if len (s ) == 0 || (! isAlpha (s [0 ]) && s [0 ] != '*' ) {
@@ -407,6 +455,19 @@ func consumeToken(s string) (consumed, rest string, ok bool) {
407455 return s [:i ], s [i :], true
408456}
409457
458+ // ParseToken parses a token from a given HTTP Structured Field Values.
459+ //
460+ // The entire HTTP SFV string must consist of a valid token. It returns the
461+ // parsed token and an ok boolean value, indicating success or not.
462+ //
463+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
464+ func ParseToken (s string ) (parsed string , ok bool ) {
465+ if _ , rest , ok := consumeToken (s ); ! ok || rest != "" {
466+ return "" , false
467+ }
468+ return s , true
469+ }
470+
410471// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
411472func consumeByteSequence (s string ) (consumed , rest string , ok bool ) {
412473 if len (s ) == 0 || s [0 ] != ':' {
@@ -423,6 +484,20 @@ func consumeByteSequence(s string) (consumed, rest string, ok bool) {
423484 return "" , s , false
424485}
425486
487+ // ParseByteSequence parses a byte sequence from a given HTTP Structured Field
488+ // Values.
489+ //
490+ // The entire HTTP SFV string must consist of a valid byte sequence. It returns
491+ // the parsed byte sequence and an ok boolean value, indicating success or not.
492+ //
493+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
494+ func ParseByteSequence (s string ) (parsed []byte , ok bool ) {
495+ if _ , rest , ok := consumeByteSequence (s ); ! ok || rest != "" {
496+ return nil , false
497+ }
498+ return []byte (s [1 : len (s )- 1 ]), true
499+ }
500+
426501// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
427502func consumeBoolean (s string ) (consumed , rest string , ok bool ) {
428503 if len (s ) >= 2 && (s [:2 ] == "?0" || s [:2 ] == "?1" ) {
@@ -431,6 +506,19 @@ func consumeBoolean(s string) (consumed, rest string, ok bool) {
431506 return "" , s , false
432507}
433508
509+ // ParseBoolean parses a boolean from a given HTTP Structured Field Values.
510+ //
511+ // The entire HTTP SFV string must consist of a valid boolean. It returns the
512+ // parsed boolean and an ok boolean value, indicating success or not.
513+ //
514+ // https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
515+ func ParseBoolean (s string ) (parsed bool , ok bool ) {
516+ if _ , rest , ok := consumeBoolean (s ); ! ok || rest != "" {
517+ return false , false
518+ }
519+ return s == "?1" , true
520+ }
521+
434522// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
435523func consumeDate (s string ) (consumed , rest string , ok bool ) {
436524 if len (s ) == 0 || s [0 ] != '@' {
0 commit comments