From 90e8c8f763ea244e66604415a152fd6594ec4ea1 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 25 Oct 2023 17:57:46 +0200 Subject: [PATCH] Make HeaderValue public Signed-off-by: Ryan Levick --- sdk/rust/src/http.rs | 131 ++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 52 deletions(-) diff --git a/sdk/rust/src/http.rs b/sdk/rust/src/http.rs index 28907786da..daf5174f18 100644 --- a/sdk/rust/src/http.rs +++ b/sdk/rust/src/http.rs @@ -28,17 +28,75 @@ pub struct Request { body: Vec, } -enum HeaderValue { +/// A header value. +/// +/// Since header values do not have to be valid utf8, this allows for +/// both utf8 strings and bags of bytes. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct HeaderValue { + inner: HeaderValueRep, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +enum HeaderValueRep { + /// Header value encoded as a utf8 string String(String), + /// Header value as a bag of bytes Bytes(Vec), } impl HeaderValue { + /// Construct a `HeaderValue` from a string + pub fn string(str: String) -> HeaderValue { + HeaderValue { + inner: HeaderValueRep::String(str), + } + } + + /// Construct a `HeaderValue` from a bag of bytes + pub fn bytes(bytes: Vec) -> HeaderValue { + HeaderValue { + inner: HeaderValueRep::Bytes(bytes), + } + } + + /// Get the `HeaderValue` as a utf8 encoded string + /// + /// Returns `None` if the value is a non utf8 encoded header value + pub fn as_str(&self) -> Option<&str> { + match &self.inner { + HeaderValueRep::String(s) => Some(s), + HeaderValueRep::Bytes(_) => None, + } + } + + /// Get the `HeaderValue` as bytes + pub fn as_bytes(&self) -> &[u8] { + self.as_ref() + } + + /// Turn the `HeaderValue` into a String (in a lossy way if the `HeaderValue` is a bag of bytes) + pub fn into_utf8_lossy(self) -> String { + match self.inner { + HeaderValueRep::String(s) => s, + HeaderValueRep::Bytes(b) => String::from_utf8_lossy(&b).into_owned(), + } + } + /// Turn the `HeaderValue` into bytes - fn into_bytes(self) -> Vec { - match self { - HeaderValue::String(s) => s.into_bytes(), - HeaderValue::Bytes(b) => b, + pub fn into_bytes(self) -> Vec { + match self.inner { + HeaderValueRep::String(s) => s.into_bytes(), + HeaderValueRep::Bytes(b) => b, + } + } +} + +impl AsRef<[u8]> for HeaderValue { + fn as_ref(&self) -> &[u8] { + match &self.inner { + HeaderValueRep::String(s) => s.as_bytes(), + HeaderValueRep::Bytes(b) => b, } } } @@ -78,33 +136,15 @@ impl Request { } /// The request headers - /// - /// This only returns headers that are utf8 encoded - pub fn headers(&self) -> impl Iterator { - self.headers.iter().filter_map(|(k, v)| match v { - HeaderValue::String(v) => Some((k.as_str(), v.as_str())), - HeaderValue::Bytes(_) => None, - }) + pub fn headers(&self) -> impl Iterator { + self.headers.iter().map(|(k, v)| (k.as_str(), v)) } /// Return a header value /// - /// Will return `None` if the header does not exist or if it is not utf8 - pub fn header(&self, name: &str) -> Option<&str> { - self.headers - .get(&name.to_lowercase()) - .and_then(|v| match v { - HeaderValue::String(s) => Some(s.as_str()), - HeaderValue::Bytes(_) => None, - }) - } - - /// The request headers as bytes - pub fn headers_raw(&self) -> impl Iterator { - self.headers.iter().map(|(k, v)| match v { - HeaderValue::String(v) => (k.as_str(), v.as_bytes()), - HeaderValue::Bytes(v) => (k.as_str(), v.as_slice()), - }) + /// Will return `None` if the header does not exist. + pub fn header(&self, name: &str) -> Option<&HeaderValue> { + self.headers.get(&name.to_lowercase()) } /// The request body @@ -172,7 +212,7 @@ impl RequestBuilder { pub fn header(&mut self, key: impl Into, value: impl Into) -> &mut Self { self.request .headers - .insert(key.into().to_lowercase(), HeaderValue::String(value.into())); + .insert(key.into().to_lowercase(), HeaderValue::string(value.into())); self } @@ -216,31 +256,18 @@ impl Response { &self.status } - /// The response headers - /// - /// This only returns headers that are utf8 encoded - pub fn headers(&self) -> impl Iterator { - self.headers.iter().filter_map(|(k, v)| match v { - HeaderValue::String(v) => Some((k.as_str(), v.as_str())), - HeaderValue::Bytes(_) => None, - }) + /// The request headers + pub fn headers(&self) -> impl Iterator { + self.headers.iter().map(|(k, v)| (k.as_str(), v)) } /// Return a header value /// /// Will return `None` if the header does not exist or if it is not utf8 pub fn header(&self, name: &str) -> Option<&str> { - self.headers.get(name).and_then(|v| match v { - HeaderValue::String(s) => Some(s.as_str()), - HeaderValue::Bytes(_) => None, - }) - } - - /// The request headers as bytes - pub fn headers_raw(&self) -> impl Iterator { - self.headers.iter().map(|(k, v)| match v { - HeaderValue::String(v) => (k.as_str(), v.as_bytes()), - HeaderValue::Bytes(v) => (k.as_str(), v.as_slice()), + self.headers.get(name).and_then(|v| match &v.inner { + HeaderValueRep::String(s) => Some(s.as_str()), + HeaderValueRep::Bytes(_) => None, }) } @@ -293,7 +320,7 @@ impl ResponseBuilder { pub fn header(&mut self, key: impl Into, value: impl Into) -> &mut Self { self.response .headers - .insert(key.into().to_lowercase(), HeaderValue::String(value.into())); + .insert(key.into().to_lowercase(), HeaderValue::string(value.into())); self } @@ -315,9 +342,9 @@ fn into_header_rep(headers: impl conversions::IntoHeaders) -> HashMap