http_types/trace/server_timing/
mod.rsmod metric;
mod parse;
pub use metric::Metric;
use parse::parse_header;
use std::convert::AsMut;
use std::fmt::Write;
use std::iter::Iterator;
use std::option;
use std::slice;
use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, SERVER_TIMING};
#[derive(Debug)]
pub struct ServerTiming {
timings: Vec<Metric>,
}
impl ServerTiming {
pub fn new() -> Self {
Self { timings: vec![] }
}
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
let mut timings = vec![];
let headers = match headers.as_ref().get(SERVER_TIMING) {
Some(headers) => headers,
None => return Ok(None),
};
for value in headers {
parse_header(value.as_str(), &mut timings)?;
}
Ok(Some(Self { timings }))
}
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
headers.as_mut().insert(SERVER_TIMING, self.value());
}
pub fn name(&self) -> HeaderName {
SERVER_TIMING
}
pub fn value(&self) -> HeaderValue {
let mut output = String::new();
for (n, timing) in self.timings.iter().enumerate() {
let timing: HeaderValue = timing.clone().into();
match n {
0 => write!(output, "{}", timing).unwrap(),
_ => write!(output, ", {}", timing).unwrap(),
};
}
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
}
pub fn push(&mut self, entry: Metric) {
self.timings.push(entry);
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.timings.iter(),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut {
inner: self.timings.iter_mut(),
}
}
}
impl IntoIterator for ServerTiming {
type Item = Metric;
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
inner: self.timings.into_iter(),
}
}
}
impl<'a> IntoIterator for &'a ServerTiming {
type Item = &'a Metric;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut ServerTiming {
type Item = &'a mut Metric;
type IntoIter = IterMut<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[derive(Debug)]
pub struct IntoIter {
inner: std::vec::IntoIter<Metric>,
}
impl Iterator for IntoIter {
type Item = Metric;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Debug)]
pub struct Iter<'a> {
inner: slice::Iter<'a, Metric>,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a Metric;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Debug)]
pub struct IterMut<'a> {
inner: slice::IterMut<'a, Metric>,
}
impl<'a> Iterator for IterMut<'a> {
type Item = &'a mut Metric;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ToHeaderValues for ServerTiming {
type Iter = option::IntoIter<HeaderValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
Ok(self.value().to_header_values().unwrap())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::headers::Headers;
#[test]
fn smoke() -> crate::Result<()> {
let mut timings = ServerTiming::new();
timings.push(Metric::new("server".to_owned(), None, None)?);
let mut headers = Headers::new();
timings.apply(&mut headers);
let timings = ServerTiming::from_headers(headers)?.unwrap();
let entry = timings.iter().next().unwrap();
assert_eq!(entry.name(), "server");
Ok(())
}
#[test]
fn to_header_values() -> crate::Result<()> {
let mut timings = ServerTiming::new();
timings.push(Metric::new("server".to_owned(), None, None)?);
let mut headers = Headers::new();
timings.apply(&mut headers);
let timings = ServerTiming::from_headers(headers)?.unwrap();
let entry = timings.iter().next().unwrap();
assert_eq!(entry.name(), "server");
Ok(())
}
#[test]
fn bad_request_on_parse_error() {
let mut headers = Headers::new();
headers.insert(SERVER_TIMING, "server; <nori ate your param omnom>");
let err = ServerTiming::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);
}
}