Ниже показан английский оригинал.
HeaderField
Each field of a headers struct without a role is a header: strings and bytes as they are, numbers in decimal, times in RFC 3339, None left out.
The header's name is the field's column name, as sqlx's rename and rename_all set it.
Its value comes from HeaderField:
| Field type | Header value |
|---|---|
String, Vec<u8> | its bytes |
integers, bool | its decimal text, true or false |
chrono::DateTime<Utc> (feature chrono), time::OffsetDateTime (feature time) | its RFC 3339 text |
Option<T> | the value of T; None leaves the header out |
A service implements HeaderField for a type of its own.
A field of a type without it does not compile, and the error offers the impl or a role.
The fields that play a role are not headers: the handler reads them on the message.
The header map is built on the first read of the delivery's headers, by middleware or an extractor such as Headers<T>, and a delivery whose headers nobody reads never builds it.
A message published into the table through a repository carries only the headers the headers struct names, and a publish with another header fails with SqlxBrokerError::Header, so no header is lost on the way into the table.
By hand, HeaderFields builds the map with put_header, which writes each value through its HeaderField.
Example
A shipment's headers are a carrier of the service's own type, an order number, a flag, a time and an optional trace; the handler writes them to an audit table as the header map holds them.
- Macros
- Manual
use ruststream_sqlx::HeaderField;
#[derive(Debug, Clone, InboxHeaders, sqlx::FromRow)]
#[inbox(table = "shipment_jobs")]
struct ShipmentHeaders {
#[field(id, generated)]
job_id: i64,
#[field(locked_until)]
locked_until: Option<DateTime<Utc>>,
// Each field without a role is a header named after its column.
carrier: Carrier,
order_id: i64,
express: bool,
placed_at: DateTime<Utc>,
trace: Option<String>,
}
/// A carrier, kept by its code in the table and named in capitals in the header.
#[derive(Debug, Clone, sqlx::Type)]
#[sqlx(transparent)]
struct Carrier(String);
impl HeaderField for Carrier {
fn header(&self) -> Option<Vec<u8>> {
Some(self.0.to_uppercase().into_bytes())
}
}
use ruststream::HeaderMap;
use ruststream_sqlx::{HeaderField, HeaderFields, put_header};
#[derive(Debug, Clone, sqlx::FromRow, JsonSchema)]
#[schemars(crate = "ruststream::schemars")]
struct Shipment {
job_id: i64,
carrier: Carrier,
order_id: i64,
express: bool,
#[schemars(with = "String")]
placed_at: DateTime<Utc>,
trace: Option<String>,
}
impl HeaderFields for Shipment {
const NAMES: &'static [&'static str] =
&["carrier", "order_id", "express", "placed_at", "trace"];
fn header_map(&self) -> HeaderMap {
let mut headers = HeaderMap::with_capacity(Self::NAMES.len());
put_header(&mut headers, "carrier", &self.carrier);
put_header(&mut headers, "order_id", &self.order_id);
put_header(&mut headers, "express", &self.express);
put_header(&mut headers, "placed_at", &self.placed_at);
put_header(&mut headers, "trace", &self.trace);
headers
}
}
/// A carrier, kept by its code in the table and named in capitals in the header.
#[derive(Debug, Clone, sqlx::Type, JsonSchema)]
#[schemars(crate = "ruststream::schemars")]
#[sqlx(transparent)]
struct Carrier(String);
impl HeaderField for Carrier {
fn header(&self) -> Option<Vec<u8>> {
Some(self.0.to_uppercase().into_bytes())
}
}