moon-sfv

RFC 9651 Structured Field Values parser and serializer for MoonBit

http
structured-fields
rfc9651
parser
Download zip
Version
0.1.0
License
MIT
Last updated
12 hours ago
Downloads
3

#moon-sfv

CI

A strict RFC 9651 Structured Field Values parser and serializer for MoonBit.

#Why

RFC 9651 (Structured Field Values for HTTP) specifies a set of data types and parsing / serializing algorithms for HTTP header and trailer fields. It provides a shared type system and unambiguous canonical text representation for modern HTTP specifications (such as HTTP Caching, Digest Fields, Priority, Client Hints, and RateLimit headers).

moon-sfv provides a small, fast, zero-dependency implementation of RFC 9651 in pure MoonBit. It allows MoonBit HTTP clients, servers, reverse proxies, and WebAssembly services to safely parse and generate compliant HTTP field values.

#Features

FeatureParseSerializeNotes
ItemTop-level parameterized values
ListTop-level comma-separated arrays
DictionaryTop-level comma-separated key-value maps
Inner ListParenthesized item sequences (item1 item2)
ParametersOrdered semicolon key-value pairs ;k=v
Integer64-bit integers in range ±999,999,999,999,999
Decimal12 integer digits, 1–3 fractional digits, canonical round-half-even
StringASCII strings with strict \" and \\ escaping
TokenValid RFC identifiers and MIME/symbol tokens
Byte SequenceBase64-encoded binary content :...:
Boolean?1 (true), ?0 (false), and parameter shorthand
DateUnix timestamps with @... prefix
Display StringUTF-8 percent-encoded display strings %"..."

#Standards Compliance

moon-sfv is thoroughly validated against the official HTTPWG structured-field-tests test suite:

  • HTTPWG Parsing Test Vectors: 1,591 / 1,591 passed
    • Mandatory vectors: 1,585 / 1,585 passed (100%)
    • Optional (can_fail) vectors: 6 / 6 supported and passed (100% strict verification)
    • Verification method: Parsed output is strictly compared for semantic equality (assert_eq) against the official expected data structures and verified for canonical serialization matching with zero silent-pass fallbacks.
  • HTTPWG Serialization Test Vectors: 544 / 544 passed (100%)
  • Internal Unit Tests: 46 / 46 passed (100%)
  • Total Test Suite: 2,181 / 2,181 passed (100%)
  • Skipped / Unsupported: 0

#Installation

Add moon-sfv to your MoonBit project from Mooncakes:

moon add Sam-Hui-dot/moon-sfv

Or reference as a dependency in your moon.pkg.json / moon.pkg:

import {
"Sam-Hui-dot/moon-sfv" @sfv,
}

#Usage

#1. Item

let input = "example;secure;level=2"

match @sfv.parse_item(input) {
Ok(item) => {
// item.value is BareItem::SfToken("example")
// item.parameters has { "secure": true, "level": 2 }
match @sfv.serialize_item(item) {
Ok(canonical) => println(canonical) // "example;secure;level=2"
Err(_) => println("Serialization failed")
}
}
Err(_) => println("Parsing failed")
}

#2. List

let input = "text/html;q=1.0, (\"gzip\" \"br\");level=9"

match @sfv.parse_list(input) {
Ok(list) => {
// list contains Item and InnerList members
match @sfv.serialize_list(list) {
Ok(canonical) => println(canonical)
Err(_) => println("Serialization failed")
}
}
Err(_) => println("Parsing failed")
}

#3. Dictionary

let input = "en=\"Applepie\", da=:w4ZibGV0w6ZydGUK:, active, flags=(x y)"

match @sfv.parse_dictionary(input) {
Ok(dict) => {
// dict contains ordered key-value pairs with boolean shorthand support
match @sfv.serialize_dictionary(dict) {
Ok(canonical) => println(canonical)
Err(_) => println("Serialization failed")
}
}
Err(_) => println("Parsing failed")
}

#API Reference

#Core Types

pub(all) enum BareItem {
SfInteger(Int64)
SfDecimal(Double)
SfString(String)
SfToken(String)
SfByteSequence(Bytes)
SfBoolean(Bool)
SfDate(Int64)
SfDisplayString(String)
} derive(Eq, Debug)

pub(all) struct Parameter {
key : String
value : BareItem
} derive(Eq, Debug)

pub(all) struct Item {
value : BareItem
parameters : Array[Parameter]
} derive(Eq, Debug)

pub(all) struct InnerList {
items : Array[Item]
parameters : Array[Parameter]
} derive(Eq, Debug)

pub(all) enum ListMember {
Item(Item)
InnerList(InnerList)
} derive(Eq, Debug)

pub(all) struct DictionaryMember {
key : String
value : ListMember
} derive(Eq, Debug)

#Parsing and Serialization Functions

