sark_http/uri/target/
plan.rs1use std::num::NonZero;
2
3use o3::alloc::{self, array};
4
5use crate::uri::target::{self, Absolute, Error, Slash, Target};
6
7pub struct Plan {
10 value: String,
11 prefix: bool,
12 slash: Slash,
13 buffer: array::Plan<u8, usize>,
14 bounds: Bounds,
15}
16
17pub(super) struct Bounds {
18 scheme_end: Option<NonZero<u32>>,
19 host_end: u32,
20 authority_end: u32,
21 path_end: u32,
22}
23
24impl Bounds {
25 pub(super) fn new(scheme: usize, host: usize, end: usize, path: usize) -> Result<Self, Error> {
26 Ok(Self {
27 scheme_end: NonZero::new(target::narrow(scheme)?),
28 host_end: target::narrow(host)?,
29 authority_end: target::narrow(end)?,
30 path_end: target::narrow(path)?,
31 })
32 }
33
34 pub(super) fn build(self, value: Box<str>) -> Target {
35 Target {
36 value,
37 scheme_end: self.scheme_end,
38 host_end: self.host_end,
39 authority_end: self.authority_end,
40 path_end: self.path_end,
41 }
42 }
43}
44
45impl Plan {
46 pub fn parse(value: String) -> Result<Self, Error> {
47 let parsed = Absolute::parse(&value)?;
48 let origin = parsed.origin();
49 let scheme = parsed.scheme().len();
50 let end = value.len() - origin.value.len();
51 let host = scheme + 3 + parsed.authority().host().len();
52 let path = end.checked_add(origin.path_end()).ok_or(Error::Length)?;
53 let capacity = value
54 .len()
55 .checked_add(usize::from(origin.slash == Slash::Insert))
56 .ok_or(Error::Length)?;
57 let bounds = Bounds::new(scheme, host, end, path)?;
58 let buffer = array::Plan::try_new(capacity).map_err(|_| Error::Length)?;
59 let slash = origin.slash;
60 Ok(Self {
61 value,
62 prefix: false,
63 slash,
64 buffer,
65 bounds,
66 })
67 }
68
69 pub fn http(value: String) -> Result<Self, Error> {
71 let parsed = target::authority(&value)?;
72 let end = 7usize.checked_add(value.len()).ok_or(Error::Length)?;
73 let capacity = end.checked_add(1).ok_or(Error::Length)?;
74 let bounds = Bounds::new(4, 7 + parsed.host().len(), end, capacity)?;
75 let buffer = array::Plan::try_new(capacity).map_err(|_| Error::Length)?;
76 Ok(Self {
77 value,
78 prefix: true,
79 slash: Slash::Insert,
80 buffer,
81 bounds,
82 })
83 }
84
85 pub fn path(&self) -> &str {
86 match self.slash {
87 Slash::Insert => "/",
88 Slash::Present => {
89 let range = self.bounds.authority_end as usize..self.bounds.path_end as usize;
90 unsafe { target::raw::slice(&self.value, range) }
92 }
93 }
94 }
95
96 pub fn try_build(self) -> Result<Target, alloc::Exhausted> {
99 let mut value = self.value;
100 if value.capacity() != *self.buffer.extent() {
101 let mut output = self.buffer.try_string()?;
102 if self.prefix {
103 output.push_str("http://");
104 }
105 let end = self.bounds.authority_end as usize - output.len();
106 let (head, tail) = unsafe {
108 (
109 target::raw::slice(&value, 0..end),
110 target::raw::slice(&value, end..value.len()),
111 )
112 };
113 output.push_str(head);
114 if self.slash == Slash::Insert {
115 output.push('/');
116 }
117 output.push_str(tail);
118 value = output;
119 } else {
120 if self.prefix {
121 unsafe { target::raw::insert(&mut value, 0, "http://") };
123 }
124 if self.slash == Slash::Insert {
125 unsafe { target::raw::insert(&mut value, self.bounds.authority_end as usize, "/") };
127 }
128 }
129 Ok(self.bounds.build(unsafe { target::raw::boxed(value) }))
131 }
132}