sark_http/uri/target/relative/
mod.rs1use std::iter;
2
3use o3::alloc::array;
4
5use crate::uri::{
6 path,
7 target::{self, Error, Origin, Slash, Target},
8};
9
10mod raw;
11
12#[derive(Clone, Copy)]
14pub struct Relative<'a> {
15 value: &'a str,
16 path_end: u32,
17}
18
19impl<'a> Relative<'a> {
20 pub(super) fn from_target(target: &'a Target) -> Self {
21 Self {
22 value: target.path_query(),
23 path_end: target.path_end - target.authority_end,
24 }
25 }
26
27 pub fn parse(value: &'a str) -> Result<Self, Error> {
28 let plan = path::Scan::new(value.as_bytes())
29 .complete()
30 .map_err(Error::Origin)?;
31 Ok(Self {
32 value,
33 path_end: target::narrow(plan.path_end())?,
34 })
35 }
36
37 pub fn resolve(self, base: &Target) -> Result<Target, Error> {
41 if self.value.is_empty() {
42 return base.try_clone().map_err(Error::Allocation);
43 }
44 if self.value.starts_with('/') && !self.has_dots() {
45 return base.with_origin(Origin {
46 value: self.value,
47 path_end: self.path_end,
48 slash: Slash::Present,
49 });
50 }
51 self.materialize(base)
52 }
53
54 pub(super) fn materialize(self, base: &Target) -> Result<Target, Error> {
55 let (path, query) = unsafe {
57 (
58 target::raw::slice(self.value, 0..self.path_end as usize),
59 target::raw::slice(self.value, self.path_end as usize..self.value.len()),
60 )
61 };
62 let (value, path_end) = if path.is_empty() {
63 let capacity = (base.path_end as usize)
64 .checked_add(query.len())
65 .ok_or(Error::Length)?;
66 let mut value = target::buffer(capacity)?;
67 value.push_str(unsafe { target::raw::slice(&base.value, 0..base.path_end as usize) });
69 value.push_str(query);
70 (value, base.path_end)
71 } else {
72 let (prefix, path) = match path.strip_prefix('/') {
73 Some(path) => ("", path),
74 None => {
75 let base = base.path();
76 let end = base.rfind('/').unwrap_or(0) + 1;
77 (unsafe { target::raw::slice(base, 1..end) }, path)
79 }
80 };
81 let segments = segments(prefix, path);
82 let path_len = segments.clone().try_fold(0usize, |len, part| {
83 len.checked_add(part.len() + 1).ok_or(Error::Length)
84 })?;
85 let path_end = (base.authority_end as usize)
86 .checked_add(path_len)
87 .ok_or(Error::Length)?;
88 let capacity = path_end.checked_add(query.len()).ok_or(Error::Length)?;
89 let buffer = array::Plan::try_new(capacity).map_err(|_| Error::Length)?;
90 let value = unsafe {
93 raw::normalize(
94 buffer,
95 target::raw::slice(&base.value, 0..base.authority_end as usize),
96 path_len,
97 segments,
98 query,
99 )
100 }
101 .map_err(Error::Allocation)?;
102 target::narrow(path_len)?;
103 let path_end = target::narrow(path_end)?;
104 (value, path_end)
105 };
106 Ok(Target {
107 value: unsafe { target::raw::boxed(value) },
109 scheme_end: base.scheme_end,
110 host_end: base.host_end,
111 authority_end: base.authority_end,
112 path_end,
113 })
114 }
115
116 pub(super) fn has_dots(self) -> bool {
117 unsafe { target::raw::slice(self.value, 0..self.path_end as usize) }
119 .split('/')
120 .any(|part| matches!(part, "." | ".."))
121 }
122}
123
124fn segments<'a>(prefix: &'a str, path: &'a str) -> impl Iterator<Item = &'a str> + Clone {
127 let trailing = matches!(path.rsplit('/').next(), Some("." | ".."));
128 let mut parents = 0usize;
129 iter::once("").take(usize::from(trailing)).chain(
130 path.rsplit('/')
131 .chain(prefix.rsplit_terminator('/'))
132 .filter(move |part| match *part {
133 "." => false,
134 ".." => {
135 parents += 1;
136 false
137 }
138 _ if parents != 0 => {
139 parents -= 1;
140 false
141 }
142 _ => true,
143 }),
144 )
145}