13 releases (8 breaking)

0.9.4 Jan 11, 2024
0.9.3 Dec 23, 2022
0.9.2 Jan 31, 2022
0.9.1 Feb 14, 2021
0.3.0 Jul 24, 2020

#91 in Network programming

Download history 13828/week @ 2024-07-25 13229/week @ 2024-08-01 12039/week @ 2024-08-08 11408/week @ 2024-08-15 13264/week @ 2024-08-22 11524/week @ 2024-08-29 12620/week @ 2024-09-05 13387/week @ 2024-09-12 14979/week @ 2024-09-19 20219/week @ 2024-09-26 17638/week @ 2024-10-03 18600/week @ 2024-10-10 18657/week @ 2024-10-17 21436/week @ 2024-10-24 25626/week @ 2024-10-31 25000/week @ 2024-11-07

94,066 downloads per month
Used in 29 crates (6 directly)

MIT/Apache

100KB
2K SLoC

License Build Status Version Docs

Structured Field Values for HTTP

sfv crate is an implementation of Structured Field Values for HTTP as specified in RFC 8941 for parsing and serializing HTTP field values (also known as "structured headers" or "structured trailers").

It also exposes a set of types that might be useful for defining new structured fields.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

lib.rs:

sfv crate is an implementation of Structured Field Values for HTTP as specified in RFC 8941 for parsing and serializing HTTP field values. It also exposes a set of types that might be useful for defining new structured fields.

Data Structures

There are three types of structured fields:

  • Item - can be an Integer, Decimal, String, Token, Byte Sequence, or Boolean. It can have associated Parameters.
  • List - array of zero or more members, each of which can be an Item or an InnerList, both of which can be Parameterized.
  • Dictionary - ordered map of name-value pairs, where the names are short textual strings and the values are Items or arrays of Items (represented with InnerList), both of which can be Parameterized. There can be zero or more members, and their names are unique in the scope of the Dictionary they occur within.

There's also a few primitive types used to construct structured field values:

  • BareItem used as Item's value or as a parameter value in Parameters.
  • Parameters are an ordered map of key-value pairs that are associated with an Item or InnerList. The keys are unique within the scope the Parameters they occur within, and the values are BareItem.
  • InnerList is an array of zero or more Items. Can have Parameters.
  • ListEntry represents either Item or InnerList as a member of List or as member-value in Dictionary.

Examples

Parsing

use sfv::Parser;

// Parsing structured field value of Item type.
let item_header_input = "12.445;foo=bar";
let item = Parser::parse_item(item_header_input.as_bytes());
assert!(item.is_ok());
println!("{:#?}", item);

// Parsing structured field value of List type.
let list_header_input = "1;a=tok, (\"foo\" \"bar\");baz, ()";
let list = Parser::parse_list(list_header_input.as_bytes());
assert!(list.is_ok());
println!("{:#?}", list);

// Parsing structured field value of Dictionary type.
let dict_header_input = "a=?0, b, c; foo=bar, rating=1.5, fruits=(apple pear)";
let dict = Parser::parse_dictionary(dict_header_input.as_bytes());
assert!(dict.is_ok());
println!("{:#?}", dict);

Getting Parsed Value Members

use sfv::*;

let dict_header = "u=2, n=(* foo 2)";
let dict = Parser::parse_dictionary(dict_header.as_bytes()).unwrap();

// Case 1 - handling value if it's an Item of Integer type
let u_val = match dict.get("u") {
Some(ListEntry::Item(item)) => item.bare_item.as_int(),
_ => None,
};

if let Some(u_val) = u_val {
println!("{}", u_val);
}

// Case 2 - matching on all possible types
match dict.get("u") {
Some(ListEntry::Item(item)) => match &item.bare_item {
BareItem::Token(val) => {
// do something if it's a Token
println!("{}", val);
}
BareItem::Integer(val) => {
// do something if it's an Integer
println!("{}", val);
}
BareItem::Boolean(val) => {
// do something if it's a Boolean
println!("{}", val);
}
BareItem::Decimal(val) => {
// do something if it's a Decimal
println!("{}", val);
}
BareItem::String(val) => {
// do something if it's a String
println!("{}", val);
}
BareItem::ByteSeq(val) => {
// do something if it's a ByteSeq
println!("{:?}", val);
}
},
Some(ListEntry::InnerList(inner_list)) => {
// do something if it's an InnerList
println!("{:?}", inner_list.items);
}
None => panic!("key not found"),
}

Structured Field Value Construction and Serialization

Creates Item with empty parameters:

use sfv::{Item, BareItem, SerializeValue};

let str_item = Item::new(BareItem::String(String::from("foo")));
assert_eq!(str_item.serialize_value().unwrap(), "\"foo\"");

Creates Item field value with parameters:

use sfv::{Item, BareItem, SerializeValue, Parameters, Decimal, FromPrimitive};

let mut params = Parameters::new();
let decimal = Decimal::from_f64(13.45655).unwrap();
params.insert("key".into(), BareItem::Decimal(decimal));
let int_item = Item::with_params(BareItem::Integer(99), params);
assert_eq!(int_item.serialize_value().unwrap(), "99;key=13.457");

Creates List field value with Item and parametrized InnerList as members:

use sfv::{Item, BareItem, InnerList, List, SerializeValue, Parameters};

let tok_item = BareItem::Token("tok".into());

// Creates Item.
let str_item = Item::new(BareItem::String(String::from("foo")));

// Creates InnerList members.
let mut int_item_params = Parameters::new();
int_item_params.insert("key".into(), BareItem::Boolean(false));
let int_item = Item::with_params(BareItem::Integer(99), int_item_params);

// Creates InnerList.
let mut inner_list_params = Parameters::new();
inner_list_params.insert("bar".into(), BareItem::Boolean(true));
let inner_list = InnerList::with_params(vec![int_item, str_item], inner_list_params);


let list: List = vec![Item::new(tok_item).into(), inner_list.into()];
assert_eq!(
list.serialize_value().unwrap(),
"tok, (99;key=?0 \"foo\");bar"
);

Creates Dictionary field value:

use sfv::{Parser, Item, BareItem, SerializeValue, ParseValue, Dictionary};

let member_value1 = Item::new(BareItem::String(String::from("apple")));
let member_value2 = Item::new(BareItem::Boolean(true));
let member_value3 = Item::new(BareItem::Boolean(false));

let mut dict = Dictionary::new();
dict.insert("key1".into(), member_value1.into());
dict.insert("key2".into(), member_value2.into());
dict.insert("key3".into(), member_value3.into());

assert_eq!(
dict.serialize_value().unwrap(),
"key1=\"apple\", key2, key3=?0"
);

Dependencies

~1.5MB
~27K SLoC