Skip to main content

sark_http/h1/head/request/route/
mod.rs

1use std::{num::NonZero, str};
2
3use o3::{buffer, buffer::write};
4use write::{CopyError, SourceExt as _};
5
6use crate::{
7    h1::{field::expect, head::request::pool, switch},
8    request::{self, span},
9};
10
11mod line;
12
13pub(in crate::h1::head::request) use line::Line;
14
15/// Admitted request routing ranges and their immutable source.
16#[derive(PartialEq, Eq)]
17pub struct Route<S> {
18    pub(in crate::h1::head::request) source: S,
19    pub(in crate::h1::head::request) line: Line,
20    pub(in crate::h1::head::request) end: NonZero<u32>,
21    pub(in crate::h1::head::request) offers: switch::Offers,
22}
23
24impl Copy for Route<&[u8]> {}
25
26impl Clone for Route<&[u8]> {
27    fn clone(&self) -> Self {
28        *self
29    }
30}
31
32impl<S> Route<S> {
33    pub(in crate::h1::head::request) const fn new(
34        source: S,
35        line: Line,
36        end: NonZero<u32>,
37        offers: switch::Offers,
38    ) -> Self {
39        Self {
40            source,
41            line,
42            end,
43            offers,
44        }
45    }
46
47    pub fn into_source(self) -> S {
48        self.source
49    }
50
51    pub fn transition(&self) -> switch::Intent {
52        self.line.transition()
53    }
54
55    pub fn take_expect(&mut self) -> expect::Decision {
56        self.line.take_expect()
57    }
58
59    pub const fn head_end(&self) -> NonZero<u32> {
60        self.end
61    }
62}
63
64impl<'d> Route<pool::Buf<'d>> {
65    pub fn seal(self) -> pool::Meta<'d> {
66        let route = self.as_ref();
67        let target = &route.line.target;
68        let layout = pool::Layout::new(
69            [
70                route.method().len() as u16,
71                target.start() as u16,
72                target.end() as u16,
73                self.end.get() as u16,
74            ],
75            pool::Source::Head,
76            route.transition(),
77        );
78        let mut bytes = self.source.bytes;
79        unsafe {
80            bytes
81                .as_mut_slice()
82                .get_unchecked_mut(..pool::Layout::BYTES)
83                .copy_from_slice(&layout.encode());
84        }
85        pool::Meta { bytes }
86    }
87}
88
89impl<S: buffer::raw::Source> Route<S> {
90    pub fn as_ref(&self) -> Route<&[u8]> {
91        Route {
92            source: self.source.as_ref(),
93            line: self.line,
94            end: self.end,
95            offers: self.offers,
96        }
97    }
98
99    pub fn try_copy<D>(&self, target: D) -> Result<Route<D>, (D, CopyError<D::Error>)>
100    where
101        D: buffer::raw::Source + write::Sink,
102    {
103        let target = target.try_copy(self.source.as_ref())?;
104        Ok(Route {
105            source: target,
106            line: self.line,
107            end: self.end,
108            offers: self.offers,
109        })
110    }
111}
112
113impl<S: write::Sink> Route<S> {
114    pub fn try_extend(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
115        self.source.write_slice(bytes)
116    }
117}
118
119impl<'a> Route<&'a [u8]> {
120    pub const fn bytes(self) -> &'a [u8] {
121        self.source
122    }
123
124    pub fn body(self) -> &'a [u8] {
125        unsafe { self.source.get_unchecked(self.end.get() as usize..) }
126    }
127
128    pub fn method(self) -> &'a str {
129        self.text(self.line.method())
130    }
131
132    pub fn target(self) -> &'a str {
133        self.text(span::Span::from_target(self.line.target))
134    }
135
136    pub fn authority(self) -> &'a str {
137        self.text(self.line.auth)
138    }
139
140    pub fn route_path(self) -> Option<&'a str> {
141        match self.line.target.form() {
142            request::Form::Root => Some("/"),
143            request::Form::Asterisk | request::Form::Authority => Some("*"),
144            request::Form::Path | request::Form::Extended => {
145                self.line.route.map(|path| self.text(path))
146            }
147        }
148    }
149
150    fn text(self, span: span::Span) -> &'a str {
151        unsafe { str::from_utf8_unchecked(self.source.get_unchecked(span.start()..span.end())) }
152    }
153}
154
155const _: () = assert!(size_of::<Route<&[u8]>>() == 7 * size_of::<usize>());