1use std::{num, str};
4
5use o3::alloc::{self, StrExt as _, array};
6
7use crate::uri::{self, path};
8
9mod plan;
10pub use plan::Plan;
11mod raw;
12mod relative;
13pub use relative::Relative;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum Error {
17 Colon,
18 Scheme(uri::Error),
19 Separator { colon: usize, len: usize },
20 UserInfo,
21 Authority(uri::Error),
22 Start { byte: Option<u8> },
23 Origin(path::Error),
24 Length,
25 Index { value: usize },
26 Allocation(alloc::Exhausted),
27}
28
29#[derive(Clone, Copy, PartialEq, Eq)]
30pub struct Absolute<'a> {
31 scheme: &'a str,
32 parsed: uri::Authority<'a>,
33 origin: Origin<'a>,
34}
35
36impl<'a> Absolute<'a> {
37 pub fn parse(value: &'a str) -> Result<Self, Error> {
38 let bytes = value.as_bytes();
39 let colon = bytes
40 .iter()
41 .position(|byte| *byte == b':')
42 .ok_or(Error::Colon)?;
43 let scheme = unsafe { raw::slice(value, 0..colon) };
45 uri::Scheme::validate(scheme.as_bytes()).map_err(Error::Scheme)?;
46 let start = colon.checked_add(3).ok_or(Error::Length)?;
47 if bytes.get(colon..start) != Some(b"://") {
48 return Err(Error::Separator {
49 colon,
50 len: bytes.len(),
51 });
52 }
53 let rest = bytes.get(start..).ok_or(Error::Length)?;
54 let len = match rest
55 .iter()
56 .position(|byte| matches!(*byte, b'/' | b'?' | b'#'))
57 {
58 Some(len) => len,
59 None => rest.len(),
60 };
61 let end = start.checked_add(len).ok_or(Error::Length)?;
62 let raw = value.get(start..end).ok_or(Error::Length)?;
63 let parsed = authority(raw)?;
64 let origin = Origin::from_uri(value.get(end..).ok_or(Error::Length)?)?;
65 Ok(Self {
66 scheme,
67 parsed,
68 origin,
69 })
70 }
71
72 pub const fn scheme(self) -> &'a str {
73 self.scheme
74 }
75
76 pub const fn authority(self) -> uri::Authority<'a> {
77 self.parsed
78 }
79
80 pub const fn origin(self) -> Origin<'a> {
81 self.origin
82 }
83}
84
85#[derive(Clone, Copy, PartialEq, Eq)]
86pub struct Origin<'a> {
87 value: &'a str,
88 path_end: u32,
89 slash: Slash,
90}
91
92#[derive(Clone, Copy, PartialEq, Eq)]
93#[repr(u8)]
94enum Slash {
95 Present,
96 Insert,
97}
98
99impl<'a> Origin<'a> {
100 pub const ROOT: Self = Self {
101 value: "/",
102 path_end: 1,
103 slash: Slash::Present,
104 };
105
106 pub fn parse(value: &'a str) -> Result<Self, Error> {
107 if !value.starts_with('/') {
108 return Err(Error::Start {
109 byte: value.as_bytes().first().copied(),
110 });
111 }
112 Self::validated(value, Slash::Present)
113 }
114
115 fn from_uri(value: &'a str) -> Result<Self, Error> {
116 match value.as_bytes().first() {
117 Some(b'/') => Self::validated(value, Slash::Present),
118 Some(b'?') => Self::validated(value, Slash::Insert),
119 Some(byte) => Err(Error::Start { byte: Some(*byte) }),
120 None => Self::validated(value, Slash::Insert),
121 }
122 }
123
124 fn validated(value: &'a str, slash: Slash) -> Result<Self, Error> {
125 let plan = path::Scan::new(value.as_bytes())
126 .complete()
127 .map_err(Error::Origin)?;
128 Ok(Self {
129 value,
130 path_end: narrow(plan.path_end())?,
131 slash,
132 })
133 }
134
135 fn len(self) -> Result<usize, Error> {
136 let prefix = match self.slash {
137 Slash::Present => 0,
138 Slash::Insert => 1,
139 };
140 self.value.len().checked_add(prefix).ok_or(Error::Length)
141 }
142
143 fn append_to(self, output: &mut String) {
144 match self.slash {
145 Slash::Present => {}
146 Slash::Insert => output.push('/'),
147 }
148 output.push_str(self.value);
149 }
150
151 fn path_end(self) -> usize {
152 self.path_end as usize + usize::from(self.slash == Slash::Insert)
153 }
154}
155
156#[derive(Debug)]
161pub struct Target {
162 value: Box<str>,
163 scheme_end: Option<num::NonZero<u32>>,
164 host_end: u32,
165 authority_end: u32,
166 path_end: u32,
167}
168
169impl Target {
170 pub fn parse(value: &str) -> Result<Self, Error> {
171 let parsed = Absolute::parse(value)?;
172 let start = parsed.scheme().len() + 3;
173 let end = value.len() - parsed.origin().value.len();
174 let raw = unsafe { raw::slice(value, start..end) };
176 pack(
177 Some(parsed.scheme()),
178 raw,
179 parsed.authority(),
180 parsed.origin(),
181 )
182 }
183
184 pub fn new(raw: &str, origin: Origin<'_>) -> Result<Self, Error> {
185 let parsed = authority(raw)?;
186 pack(None, raw, parsed, origin)
187 }
188
189 pub fn from_host(
190 host: &str,
191 port: Option<num::NonZero<u16>>,
192 origin: Origin<'_>,
193 ) -> Result<Self, Error> {
194 let brackets = host.contains(':') && !host.starts_with('[');
195 let host_end = if brackets {
196 if !uri::Authority::is_ip(host.as_bytes()) {
197 return Err(Error::Authority(uri::Error::InvalidHost));
198 }
199 host.len().checked_add(2).ok_or(Error::Length)?
200 } else {
201 if host.is_empty() && port.is_some() {
202 return Err(Error::Authority(uri::Error::InvalidHost));
203 }
204 let parsed = uri::Authority::parse(host.as_bytes()).map_err(Error::Authority)?;
205 if port.is_some() && parsed.port().is_some() {
206 return Err(Error::Authority(uri::Error::InvalidPort));
207 }
208 parsed.host().len()
209 };
210 let mut digits = [0; 20];
211 let port = port.map(|port| raw::decimal(port, &mut digits));
212 let port_len = port.map_or(0, |port| port.len() + 1);
213 let end = host
214 .len()
215 .checked_add(usize::from(brackets) * 2)
216 .and_then(|len| len.checked_add(port_len))
217 .ok_or(Error::Length)?;
218 let capacity = end.checked_add(origin.len()?).ok_or(Error::Length)?;
219 let path_end = end.checked_add(origin.path_end()).ok_or(Error::Length)?;
220 let bounds = plan::Bounds::new(0, host_end, end, path_end)?;
221 let mut value = buffer(capacity)?;
222 if brackets {
223 value.push('[');
224 }
225 value.push_str(host);
226 if brackets {
227 value.push(']');
228 }
229 if let Some(port) = port {
230 value.push(':');
231 value.push_str(port);
232 }
233 origin.append_to(&mut value);
234 Ok(bounds.build(unsafe { raw::boxed(value) }))
236 }
237
238 pub fn scheme_str(&self) -> Option<&str> {
239 self.scheme_end.map(|end| {
240 unsafe { raw::slice(&self.value, 0..end.get() as usize) }
242 })
243 }
244
245 pub fn try_clone(&self) -> Result<Self, alloc::Exhausted> {
247 let value = self.value.try_to_owned()?;
248 Ok(Self {
249 value: unsafe { raw::boxed(value) },
251 scheme_end: self.scheme_end,
252 host_end: self.host_end,
253 authority_end: self.authority_end,
254 path_end: self.path_end,
255 })
256 }
257
258 pub fn authority(&self) -> &str {
259 unsafe { raw::slice(&self.value, self.start()..self.authority_end as usize) }
261 }
262
263 pub fn host_raw(&self) -> &str {
265 unsafe { raw::slice(&self.value, self.start()..self.host_end as usize) }
267 }
268
269 pub fn host(&self) -> &str {
270 let host = self.host_raw();
271 host.strip_prefix('[')
272 .and_then(|host| host.strip_suffix(']'))
273 .unwrap_or(host)
274 }
275
276 pub fn path(&self) -> &str {
277 let range = self.authority_end as usize..self.path_end as usize;
278 unsafe { raw::slice(&self.value, range) }
280 }
281
282 pub fn path_query(&self) -> &str {
283 unsafe { raw::slice(&self.value, self.authority_end as usize..self.value.len()) }
285 }
286
287 pub fn query(&self) -> Option<&str> {
288 let end = self.path_end as usize;
289 (end != self.value.len()).then(|| {
290 unsafe { raw::slice(&self.value, end + 1..self.value.len()) }
292 })
293 }
294
295 pub fn retarget(self, raw: &str) -> Result<Self, Error> {
296 let parsed = authority(raw)?;
297 pack(self.scheme_str(), raw, parsed, Origin::from(&self))
298 }
299
300 pub fn with_origin(&self, origin: Origin<'_>) -> Result<Self, Error> {
301 pack(self.scheme_str(), self.authority(), self.parts(), origin)
302 }
303
304 pub fn normalize(self) -> Result<Self, Error> {
306 let relative = Relative::from_target(&self);
307 if !relative.has_dots() {
308 return Ok(self);
309 }
310 relative.materialize(&self)
311 }
312
313 fn start(&self) -> usize {
314 match self.scheme_end {
315 Some(end) => end.get() as usize + 3,
316 None => 0,
317 }
318 }
319
320 pub fn parts(&self) -> uri::Authority<'_> {
322 let host = self.host_raw().as_bytes();
323 let port = (self.host_end != self.authority_end).then(|| {
324 let range = self.host_end as usize + 1..self.authority_end as usize;
325 unsafe { raw::slice(&self.value, range) }.as_bytes()
327 });
328 uri::Authority::new(host, port)
329 }
330}
331
332impl<'a> From<&'a Target> for Origin<'a> {
333 fn from(target: &'a Target) -> Self {
334 Self {
335 value: target.path_query(),
336 path_end: target.path_end - target.authority_end,
337 slash: Slash::Present,
338 }
339 }
340}
341
342impl AsRef<str> for Target {
343 fn as_ref(&self) -> &str {
344 &self.value
345 }
346}
347
348impl str::FromStr for Target {
349 type Err = Error;
350
351 fn from_str(value: &str) -> Result<Self, Self::Err> {
352 if !value.contains("://") {
353 return Self::new(value, Origin::ROOT);
354 }
355 Self::parse(value)
356 }
357}
358
359impl PartialEq for Target {
360 fn eq(&self, other: &Self) -> bool {
361 let scheme = match (self.scheme_str(), other.scheme_str()) {
362 (Some(left), Some(right)) => left.eq_ignore_ascii_case(right),
363 (None, None) => true,
364 _ => false,
365 };
366 scheme
367 && self.authority().eq_ignore_ascii_case(other.authority())
368 && self.path_query() == other.path_query()
369 }
370}
371
372impl Eq for Target {}
373
374fn authority(raw: &str) -> Result<uri::Authority<'_>, Error> {
375 if raw.as_bytes().contains(&b'@') {
376 return Err(Error::UserInfo);
377 }
378 uri::Authority::parse(raw.as_bytes()).map_err(Error::Authority)
379}
380
381fn pack(
382 scheme: Option<&str>,
383 raw: &str,
384 parsed: uri::Authority<'_>,
385 origin: Origin<'_>,
386) -> Result<Target, Error> {
387 let start = match scheme {
388 Some(scheme) => scheme.len().checked_add(3).ok_or(Error::Length)?,
389 None => 0,
390 };
391 let end = start.checked_add(raw.len()).ok_or(Error::Length)?;
392 let capacity = end.checked_add(origin.len()?).ok_or(Error::Length)?;
393 let host_end = start + parsed.host().len();
394 let path_end = end.checked_add(origin.path_end()).ok_or(Error::Length)?;
395 let bounds = plan::Bounds::new(scheme.map_or(0, str::len), host_end, end, path_end)?;
396 let mut value = buffer(capacity)?;
397 if let Some(scheme) = scheme {
398 value.push_str(scheme);
399 value.push_str("://");
400 }
401 value.push_str(raw);
402 origin.append_to(&mut value);
403 Ok(bounds.build(unsafe { raw::boxed(value) }))
405}
406
407fn narrow(value: usize) -> Result<u32, Error> {
408 if value > u32::MAX as usize {
409 return Err(Error::Index { value });
410 }
411 Ok(value as u32)
412}
413
414fn buffer(capacity: usize) -> Result<String, Error> {
415 array::Plan::try_new(capacity)
416 .map_err(|_| Error::Length)?
417 .try_string()
418 .map_err(Error::Allocation)
419}
420
421const _: () = assert!(core::mem::size_of::<Absolute<'_>>() <= 9 * core::mem::size_of::<usize>());
422const _: () = assert!(core::mem::size_of::<Slash>() == core::mem::size_of::<bool>());
423const _: () = assert!(core::mem::size_of::<Origin<'_>>() <= 3 * core::mem::size_of::<usize>());
424const _: () = assert!(core::mem::size_of::<Target>() <= 4 * core::mem::size_of::<usize>());