use super::component_input::IntoUriComponent;
use super::encode;
use super::owned::OwnedUriRef;
use super::query::{Query, QueryPair, QueryPairRef, QueryRef, form_decode_bytes};
use rama_core::bytes::{Bytes, BytesMut};
pub struct QueryMut<'a> {
owned: &'a mut OwnedUriRef,
}
impl<'a> QueryMut<'a> {
#[inline]
pub(crate) fn new(owned: &'a mut OwnedUriRef) -> Self {
Self { owned }
}
#[expect(
clippy::needless_pass_by_value,
reason = "by-value matches IntoUriComponent's signature on sibling setters"
)]
pub fn push_pair(
&mut self,
name: impl IntoUriComponent,
value: impl IntoUriComponent,
) -> &mut Self {
let buf = self.buf_for_append();
encode::extend_encoded_pair(buf, &name);
buf.extend_from_slice(b"=");
encode::extend_encoded_pair(buf, &value);
self
}
#[expect(
clippy::needless_pass_by_value,
reason = "by-value matches IntoUriComponent's signature on sibling setters"
)]
pub fn push_key(&mut self, name: impl IntoUriComponent) -> &mut Self {
let buf = self.buf_for_append();
encode::extend_encoded_pair(buf, &name);
self
}
pub fn pop(&mut self) -> Option<QueryPair> {
loop {
let q = self.owned.query.as_mut()?;
if q.bytes.is_empty() {
return None;
}
let pair_bytes = match memchr::memrchr(b'&', &q.bytes) {
Some(i) => {
let mut tail = q.bytes.split_off(i);
let _amp = tail.split_to(1);
tail
}
None => core::mem::take(&mut q.bytes),
};
if pair_bytes.is_empty() {
continue;
}
return Some(QueryPair::from_raw(pair_bytes.freeze()));
}
}
pub fn drain(&mut self) -> Drain {
let bytes = match self.owned.query.as_mut() {
Some(q) => core::mem::take(&mut q.bytes).freeze(),
None => Bytes::new(),
};
Drain { bytes, offset: 0 }
}
pub fn retain(&mut self, mut keep: impl FnMut(QueryPairRef<'_>) -> bool) -> &mut Self {
let Some(q) = self.owned.query.as_mut() else {
return self;
};
let old = core::mem::take(&mut q.bytes);
let mut new = BytesMut::with_capacity(old.len());
for pair in QueryRef::new(&old).pairs() {
if keep(pair) {
if !new.is_empty() {
new.extend_from_slice(b"&");
}
new.extend_from_slice(pair.raw_bytes());
}
}
q.bytes = new;
self
}
#[expect(
clippy::needless_pass_by_value,
reason = "by-value matches IntoUriComponent's signature on sibling setters; this impl only borrows the input"
)]
pub fn remove(&mut self, name: impl IntoUriComponent) -> usize {
let name = name.as_uri_component_bytes();
let pattern = form_decode_bytes(&name).into_owned();
let mut removed = 0;
self.retain(|pair| {
let matches = *form_decode_bytes(pair.name_bytes()) == *pattern;
removed += usize::from(matches);
!matches
});
removed
}
pub fn set_pair(
&mut self,
name: impl IntoUriComponent,
value: impl IntoUriComponent,
) -> &mut Self {
self.remove(&*name.as_uri_component_bytes());
self.push_pair(name, value)
}
fn buf_for_append(&mut self) -> &mut BytesMut {
let q = self.owned.query.get_or_insert_with(|| Query {
bytes: BytesMut::new(),
});
if !q.bytes.is_empty() {
q.bytes.extend_from_slice(b"&");
}
&mut q.bytes
}
}
impl core::fmt::Debug for QueryMut<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let query = self
.owned
.query
.as_ref()
.map(|q| unsafe { core::str::from_utf8_unchecked(&q.bytes) });
f.debug_struct("QueryMut").field("query", &query).finish()
}
}
#[derive(Debug, Clone)]
pub struct Drain {
bytes: Bytes,
offset: usize,
}
impl Iterator for Drain {
type Item = QueryPair;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.offset >= self.bytes.len() {
return None;
}
let remaining = &self.bytes[self.offset..];
let (start, end) = match memchr::memchr(b'&', remaining) {
Some(i) => (self.offset, self.offset + i),
None => (self.offset, self.bytes.len()),
};
self.offset = end + 1;
if start == end {
continue;
}
let fragment = self.bytes.slice(start..end);
return Some(QueryPair::from_raw(fragment));
}
}
}
impl core::iter::FusedIterator for Drain {}