@@ -11,7 +11,7 @@ use jiter::{JsonObject, JsonValue};
1111use crate :: build_tools:: py_schema_err;
1212use crate :: errors:: { py_err_string, ErrorType , LocItem , Location , ToErrorValue , ValError , ValLineError , ValResult } ;
1313use crate :: input:: StringMapping ;
14- use crate :: tools:: { extract_i64, py_err} ;
14+ use crate :: tools:: { extract_i64, mapping_get , py_err} ;
1515
1616/// Used for getting items from python dicts, python objects, or JSON objects, in different ways
1717#[ derive( Debug ) ]
@@ -89,44 +89,12 @@ impl LookupKey {
8989 pub fn py_get_dict_item < ' py , ' s > (
9090 & ' s self ,
9191 dict : & Bound < ' py , PyDict > ,
92- ) -> ValResult < Option < ( & ' s LookupPath , Bound < ' py , PyAny > ) > > {
93- match self {
94- Self :: Simple ( path) => match dict. get_item ( & path. first_item . py_key ) ? {
95- Some ( value) => {
96- debug_assert ! ( path. rest. is_empty( ) ) ;
97- Ok ( Some ( ( path, value) ) )
98- }
99- None => Ok ( None ) ,
100- } ,
101- Self :: Choice { path1, path2, .. } => match dict. get_item ( & path1. first_item . py_key ) ? {
102- Some ( value) => {
103- debug_assert ! ( path1. rest. is_empty( ) ) ;
104- Ok ( Some ( ( path1, value) ) )
105- }
106- None => match dict. get_item ( & path2. first_item . py_key ) ? {
107- Some ( value) => {
108- debug_assert ! ( path2. rest. is_empty( ) ) ;
109- Ok ( Some ( ( path2, value) ) )
110- }
111- None => Ok ( None ) ,
112- } ,
113- } ,
114- Self :: PathChoices ( path_choices) => {
115- for path in path_choices {
116- let Some ( first_value) = dict. get_item ( & path. first_item . py_key ) ? else {
117- continue ;
118- } ;
119- // iterate over the path and plug each value into the py_any from the last step,
120- // this could just be a loop but should be somewhat faster with a functional design
121- if let Some ( v) = path. rest . iter ( ) . try_fold ( first_value, |d, loc| loc. py_get_item ( & d) ) {
122- // Successfully found an item, return it
123- return Ok ( Some ( ( path, v) ) ) ;
124- }
125- }
126- // got to the end of path_choices, without a match, return None
127- Ok ( None )
128- }
129- }
92+ ) -> PyResult < Option < ( & ' s LookupPath , Bound < ' py , PyAny > ) > > {
93+ self . get_impl (
94+ dict,
95+ |dict, path| dict. get_item ( & path. py_key ) ,
96+ |d, loc| Ok ( loc. py_get_item ( & d) ) ,
97+ )
13098 }
13199
132100 pub fn py_get_string_mapping_item < ' py , ' s > (
@@ -144,94 +112,23 @@ impl LookupKey {
144112 pub fn py_get_mapping_item < ' py , ' s > (
145113 & ' s self ,
146114 dict : & Bound < ' py , PyMapping > ,
147- ) -> ValResult < Option < ( & ' s LookupPath , Bound < ' py , PyAny > ) > > {
148- match self {
149- Self :: Simple ( path) => match dict. get_item ( & path. first_item . py_key ) {
150- Ok ( value) => {
151- debug_assert ! ( path. rest. is_empty( ) ) ;
152- Ok ( Some ( ( path, value) ) )
153- }
154- _ => Ok ( None ) ,
155- } ,
156- Self :: Choice { path1, path2, .. } => match dict. get_item ( & path1. first_item . py_key ) {
157- Ok ( value) => {
158- debug_assert ! ( path1. rest. is_empty( ) ) ;
159- Ok ( Some ( ( path1, value) ) )
160- }
161- _ => match dict. get_item ( & path2. first_item . py_key ) {
162- Ok ( value) => {
163- debug_assert ! ( path2. rest. is_empty( ) ) ;
164- Ok ( Some ( ( path2, value) ) )
165- }
166- _ => Ok ( None ) ,
167- } ,
168- } ,
169- Self :: PathChoices ( path_choices) => {
170- for path in path_choices {
171- let Some ( first_value) = dict. get_item ( & path. first_item . py_key ) . ok ( ) else {
172- continue ;
173- } ;
174- // iterate over the path and plug each value into the py_any from the last step,
175- // this could just be a loop but should be somewhat faster with a functional design
176- if let Some ( v) = path. rest . iter ( ) . try_fold ( first_value, |d, loc| loc. py_get_item ( & d) ) {
177- // Successfully found an item, return it
178- return Ok ( Some ( ( path, v) ) ) ;
179- }
180- }
181- // got to the end of path_choices, without a match, return None
182- Ok ( None )
183- }
184- }
115+ ) -> PyResult < Option < ( & ' s LookupPath , Bound < ' py , PyAny > ) > > {
116+ self . get_impl (
117+ dict,
118+ |dict, path| mapping_get ( dict, & path. py_key ) ,
119+ |d, loc| Ok ( loc. py_get_item ( & d) ) ,
120+ )
185121 }
186122
187123 pub fn simple_py_get_attr < ' py , ' s > (
188124 & ' s self ,
189125 obj : & Bound < ' py , PyAny > ,
190126 ) -> PyResult < Option < ( & ' s LookupPath , Bound < ' py , PyAny > ) > > {
191- match self {
192- Self :: Simple ( path) => match py_get_attrs ( obj, & path. first_item . py_key ) ? {
193- Some ( value) => {
194- debug_assert ! ( path. rest. is_empty( ) ) ;
195- Ok ( Some ( ( path, value) ) )
196- }
197- None => Ok ( None ) ,
198- } ,
199- Self :: Choice { path1, path2, .. } => match py_get_attrs ( obj, & path1. first_item . py_key ) ? {
200- Some ( value) => {
201- debug_assert ! ( path1. rest. is_empty( ) ) ;
202- Ok ( Some ( ( path1, value) ) )
203- }
204- None => match py_get_attrs ( obj, & path2. first_item . py_key ) ? {
205- Some ( value) => {
206- debug_assert ! ( path2. rest. is_empty( ) ) ;
207- Ok ( Some ( ( path2, value) ) )
208- }
209- None => Ok ( None ) ,
210- } ,
211- } ,
212- Self :: PathChoices ( path_choices) => {
213- ' outer: for path in path_choices {
214- // similar to above, but using `py_get_attrs`, we can't use try_fold because of the extra Err
215- // so we have to loop manually
216- let Some ( mut v) = path. first_item . py_get_attrs ( obj) ? else {
217- continue ;
218- } ;
219- for loc in & path. rest {
220- v = match loc. py_get_attrs ( & v) {
221- Ok ( Some ( v) ) => v,
222- Ok ( None ) => {
223- continue ' outer;
224- }
225- Err ( e) => return Err ( e) ,
226- }
227- }
228- // Successfully found an item, return it
229- return Ok ( Some ( ( path, v) ) ) ;
230- }
231- // got to the end of path_choices, without a match, return None
232- Ok ( None )
233- }
234- }
127+ self . get_impl (
128+ obj,
129+ |obj, path| py_get_attrs ( obj, & path. py_key ) ,
130+ |d, loc| loc. py_get_attrs ( & d) ,
131+ )
235132 }
236133
237134 pub fn py_get_attr < ' py , ' s > (
@@ -324,6 +221,57 @@ impl LookupKey {
324221 }
325222 }
326223
224+ fn get_impl < ' s , ' a , SourceT , OutputT : ' a > (
225+ & ' s self ,
226+ source : & ' a SourceT ,
227+ lookup : impl Fn ( & ' a SourceT , & ' s PathItemString ) -> PyResult < Option < OutputT > > ,
228+ nested_lookup : impl Fn ( OutputT , & ' s PathItem ) -> PyResult < Option < OutputT > > ,
229+ ) -> PyResult < Option < ( & ' s LookupPath , OutputT ) > > {
230+ match self {
231+ Self :: Simple ( path) => match lookup ( source, & path. first_item ) ? {
232+ Some ( value) => {
233+ debug_assert ! ( path. rest. is_empty( ) ) ;
234+ Ok ( Some ( ( path, value) ) )
235+ }
236+ None => Ok ( None ) ,
237+ } ,
238+ Self :: Choice { path1, path2, .. } => match lookup ( source, & path1. first_item ) ? {
239+ Some ( value) => {
240+ debug_assert ! ( path1. rest. is_empty( ) ) ;
241+ Ok ( Some ( ( path1, value) ) )
242+ }
243+ None => match lookup ( source, & path2. first_item ) ? {
244+ Some ( value) => {
245+ debug_assert ! ( path2. rest. is_empty( ) ) ;
246+ Ok ( Some ( ( path2, value) ) )
247+ }
248+ None => Ok ( None ) ,
249+ } ,
250+ } ,
251+ Self :: PathChoices ( path_choices) => {
252+ ' choices: for path in path_choices {
253+ let Some ( mut value) = lookup ( source, & path. first_item ) ? else {
254+ continue ;
255+ } ;
256+
257+ // iterate over the path and plug each value into the value from the last step
258+ for loc in & path. rest {
259+ value = match nested_lookup ( value, loc) {
260+ Ok ( Some ( v) ) => v,
261+ // this choice did not match, try the next one
262+ Ok ( None ) => continue ' choices,
263+ Err ( e) => return Err ( e) ,
264+ }
265+ }
266+ // Successfully found an item, return it
267+ return Ok ( Some ( ( path, value) ) ) ;
268+ }
269+ // got to the end of path_choices, without a match, return None
270+ Ok ( None )
271+ }
272+ }
273+ }
274+
327275 pub fn error (
328276 & self ,
329277 error_type : ErrorType ,
0 commit comments