1use o3::buffer::Shared;
2use sark_core::http::LocalFrameBytes;
3
4use crate::Result;
5use crate::body::InlineToken;
6use crate::error::Fail;
7use crate::scan::Scan;
8
9pub struct Parse;
10
11impl Parse {
12 pub fn local(owner: Shared, input: &[u8], idx: &mut usize) -> Result<LocalFrameBytes> {
13 if *idx >= input.len() {
14 return Err(Fail::bad());
15 }
16 if input[*idx] == b'"' {
17 let start = *idx;
18 let _ = Scan::str_slice(input, idx)?;
19 let raw_start = start.saturating_add(1);
20 let raw_end = idx.saturating_sub(1);
21 if raw_end < raw_start {
22 return Err(Fail::bad());
23 }
24 let mut esc = raw_start;
25 while esc < raw_end {
26 if input[esc] == b'\\' {
27 let decoded = Scan::decode_str(&input[raw_start..raw_end])?;
28 return Ok(LocalFrameBytes::from_shared(Shared::from(decoded)));
29 }
30 esc += 1;
31 }
32 return Ok(LocalFrameBytes::from_shared_range(
33 owner,
34 raw_start..raw_end,
35 ));
36 }
37 let start = *idx;
38 Scan::skip_value(input, idx)?;
39 let end = *idx;
40 if end <= start {
41 return Err(Fail::bad());
42 }
43 Ok(LocalFrameBytes::from_shared_range(owner, start..end))
44 }
45
46 pub fn empty_local() -> LocalFrameBytes {
47 LocalFrameBytes::from_shared(Shared::new())
48 }
49
50 pub fn local_plain(owner: Shared, input: &[u8], idx: &mut usize) -> Result<LocalFrameBytes> {
51 Scan::expect_byte(input, idx, b'"')?;
52 let start = *idx;
53 while *idx < input.len() {
54 match input[*idx] {
55 b'"' => {
56 let end = *idx;
57 *idx += 1;
58 return Ok(LocalFrameBytes::from_shared_range(owner, start..end));
59 }
60 b'\\' => return Err(Fail::bad()),
61 _ => *idx += 1,
62 }
63 }
64 Err(Fail::bad())
65 }
66
67 pub fn inline_plain<const N: usize>(input: &[u8], idx: &mut usize) -> Result<InlineToken<N>> {
68 Scan::expect_byte(input, idx, b'"')?;
69 let mut out = InlineToken::<N>::new();
70 while *idx < input.len() {
71 match input[*idx] {
72 b'"' => {
73 *idx += 1;
74 return Ok(out);
75 }
76 b'\\' => return Err(Fail::bad()),
77 b => {
78 out.push(b)?;
79 *idx += 1;
80 }
81 }
82 }
83 Err(Fail::bad())
84 }
85
86 pub fn local_raw(owner: Shared, input: &[u8], idx: &mut usize) -> Result<LocalFrameBytes> {
87 let start = *idx;
88 while *idx < input.len() {
89 match input[*idx] {
90 b',' | b'}' | b']' => break,
91 _ => *idx += 1,
92 }
93 }
94 let end = *idx;
95 if end <= start {
96 return Err(Fail::bad());
97 }
98 Ok(LocalFrameBytes::from_shared_range(owner, start..end))
99 }
100
101 pub fn inline_raw<const N: usize>(input: &[u8], idx: &mut usize) -> Result<InlineToken<N>> {
102 let mut out = InlineToken::<N>::new();
103 while *idx < input.len() {
104 match input[*idx] {
105 b',' | b'}' | b']' => break,
106 b => {
107 out.push(b)?;
108 *idx += 1;
109 }
110 }
111 }
112 if out.is_empty() {
113 return Err(Fail::bad());
114 }
115 Ok(out)
116 }
117
118 pub fn u64(input: &[u8], idx: &mut usize) -> Result<u64> {
119 if *idx >= input.len() {
120 return Err(Fail::bad());
121 }
122 let mut value = 0u64;
123 let mut seen = false;
124 while *idx < input.len() {
125 let b = input[*idx];
126 if !b.is_ascii_digit() {
127 break;
128 }
129 value = value
130 .checked_mul(10)
131 .and_then(|v| v.checked_add((b - b'0') as u64))
132 .ok_or_else(Fail::bad)?;
133 *idx += 1;
134 seen = true;
135 }
136 if !seen {
137 return Err(Fail::bad());
138 }
139 Ok(value)
140 }
141
142 pub fn bool(input: &[u8], idx: &mut usize) -> Result<bool> {
143 if input.get(*idx..(*idx + 4)) == Some(b"true") {
144 *idx += 4;
145 return Ok(true);
146 }
147 if input.get(*idx..(*idx + 5)) == Some(b"false") {
148 *idx += 5;
149 return Ok(false);
150 }
151 Err(Fail::bad())
152 }
153}