Skip to main content

rama_net/uri/
component_input.rs

1//! Input trait for URI component setters.
2//!
3//! `IntoUriComponent` accepts borrowed (`&str`, `&[u8]`) and owned
4//! (`String`, `Vec<u8>`, `Bytes`, `BytesMut`) byte sources, and lets
5//! owned inputs move into the URI without an extra copy. Integer and
6//! boolean scalars are also accepted — they format to their decimal /
7//! `true`/`false` rendering, so e.g. a numeric id can be pushed as a
8//! path segment without a manual `.to_string()`.
9
10use crate::std::{borrow::Cow, string::String, vec::Vec};
11
12use rama_core::bytes::{Bytes, BytesMut};
13
14mod sealed {
15    use crate::std::borrow::Cow;
16
17    use rama_core::bytes::BytesMut;
18
19    pub trait Sealed {
20        /// The component bytes to read for encoding. Backed by the value
21        /// itself for byte-source types (`Cow::Borrowed`); scalar types
22        /// format on demand and return `Cow::Owned`.
23        fn as_uri_component_bytes(&self) -> Cow<'_, [u8]>;
24        fn into_uri_component_bytes_mut(self) -> BytesMut;
25        fn is_already_uri_component(&self) -> bool {
26            false
27        }
28    }
29}
30
31/// Sealed marker — types accepted by URI component setters.
32pub trait IntoUriComponent: sealed::Sealed {}
33
34impl sealed::Sealed for BytesMut {
35    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
36        Cow::Borrowed(self)
37    }
38    fn into_uri_component_bytes_mut(self) -> BytesMut {
39        self
40    }
41}
42impl IntoUriComponent for BytesMut {}
43
44impl sealed::Sealed for Bytes {
45    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
46        Cow::Borrowed(self)
47    }
48    fn into_uri_component_bytes_mut(self) -> BytesMut {
49        // `BytesMut::from(Bytes)` is zero-copy when the Bytes is the
50        // unique owner of its underlying buffer; otherwise it copies.
51        BytesMut::from(self)
52    }
53}
54impl IntoUriComponent for Bytes {}
55
56impl sealed::Sealed for String {
57    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
58        Cow::Borrowed(self.as_bytes())
59    }
60    fn into_uri_component_bytes_mut(self) -> BytesMut {
61        // String → Vec<u8> → Bytes → BytesMut is a zero-copy chain.
62        BytesMut::from(Bytes::from(self.into_bytes()))
63    }
64}
65impl IntoUriComponent for String {}
66
67impl sealed::Sealed for Vec<u8> {
68    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
69        Cow::Borrowed(self)
70    }
71    fn into_uri_component_bytes_mut(self) -> BytesMut {
72        BytesMut::from(Bytes::from(self))
73    }
74}
75impl IntoUriComponent for Vec<u8> {}
76
77impl sealed::Sealed for &str {
78    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
79        Cow::Borrowed(self.as_bytes())
80    }
81    fn into_uri_component_bytes_mut(self) -> BytesMut {
82        BytesMut::from(self.as_bytes())
83    }
84}
85impl IntoUriComponent for &str {}
86
87impl sealed::Sealed for &[u8] {
88    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
89        Cow::Borrowed(self)
90    }
91    fn into_uri_component_bytes_mut(self) -> BytesMut {
92        BytesMut::from(self)
93    }
94}
95impl IntoUriComponent for &[u8] {}
96
97// Integer scalars format to their ASCII decimal rendering via `itoa`
98// (no allocation for the format itself; the `Cow::Owned` / `BytesMut`
99// copy is the only allocation). Decimal digits and a leading `-` are all
100// legal in every URI component, so the encoder always takes its
101// pass-through path for these.
102macro_rules! impl_integer {
103    ($($ty:ty),+ $(,)?) => {
104        $(
105            impl sealed::Sealed for $ty {
106                fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
107                    let mut buf = itoa::Buffer::new();
108                    Cow::Owned(buf.format(*self).as_bytes().to_vec())
109                }
110                fn into_uri_component_bytes_mut(self) -> BytesMut {
111                    let mut buf = itoa::Buffer::new();
112                    BytesMut::from(buf.format(self).as_bytes())
113                }
114            }
115            impl IntoUriComponent for $ty {}
116        )+
117    };
118}
119
120impl_integer!(
121    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
122);
123
124impl sealed::Sealed for super::path::PathRef<'_> {
125    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
126        match self.as_encoded_str() {
127            Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
128            Cow::Owned(s) => Cow::Owned(s.into_bytes()),
129        }
130    }
131    fn into_uri_component_bytes_mut(self) -> BytesMut {
132        let mut bytes = BytesMut::with_capacity(self.bytes.len());
133        self.write_encoded_to(&mut bytes);
134        bytes
135    }
136    fn is_already_uri_component(&self) -> bool {
137        true
138    }
139}
140impl IntoUriComponent for super::path::PathRef<'_> {}
141
142impl sealed::Sealed for super::path::PathSegment<'_> {
143    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
144        match self.as_encoded_str() {
145            Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
146            Cow::Owned(s) => Cow::Owned(s.into_bytes()),
147        }
148    }
149    fn into_uri_component_bytes_mut(self) -> BytesMut {
150        let mut bytes = BytesMut::with_capacity(self.encoded_capacity_hint());
151        self.write_encoded_to(&mut bytes);
152        bytes
153    }
154    fn is_already_uri_component(&self) -> bool {
155        true
156    }
157}
158impl IntoUriComponent for super::path::PathSegment<'_> {}
159
160impl sealed::Sealed for bool {
161    fn as_uri_component_bytes(&self) -> Cow<'_, [u8]> {
162        Cow::Borrowed(if *self { b"true" } else { b"false" })
163    }
164    fn into_uri_component_bytes_mut(self) -> BytesMut {
165        BytesMut::from(if self { &b"true"[..] } else { &b"false"[..] })
166    }
167}
168impl IntoUriComponent for bool {}