aidoku/helpers/
uri.rs

1//! Module for encoding and decoding URIs.
2extern crate alloc;
3
4use crate::AidokuError;
5use alloc::{
6	format,
7	string::{String, ToString},
8	vec::Vec,
9};
10use core::fmt::Display;
11use paste::paste;
12use serde::{
13	Serialize, Serializer,
14	ser::{Error as SerError, Impossible, SerializeMap, SerializeStruct},
15};
16use thiserror::Error;
17
18/// Percent-encode an entire URI string that is valid UTF-8.
19///
20/// `internal_encode_uri` escapes all non-alphanumeric characters not
21/// in the `charset` parameter.
22///
23/// This function is made public for use with a custom unencoded charset.
24pub fn internal_encode_uri<T: AsRef<[u8]>>(url: T, charset: T) -> String {
25	let bytes = url.as_ref();
26	let charset = charset.as_ref();
27	let hex = b"0123456789ABCDEF";
28
29	let mut result: Vec<u8> = Vec::with_capacity(bytes.len() * 3);
30
31	for &byte in bytes {
32		if byte.is_ascii_alphanumeric() || charset.contains(&byte) {
33			result.push(byte);
34		} else {
35			result.push(b'%');
36			result.push(hex[(byte >> 4) as usize]);
37			result.push(hex[(byte & 0x0F) as usize]);
38		}
39	}
40	String::from_utf8(result).unwrap_or_default()
41}
42
43/// Percent-encode an entire URI string that is valid UTF-8.
44///
45/// `encode_uri` escapes all characters except `a-z A-Z 0-9 ; , / ? : @ & = +
46/// $ - _ . ! ~ * ' ( ) #`.
47///
48/// # Examples
49/// ```
50/// use aidoku::helpers::uri::encode_uri;
51/// assert_eq!(
52///     encode_uri("http://www.example.org/a file with spaces.html"),
53///     "http://www.example.org/a%20file%20with%20spaces.html",
54/// )
55/// ```
56pub fn encode_uri<T: AsRef<[u8]>>(url: T) -> String {
57	internal_encode_uri(url.as_ref(), b";,/?:@&=+$-_.!~*'()#")
58}
59
60/// Percent-encode a URI component string that is valid UTF-8.
61///
62/// `encode_uri_component` escapes all characters except `a-z A-Z 0-9 - _ . !
63/// ~ * ' ( )`.
64///
65/// # Examples
66/// ```
67/// use aidoku::helpers::uri::encode_uri_component;
68/// assert_eq!(
69///     encode_uri_component(";,/?:@&=+$"),
70///     "%3B%2C%2F%3F%3A%40%26%3D%2B%24",
71/// )
72/// ```
73pub fn encode_uri_component<T: AsRef<[u8]>>(url: T) -> String {
74	internal_encode_uri(url.as_ref(), b"-_.!~*'()")
75}
76
77/// Decodes a percent-encoded URI string.
78///
79/// Returns the decoded String, or an empty string if invalid UTF-8.
80///
81/// # Examples
82/// ```
83/// use aidoku::helpers::uri::decode_uri;
84/// assert_eq!(
85///     decode_uri("%3B%2C%2F%3F%3A%40%26%3D%2B%24"),
86///     ";,/?:@&=+$",
87/// )
88/// ```
89pub fn decode_uri<T: AsRef<[u8]>>(encoded: T) -> String {
90	let mut iter = encoded.as_ref().iter();
91	let mut result = Vec::with_capacity(encoded.as_ref().len());
92
93	while let Some(&c) = iter.next() {
94		if c == b'%' {
95			// read next two hex digits
96			let hi = iter.next();
97			let lo = iter.next();
98			match (hi, lo) {
99				(Some(&hi), Some(&lo)) => {
100					let hex = (hi as char).to_digit(16).zip((lo as char).to_digit(16));
101					match hex {
102						Some((hi, lo)) => result.push(((hi << 4) | lo) as u8),
103						None => {
104							// invalid hex, push as-is
105							result.push(b'%');
106							result.push(hi);
107							result.push(lo);
108						}
109					}
110				}
111				_ => {
112					// not enough chars, push as-is
113					result.push(b'%');
114					if let Some(&hi) = hi {
115						result.push(hi);
116					}
117					if let Some(&lo) = lo {
118						result.push(lo);
119					}
120				}
121			}
122		} else {
123			result.push(c);
124		}
125	}
126	String::from_utf8(result).unwrap_or_default()
127}
128
129/// Alternating, decoded query names and values.
130#[derive(Clone, Debug, Default)]
131pub struct QueryParameters {
132	params: Vec<(String, Option<String>)>,
133}
134
135impl QueryParameters {
136	#[inline]
137	pub fn new() -> Self {
138		QueryParameters { params: Vec::new() }
139	}
140
141	#[inline]
142	pub fn with_capacity(capacity: usize) -> Self {
143		QueryParameters {
144			params: Vec::with_capacity(capacity),
145		}
146	}
147
148	#[inline]
149	pub fn len(&self) -> usize {
150		self.params.len()
151	}
152
153	#[inline]
154	pub fn is_empty(&self) -> bool {
155		self.params.is_empty()
156	}
157
158	/// Percent-encode the query paramter with [encode_uri_component] and
159	/// add it to the query string along with a value.
160	pub fn push(&mut self, name: &str, value: Option<&str>) {
161		self.params
162			.push((encode_uri_component(name), value.map(encode_uri_component)));
163	}
164
165	/// Percent-encode the query paramter with [encode_uri_component] and
166	/// add it to the query string.
167	pub fn push_key<K: AsRef<str>>(&mut self, name: K) {
168		self.params
169			.push((encode_uri_component(name.as_ref()), None));
170	}
171
172	/// Add a pre-encoded query parameter to the query string.
173	pub fn push_encoded(&mut self, name: &str, value: Option<&str>) {
174		self.params
175			.push((name.to_string(), value.map(|v| v.to_string())));
176	}
177
178	/// Percent-encode the query parameter with [encode_uri_component] and
179	/// replace any existing values.
180	pub fn set(&mut self, name: &str, value: Option<&str>) {
181		self.remove_all(name);
182		self.push(name, value);
183	}
184
185	/// Replace any existing values with the given pair, without encoding.
186	pub fn set_encoded(&mut self, name: &str, value: Option<&str>) {
187		self.remove_all(name);
188		self.push_encoded(name, value);
189	}
190
191	/// Remove all query parameters matching given name.
192	pub fn remove_all<T: AsRef<str>>(&mut self, name: T) {
193		let name = name.as_ref();
194		self.remove_all_encoded(encode_uri_component(name));
195	}
196
197	/// Remove all query parameters matching given pre-encoded name.
198	pub fn remove_all_encoded<T: AsRef<str>>(&mut self, name: T) {
199		let name = name.as_ref();
200		self.params.retain(|(n, _)| n != name);
201	}
202
203	/// Serialize the given data as a [`QueryParameters`] struct.
204	pub fn from_data<T: Serialize>(value: &T) -> Result<Self, SerializeError> {
205		let mut query = Self::new();
206		value.serialize(&mut query)?;
207		Ok(query)
208	}
209}
210
211impl Display for QueryParameters {
212	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213		let mut first = true;
214		for (n, v) in &self.params {
215			if !first {
216				write!(f, "&")?;
217			} else {
218				first = false;
219			}
220			write!(f, "{}", n)?;
221			if let Some(v) = v {
222				write!(f, "={}", v)?;
223			}
224		}
225		Ok(())
226	}
227}
228
229macro_rules! serialize_integer {
230	($($type:ty),+) => {$(paste! {
231		fn [<serialize_ $type>](self, v: $type) -> Result<Self::Ok, Self::Error> {
232			self.params
233				.last_mut()
234				.ok_or(SerializeError::TopLevel(stringify!($type)))?
235				.1 = Some(itoa::Buffer::new().format(v).into());
236			Ok(())
237		}
238	})+};
239}
240
241macro_rules! serialize_display {
242	($($type:ty),+) => {$(paste! {
243		fn [<serialize_ $type>](self, v: $type) -> Result<Self::Ok, Self::Error> {
244			self.params
245				.last_mut()
246				.ok_or(SerializeError::TopLevel(stringify!($type)))?
247				.1 = Some(v.to_string());
248			Ok(())
249		}
250	})+};
251}
252
253impl Serializer for &mut QueryParameters {
254	type Ok = ();
255	type Error = SerializeError;
256
257	type SerializeSeq = Impossible<(), SerializeError>;
258	type SerializeTuple = Impossible<(), SerializeError>;
259	type SerializeTupleStruct = Impossible<(), SerializeError>;
260	type SerializeTupleVariant = Impossible<(), SerializeError>;
261	type SerializeMap = Self;
262	type SerializeStruct = Self;
263	type SerializeStructVariant = Impossible<(), SerializeError>;
264
265	/// key1=`true`&key2=`false`&...
266	fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
267		self.params.try_last_mut("bool")?.1 = Some(if v { "true" } else { "false" }.into());
268		Ok(())
269	}
270
271	serialize_integer! { i8, i16, i32, i64, i128, u8, u16, u32, u64, u128 }
272
273	serialize_display! { f32, f64 }
274
275	/// key1=`a`&key2=`%20`&...
276	fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
277		if self.params.last().is_none() {
278			return Err(SerializeError::TopLevel("char"));
279		}
280
281		let mut b = [0; 4];
282		self.serialize_str(v.encode_utf8(&mut b))
283	}
284
285	/// key1=`abc`&key2=`a%20b%20c`&...
286	fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
287		if self.params.last().is_none() {
288			return Err(SerializeError::TopLevel("&str"));
289		}
290
291		self.serialize_bytes(v.as_bytes())
292	}
293
294	fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
295		self.params.try_last_mut("&[u8]")?.1 = Some(encode_uri_component(v));
296		Ok(())
297	}
298
299	/// key1&key2&...
300	fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
301		let last = self.params.try_last_mut("Option<T>")?;
302		if last.0.is_empty() {
303			return Err(SerializeError::InvalidKey("Option<T>"));
304		}
305
306		last.1 = None;
307		Ok(())
308	}
309
310	fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
311	where
312		T: ?Sized + Serialize,
313	{
314		if self
315			.params
316			.last()
317			.ok_or(SerializeError::TopLevel("Option<T>"))?
318			.0
319			.is_empty()
320		{
321			return Err(SerializeError::InvalidKey("Option<T>"));
322		}
323
324		value.serialize(self)
325	}
326
327	/// key1=&key2=&...
328	fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
329		self.params.try_last_mut("()")?.1 = Some(String::new());
330		Ok(())
331	}
332
333	/// Serialize as a unit.
334	fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
335		if self.params.last().is_none() {
336			return Err(SerializeError::TopLevel(name));
337		}
338
339		self.serialize_unit()
340	}
341
342	/// Serialize as the name of the variant.
343	fn serialize_unit_variant(
344		self,
345		name: &'static str,
346		_variant_index: u32,
347		variant: &'static str,
348	) -> Result<Self::Ok, Self::Error> {
349		if self.params.last().is_none() {
350			return Err(SerializeError::TopLevel(name));
351		}
352
353		self.serialize_str(variant)
354	}
355
356	/// Serialize as the value wrapped in the struct.
357	fn serialize_newtype_struct<T>(
358		self,
359		name: &'static str,
360		value: &T,
361	) -> Result<Self::Ok, Self::Error>
362	where
363		T: ?Sized + Serialize,
364	{
365		if self.params.last().is_none() {
366			return Err(SerializeError::TopLevel(name));
367		}
368
369		value.serialize(self)
370	}
371
372	/// Serialize as the value contained within the variant.
373	fn serialize_newtype_variant<T>(
374		self,
375		name: &'static str,
376		_variant_index: u32,
377		_variant: &'static str,
378		value: &T,
379	) -> Result<Self::Ok, Self::Error>
380	where
381		T: ?Sized + Serialize,
382	{
383		if self.params.last().is_none() {
384			return Err(SerializeError::TopLevel(name));
385		}
386
387		value.serialize(self)
388	}
389
390	fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
391		Err(SerializeError::invalid("Vec<T>"))
392	}
393
394	fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
395		Err(SerializeError::invalid("(T0, T1,...)` or `[T, T,...]"))
396	}
397
398	fn serialize_tuple_struct(
399		self,
400		name: &'static str,
401		_len: usize,
402	) -> Result<Self::SerializeTupleStruct, Self::Error> {
403		Err(SerializeError::invalid(name))
404	}
405
406	fn serialize_tuple_variant(
407		self,
408		name: &'static str,
409		_variant_index: u32,
410		variant: &'static str,
411		_len: usize,
412	) -> Result<Self::SerializeTupleVariant, Self::Error> {
413		Err(SerializeError::invalid(format!("{name}::{variant}")))
414	}
415
416	fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
417		if len.is_some() && !self.params.is_empty() {
418			return Err(SerializeError::NotTopLevel("Map<K, V>"));
419		}
420
421		Ok(self)
422	}
423
424	fn serialize_struct(
425		self,
426		name: &'static str,
427		_len: usize,
428	) -> Result<Self::SerializeStruct, Self::Error> {
429		if !self.params.is_empty() {
430			return Err(SerializeError::NotTopLevel(name));
431		}
432
433		Ok(self)
434	}
435
436	fn serialize_struct_variant(
437		self,
438		name: &'static str,
439		_variant_index: u32,
440		variant: &'static str,
441		_len: usize,
442	) -> Result<Self::SerializeStructVariant, Self::Error> {
443		Err(SerializeError::invalid(format!("{name}::{variant}")))
444	}
445}
446
447impl SerializeMap for &mut QueryParameters {
448	type Ok = ();
449	type Error = SerializeError;
450
451	fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
452	where
453		T: ?Sized + Serialize,
454	{
455		self.push_encoded("", None);
456		key.serialize(&mut **self)?;
457		let last = self.params.last_mut().ok_or(SerializeError::NoParam)?;
458		last.0 = last
459			.1
460			.as_deref()
461			.ok_or(SerializeError::InvalidKey("Option<T>"))?
462			.into();
463		Ok(())
464	}
465
466	fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
467	where
468		T: ?Sized + Serialize,
469	{
470		value.serialize(&mut **self)
471	}
472
473	fn end(self) -> Result<Self::Ok, Self::Error> {
474		Ok(())
475	}
476}
477
478impl SerializeStruct for &mut QueryParameters {
479	type Ok = ();
480	type Error = SerializeError;
481
482	fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
483	where
484		T: ?Sized + Serialize,
485	{
486		self.push_key(key);
487		value.serialize(&mut **self)
488	}
489
490	fn end(self) -> Result<Self::Ok, Self::Error> {
491		Ok(())
492	}
493}
494
495#[derive(Error, Debug, PartialEq, Eq)]
496pub enum SerializeError {
497	#[error("{0}")]
498	Custom(String),
499	#[error("`{0}` can only be serialized at the top level")]
500	NotTopLevel(&'static str),
501	#[error("cannot serialize `{0}` at the top level")]
502	TopLevel(&'static str),
503	#[error("expected parameter after serialized key, but none was found")]
504	NoParam,
505	#[error("cannot serialize `{0}` as key")]
506	InvalidKey(&'static str),
507	#[error("cannot serialize `{0}`")]
508	Invalid(String),
509}
510
511impl SerializeError {
512	fn invalid<S: Display>(r#type: S) -> Self {
513		Self::Invalid(r#type.to_string())
514	}
515}
516
517impl From<SerializeError> for AidokuError {
518	fn from(error: SerializeError) -> Self {
519		Self::Message(error.to_string())
520	}
521}
522
523impl SerError for SerializeError {
524	fn custom<T>(msg: T) -> Self
525	where
526		T: Display,
527	{
528		Self::Custom(msg.to_string())
529	}
530}
531
532trait TryParams {
533	type Param;
534	fn try_last_mut(&mut self, r#type: &'static str) -> Result<&mut Self::Param, SerializeError>;
535}
536
537impl<T> TryParams for Vec<T> {
538	type Param = T;
539	fn try_last_mut(&mut self, r#type: &'static str) -> Result<&mut Self::Param, SerializeError> {
540		self.last_mut().ok_or(SerializeError::TopLevel(r#type))
541	}
542}