Skip to content

Commit 5bbdcfd

Browse files
authored
make PythonParser generic around cache mode, not its functions (#79)
1 parent efad44e commit 5bbdcfd

File tree

1 file changed

+41
-40
lines changed

1 file changed

+41
-40
lines changed

src/python.rs

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::marker::PhantomData;
2+
13
use pyo3::exceptions::PyValueError;
24
use pyo3::ffi;
35
use pyo3::prelude::*;
@@ -31,45 +33,52 @@ pub fn python_parse<'py>(
3133
cache_mode: StringCacheMode,
3234
allow_partial: bool,
3335
) -> JsonResult<Bound<'py, PyAny>> {
34-
let mut python_parser = PythonParser {
35-
parser: Parser::new(json_data),
36-
tape: Tape::default(),
37-
recursion_limit: DEFAULT_RECURSION_LIMIT,
38-
allow_inf_nan,
39-
allow_partial,
40-
};
41-
42-
let peek = python_parser.parser.peek()?;
43-
let v = match cache_mode {
44-
StringCacheMode::All => python_parser.py_take_value::<StringCacheAll>(py, peek)?,
45-
StringCacheMode::Keys => python_parser.py_take_value::<StringCacheKeys>(py, peek)?,
46-
StringCacheMode::None => python_parser.py_take_value::<StringNoCache>(py, peek)?,
47-
};
48-
if !allow_partial {
49-
python_parser.parser.finish()?;
36+
match cache_mode {
37+
StringCacheMode::All => PythonParser::<StringCacheAll>::parse(py, json_data, allow_inf_nan, allow_partial),
38+
StringCacheMode::Keys => PythonParser::<StringCacheKeys>::parse(py, json_data, allow_inf_nan, allow_partial),
39+
StringCacheMode::None => PythonParser::<StringNoCache>::parse(py, json_data, allow_inf_nan, allow_partial),
5040
}
51-
Ok(v)
5241
}
5342

5443
/// Map a `JsonError` to a `PyErr` which can be raised as an exception in Python as a `ValueError`.
5544
pub fn map_json_error(json_data: &[u8], json_error: &JsonError) -> PyErr {
5645
PyValueError::new_err(json_error.description(json_data))
5746
}
5847

59-
struct PythonParser<'j> {
48+
struct PythonParser<'j, StringCache> {
49+
_string_cache: PhantomData<StringCache>,
6050
parser: Parser<'j>,
6151
tape: Tape,
6252
recursion_limit: u8,
6353
allow_inf_nan: bool,
6454
allow_partial: bool,
6555
}
6656

