From d46f745d262001ce7a1721ca1ea0b05f88a81be8 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Fri, 12 May 2023 01:05:25 -0400 Subject: [PATCH] Add static representation for custom header names --- async-nats/src/header.rs | 66 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/async-nats/src/header.rs b/async-nats/src/header.rs index 52cb3e6c7..1ddf2e071 100644 --- a/async-nats/src/header.rs +++ b/async-nats/src/header.rs @@ -13,6 +13,7 @@ //! NATS [Message][crate::Message] headers, modeled loosely after the [http::header] crate. +use bytes::Bytes; use std::{collections::HashMap, fmt, slice, str::FromStr}; /// A struct for handling NATS headers. @@ -265,7 +266,7 @@ pub trait IntoHeaderName { impl IntoHeaderName for &str { fn into_header_name(self) -> HeaderName { HeaderName { - inner: HeaderRepr::Custom(self.to_string()), + inner: HeaderRepr::Custom(self.into()), } } } @@ -385,10 +386,47 @@ standard_headers! { (NatsExpectedStream, NATS_EXPECTED_STREAM, b"Nats-Expected-Stream"); } +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +struct CustomHeader { + bytes: Bytes, +} + +impl CustomHeader { + #[inline] + pub(crate) const fn from_static(value: &'static str) -> CustomHeader { + CustomHeader { + bytes: Bytes::from_static(value.as_bytes()), + } + } + + #[inline] + pub(crate) fn as_str(&self) -> &str { + unsafe { std::str::from_utf8_unchecked(self.bytes.as_ref()) } + } +} + +impl From for CustomHeader { + #[inline] + fn from(value: String) -> CustomHeader { + CustomHeader { + bytes: Bytes::from(value), + } + } +} + +impl<'a> From<&'a str> for CustomHeader { + #[inline] + fn from(value: &'a str) -> CustomHeader { + CustomHeader { + bytes: Bytes::copy_from_slice(value.as_bytes()), + } + } +} + #[derive(Debug, Hash, PartialEq, Eq, Clone)] enum HeaderRepr { Standard(StandardHeader), - Custom(String), + Custom(CustomHeader), } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -397,6 +435,20 @@ pub struct HeaderName { } impl HeaderName { + /// Converts a static string to a NATS header name. + #[inline] + pub const fn from_static(value: &'static str) -> HeaderName { + if let Some(standard) = StandardHeader::from_bytes(value.as_bytes()) { + return HeaderName { + inner: HeaderRepr::Standard(standard), + }; + } + + HeaderName { + inner: HeaderRepr::Custom(CustomHeader::from_static(value)), + } + } + /// Returns a `str` representation of the header. #[inline] fn as_str(&self) -> &str { @@ -420,7 +472,7 @@ impl FromStr for HeaderName { inner: HeaderRepr::Standard(v), }), None => Ok(HeaderName { - inner: HeaderRepr::Custom(s.to_string()), + inner: HeaderRepr::Custom(CustomHeader::from(s)), }), } } @@ -572,4 +624,12 @@ mod tests { parsed_header.ok() ); } + + #[test] + fn from_static_eq() { + let a = HeaderName::from_static("NATS-Stream"); + let b = HeaderName::from_static("NATS-Stream"); + + assert_eq!(a, b); + } }