pub fn parse_item(String) -> Result[Item, ParseError]
pub fn serialize_item(Item) -> Result[String, SerializeError]

pub fn parse_list(String) -> Result[Array[ListMember], ParseError]
pub fn serialize_list(Array[ListMember]) -> Result[String, SerializeError]

pub fn parse_dictionary(String) -> Result[Array[DictionaryMember], ParseError]
pub fn serialize_dictionary(Array[DictionaryMember]) -> Result[String, SerializeError]

pub fn base64_encode(Bytes) -> String
pub fn base64_decode(String) -> Bytes?

pub fn string_to_utf8_bytes(String) -> Array[Byte]
pub fn utf8_bytes_to_string(Array[Byte]) -> String?

#Testing

Run the full verification suite with the MoonBit toolchain:

moon check --deny-warn moon test moon fmt --check

Run the interactive examples:

moon run examples/main

#Status

moon-sfv is at version v0.1.0. All RFC 9651 bare item types, container types, and canonical serialization rules are implemented and validated against the HTTP Working Group test vectors.

#License and Attribution

BareItem

pub(all) enum BareItem {
SfInteger(Int64)
SfDecimal(Double)
SfString(String)
SfToken(String)
SfByteSequence(Bytes)
SfBoolean(Bool)
SfDate(Int64)
SfDisplayString(String)
} derive(Eq,
Debug
)

A bare item value defined by RFC 9651.

DictionaryMember

pub(all) struct DictionaryMember {
key : String
value : ListMember
} derive(Eq,
Debug
)

A key-value pair in an RFC 9651 Dictionary.

InnerList

pub(all) struct InnerList {
items : Array[Item]
parameters : Array[Parameter]
} derive(Eq,
Debug
)

An RFC 9651 Inner List containing zero or more Items with ordered parameters.

Item

pub(all) struct Item {
value : BareItem
parameters : Array[Parameter]
} derive(Eq,
Debug
)

A parameterized RFC 9651 Item.

ListMember

pub(all) enum ListMember {
Item(Item)
InnerList(InnerList)
} derive(Eq,
Debug
)

A member of a top-level List or Dictionary value: either an Item or an Inner List.

Parameter

pub(all) struct Parameter {
key : String
value : BareItem
} derive(Eq,
Debug
)

An ordered parameter attached to an Item or Inner List.

ParseError

pub(all) enum ParseError {
EmptyInput
InvalidBoolean(Int)
InvalidInteger(String)
IntegerOutOfRange(String)
InvalidDecimal(String)
DecimalOutOfRange(String)
UnterminatedString(Int)
InvalidStringEscape(Int)
InvalidStringCharacter(Int)
InvalidToken(Int)
InvalidByteSequence(Int)
InvalidDate(String)
InvalidDisplayString(Int)
InvalidParameterKey(Int)
InvalidDictionaryKey(Int)
InvalidInnerList(Int)
InvalidList(Int)
InvalidDictionary(Int)
UnsupportedBareItem(Int)
TrailingCharacters(Int)
} derive(Eq,
Debug
)

Errors returned by the strict Structured Fields parser.

SerializeError

pub(all) enum SerializeError {
IntegerOutOfRange(Int64)
DecimalOutOfRange(Double)
InvalidDecimal(Double)
DateOutOfRange(Int64)
InvalidString(String)
InvalidToken(String)
InvalidByteSequence
InvalidDisplayString(String)
InvalidParameterKey(String)
InvalidDictionaryKey(String)
UnsupportedBareItem
} derive(Eq,
Debug
)

Errors returned when a value cannot be serialized as RFC 9651.

base64_decode

fn base64_decode(input : String) -> Bytes?

base64_encode

fn base64_encode(bytes : Bytes) -> String

parse_dictionary

fn parse_dictionary(input : String) -> Result[Array[DictionaryMember], ParseError]

Parses an RFC 9651 Dictionary from an HTTP field string.

parse_item

fn parse_item(input : String) -> Result[Item, ParseError]

Parses a single RFC 9651 Item from an HTTP field string.

parse_list

fn parse_list(input : String) -> Result[Array[ListMember], ParseError]

Parses an RFC 9651 List from an HTTP field string.

serialize_dictionary

fn serialize_dictionary(dict : Array[DictionaryMember]) -> Result[String, SerializeError]

Serialize an RFC 9651 Dictionary into its canonical textual representation.

serialize_item

fn serialize_item(item : Item) -> Result[String, SerializeError]

Serialize an RFC 9651 Item into its canonical textual representation.

serialize_list

fn serialize_list(list : Array[ListMember]) -> Result[String, SerializeError]

Serialize an RFC 9651 List into its canonical textual representation.

string_to_utf8_bytes

fn string_to_utf8_bytes(str : String) -> Array[Byte]

utf8_bytes_to_string

fn utf8_bytes_to_string(bytes : Array[Byte]) -> String?