67-
impl<'j> PythonParser<'j> {
68-
fn py_take_value<'py, StringCache: StringMaybeCache>(
69-
&mut self,
57+
impl<'j, StringCache: StringMaybeCache> PythonParser<'j, StringCache> {
58+
fn parse<'py>(
7059
py: Python<'py>,
71-
peek: Peek,
60+
json_data: &[u8],
61+
allow_inf_nan: bool,
62+
allow_partial: bool,
7263
) -> JsonResult<Bound<'py, PyAny>> {
64+
let mut slf = PythonParser {
65+
_string_cache: PhantomData::<StringCache>,
66+
parser: Parser::new(json_data),
67+
tape: Tape::default(),
68+
recursion_limit: DEFAULT_RECURSION_LIMIT,
69+
allow_inf_nan,
70+
allow_partial,
71+
};
72+
73+
let peek = slf.parser.peek()?;
74+
let v = slf.py_take_value(py, peek)?;
75+
if !allow_partial {
76+
slf.parser.finish()?;
77+
}
78+
Ok(v)
79+
}
80+
81+
fn py_take_value<'py>(&mut self, py: Python<'py>, peek: Peek) -> JsonResult<Bound<'py, PyAny>> {
7382
match peek {
7483
Peek::Null => {
7584
self.parser.consume_null()?;
@@ -95,7 +104,7 @@ impl<'j> PythonParser<'j> {
95104
};
96105

97106
let mut vec: SmallVec<[Bound<'_, PyAny>; 8]> = SmallVec::with_capacity(8);
98-
if let Err(e) = self._parse_array::<StringCache>(py, peek_first, &mut vec) {
107+
if let Err(e) = self._parse_array(py, peek_first, &mut vec) {
99108
if !self._allow_partial_err(&e) {
100109
return Err(e);
101110
}
@@ -105,7 +114,7 @@ impl<'j> PythonParser<'j> {
105114
}
106115
Peek::Object => {
107116
let dict = PyDict::new_bound(py);
108-
if let Err(e) = self._parse_object::<StringCache>(py, &dict) {
117+
if let Err(e) = self._parse_object(py, &dict) {
109118
if !self._allow_partial_err(&e) {
110119
return Err(e);
111120
}
@@ -132,26 +141,22 @@ impl<'j> PythonParser<'j> {
132141
}
133142
}
134143

135-
fn _parse_array<'py, StringCache: StringMaybeCache>(
144+
fn _parse_array<'py>(
136145
&mut self,
137146
py: Python<'py>,
138147
peek_first: Peek,
139148
vec: &mut SmallVec<[Bound<'py, PyAny>; 8]>,
140149
) -> JsonResult<()> {
141-
let v = self._check_take_value::<StringCache>(py, peek_first)?;
150+
let v = self._check_take_value(py, peek_first)?;
142151
vec.push(v);
143152
while let Some(peek) = self.parser.array_step()? {
144-
let v = self._check_take_value::<StringCache>(py, peek)?;
153+
let v = self._check_take_value(py, peek)?;
145154
vec.push(v);
146155
}
147156
Ok(())
148157
}
149158

150-
fn _parse_object<'py, StringCache: StringMaybeCache>(
151-
&mut self,
152-
py: Python<'py>,
153-
dict: &Bound<'py, PyDict>,
154-
) -> JsonResult<()> {
159+
fn _parse_object<'py>(&mut self, py: Python<'py>, dict: &Bound<'py, PyDict>) -> JsonResult<()> {
155160
let set_item = |key: Bound<'py, PyString>, value: Bound<'py, PyAny>| {
156161
let r = unsafe { ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr()) };
157162
// AFAIK this shouldn't happen since the key will always be a string which is hashable
@@ -164,12 +169,12 @@ impl<'j> PythonParser<'j> {
164169
if let Some(first_key) = self.parser.object_first::<StringDecoder>(&mut self.tape)? {
165170
let first_key = StringCache::get_key(py, first_key.as_str(), first_key.ascii_only());
166171
let peek = self.parser.peek()?;
167-
let first_value = self._check_take_value::<StringCache>(py, peek)?;
172+
let first_value = self._check_take_value(py, peek)?;
168173
set_item(first_key, first_value);
169174
while let Some(key) = self.parser.object_step::<StringDecoder>(&mut self.tape)? {
170175
let key = StringCache::get_key(py, key.as_str(), key.ascii_only());
171176
let peek = self.parser.peek()?;
172-
let value = self._check_take_value::<StringCache>(py, peek)?;
177+
let value = self._check_take_value(py, peek)?;
173178
set_item(key, value);
174179
}
175180
}
@@ -192,17 +197,13 @@ impl<'j> PythonParser<'j> {
192197
}
193198
}
194199

195-
fn _check_take_value<'py, StringCache: StringMaybeCache>(
196-
&mut self,
197-
py: Python<'py>,
198-
peek: Peek,
199-
) -> JsonResult<Bound<'py, PyAny>> {
200+
fn _check_take_value<'py>(&mut self, py: Python<'py>, peek: Peek) -> JsonResult<Bound<'py, PyAny>> {
200201
self.recursion_limit = match self.recursion_limit.checked_sub(1) {
201202
Some(limit) => limit,
202203
None => return json_err!(RecursionLimitExceeded, self.parser.index),
203204
};
204205

205-
let r = self.py_take_value::<StringCache>(py, peek);
206+
let r = self.py_take_value(py, peek);
206207

207208
self.recursion_limit += 1;
208209
r

0 commit comments

Comments
 (0)