rama_net/uri/
query_mut.rs1use super::component_input::IntoUriComponent;
9use super::encode;
10use super::owned::OwnedUriRef;
11use super::query::{Query, QueryPair, QueryPairRef, QueryRef, form_decode_bytes};
12
13use rama_core::bytes::{Bytes, BytesMut};
14
15pub struct QueryMut<'a> {
21 owned: &'a mut OwnedUriRef,
22}
23
24impl<'a> QueryMut<'a> {
25 #[inline]
26 pub(crate) fn new(owned: &'a mut OwnedUriRef) -> Self {
27 Self { owned }
28 }
29
30 #[expect(
34 clippy::needless_pass_by_value,
35 reason = "by-value matches IntoUriComponent's signature on sibling setters"
36 )]
37 pub fn push_pair(
38 &mut self,
39 name: impl IntoUriComponent,
40 value: impl IntoUriComponent,
41 ) -> &mut Self {
42 let buf = self.buf_for_append();
43 encode::extend_encoded_pair(buf, &name);
44 buf.extend_from_slice(b"=");
45 encode::extend_encoded_pair(buf, &value);
46 self
47 }
48
49 #[expect(
52 clippy::needless_pass_by_value,
53 reason = "by-value matches IntoUriComponent's signature on sibling setters"
54 )]
55 pub fn push_key(&mut self, name: impl IntoUriComponent) -> &mut Self {
56 let buf = self.buf_for_append();
57 encode::extend_encoded_pair(buf, &name);
58 self
59 }
60
61 pub fn pop(&mut self) -> Option<QueryPair> {
67 loop {
68 let q = self.owned.query.as_mut()?;
69 if q.bytes.is_empty() {
70 return None;
71 }
72 let pair_bytes = match memchr::memrchr(b'&', &q.bytes) {
73 Some(i) => {
74 let mut tail = q.bytes.split_off(i);
76 let _amp = tail.split_to(1);
77 tail
78 }
79 None => core::mem::take(&mut q.bytes),
80 };
81 if pair_bytes.is_empty() {
82 continue;
84 }
85 return Some(QueryPair::from_raw(pair_bytes.freeze()));
86 }
87 }
88
89 pub fn drain(&mut self) -> Drain {
96 let bytes = match self.owned.query.as_mut() {
97 Some(q) => core::mem::take(&mut q.bytes).freeze(),
98 None => Bytes::new(),
99 };
100 Drain { bytes, offset: 0 }
101 }
102
103 pub fn retain(&mut self, mut keep: impl FnMut(QueryPairRef<'_>) -> bool) -> &mut Self {
110 let Some(q) = self.owned.query.as_mut() else {
111 return self;
112 };
113 let old = core::mem::take(&mut q.bytes);
114 let mut new = BytesMut::with_capacity(old.len());
115 for pair in QueryRef::new(&old).pairs() {
116 if keep(pair) {
117 if !new.is_empty() {
118 new.extend_from_slice(b"&");
119 }
120 new.extend_from_slice(pair.raw_bytes());
121 }
122 }
123 q.bytes = new;
124 self
125 }
126
127 #[expect(
134 clippy::needless_pass_by_value,
135 reason = "by-value matches IntoUriComponent's signature on sibling setters; this impl only borrows the input"
136 )]
137 pub fn remove(&mut self, name: impl IntoUriComponent) -> usize {
138 let name = name.as_uri_component_bytes();
139 let pattern = form_decode_bytes(&name).into_owned();
140 let mut removed = 0;
141 self.retain(|pair| {
142 let matches = *form_decode_bytes(pair.name_bytes()) == *pattern;
143 removed += usize::from(matches);
144 !matches
145 });
146 removed
147 }
148
149 pub fn set_pair(
154 &mut self,
155 name: impl IntoUriComponent,
156 value: impl IntoUriComponent,
157 ) -> &mut Self {
158 self.remove(&*name.as_uri_component_bytes());
159 self.push_pair(name, value)
160 }
161
162 fn buf_for_append(&mut self) -> &mut BytesMut {
166 let q = self.owned.query.get_or_insert_with(|| Query {
167 bytes: BytesMut::new(),
168 });
169 if !q.bytes.is_empty() {
170 q.bytes.extend_from_slice(b"&");
171 }
172 &mut q.bytes
173 }
174}
175
176impl core::fmt::Debug for QueryMut<'_> {
177 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178 let query = self
179 .owned
180 .query
181 .as_ref()
182 .map(|q| unsafe { core::str::from_utf8_unchecked(&q.bytes) });
184 f.debug_struct("QueryMut").field("query", &query).finish()
185 }
186}
187
188#[derive(Debug, Clone)]
191pub struct Drain {
192 bytes: Bytes,
193 offset: usize,
194}
195
196impl Iterator for Drain {
197 type Item = QueryPair;
198
199 fn next(&mut self) -> Option<Self::Item> {
200 loop {
201 if self.offset >= self.bytes.len() {
202 return None;
203 }
204 let remaining = &self.bytes[self.offset..];
206 let (start, end) = match memchr::memchr(b'&', remaining) {
207 Some(i) => (self.offset, self.offset + i),
208 None => (self.offset, self.bytes.len()),
209 };
210 self.offset = end + 1; if start == end {
213 continue;
215 }
216
217 let fragment = self.bytes.slice(start..end);
218 return Some(QueryPair::from_raw(fragment));
219 }
220 }
221}
222
223impl core::iter::FusedIterator for Drain {}