pub struct RefusalDigest<const N: usize = 0> { /* private fields */ }target_has_atomic=32 only.Expand description
How many times each failure code was returned, for one route.
One counter per code form-net returns, one per inspector code named in
with_codes, and one for every other inspector
refusal. record is a match and one relaxed atomic add:
no allocation, no lock, safe to call from every thread at once. Keep one
digest per route (a static works) and record every error the route
returns, whichever call returned it; a detection layer reads the counts
and compares them over time.
Counters are 64 bits where the target has 64-bit atomics and 32 bits
elsewhere; they wrap, so compare readings with wrapping_sub.
§Examples
use form_net::RefusalDigest;
static LOGIN: RefusalDigest<1> = RefusalDigest::with_codes(["customs.unexpected_field"]);
for body in [&b"email=a%zz"[..], b"a=1", b"b=%"] {
if let Err(e) = form_net::parse_urlencoded(body) {
LOGIN.record(&e);
}
}
assert_eq!(LOGIN.count("encoding.invalid_percent"), 2);
assert_eq!(LOGIN.total(), 2);Implementations§
Source§impl RefusalDigest<0>
impl RefusalDigest<0>
Sourcepub const fn new() -> RefusalDigest<0>
pub const fn new() -> RefusalDigest<0>
Source§impl<const N: usize> RefusalDigest<N>
impl<const N: usize> RefusalDigest<N>
Sourcepub const fn with_codes(codes: [&'static str; N]) -> RefusalDigest<N>
pub const fn with_codes(codes: [&'static str; N]) -> RefusalDigest<N>
A digest that also counts these inspector codes one by one (the codes the route’s inspectors can return, such as customs-sec’s).
§Examples
use form_net::{FormError, Location, Refusal, RefusalDigest};
let digest = RefusalDigest::with_codes(["customs.too_many"]);
digest.record(&FormError::Refused { refusal: Refusal::new(400, "customs.too_many"), at: Location::NONE });
assert_eq!(digest.count("customs.too_many"), 1);
assert_eq!(digest.unnamed(), 0);Sourcepub fn record(&self, error: &FormError)
pub fn record(&self, error: &FormError)
Counts error under its code.
Cost: one match and one relaxed atomic add for form-net’s codes; an inspector refusal compares its code with the named codes first.
§Examples
let digest = form_net::RefusalDigest::new();
if let Err(e) = form_net::parse_urlencoded(b"a=%zz") {
digest.record(&e);
}
assert_eq!(digest.total(), 1);Sourcepub fn count(&self, code: &str) -> u64
pub fn count(&self, code: &str) -> u64
How many times code was recorded (form-net’s codes and the named
inspector codes; a code named twice, or named and also form-net’s,
sums its counters).
§Examples
let digest = form_net::RefusalDigest::new();
digest.record(&form_net::parse_urlencoded(b"a=%").unwrap_err());
assert_eq!(digest.count("encoding.invalid_percent"), 1);
assert_eq!(digest.count("limit.body"), 0);Sourcepub fn unnamed(&self) -> u64
pub fn unnamed(&self) -> u64
How many inspector refusals had a code that was not named.
§Examples
use form_net::{FormError, Location, Refusal, RefusalDigest};
let digest = RefusalDigest::new();
digest.record(&FormError::Refused { refusal: Refusal::new(403, "app.blocked"), at: Location::NONE });
assert_eq!(digest.unnamed(), 1);Sourcepub fn total(&self) -> u64
pub fn total(&self) -> u64
Everything recorded.
§Examples
use form_net::{FormError, Location, Refusal, RefusalDigest};
let digest = RefusalDigest::new();
digest.record(&form_net::parse_urlencoded(b"a=%").unwrap_err());
digest.record(&FormError::Refused { refusal: Refusal::new(403, "app.blocked"), at: Location::NONE });
assert_eq!(digest.total(), 2);Sourcepub fn iter(&self) -> impl Iterator<Item = (&'static str, u64)> + '_
pub fn iter(&self) -> impl Iterator<Item = (&'static str, u64)> + '_
Every code with a count above zero, form-net’s first in a fixed
order, then the named codes; unnamed is apart.
§Examples
let digest = form_net::RefusalDigest::new();
digest.record(&form_net::parse_urlencoded(b"a=%").unwrap_err());
let counts: Vec<_> = digest.iter().collect();
assert_eq!(counts, [("encoding.invalid_percent", 1)]);