1use crate::error;
2use crate::result::ParseResult;
3use crate::types::FieldSpec;
4use pyo3::prelude::*;
5use pyo3::IntoPyObjectExt;
6use std::collections::HashMap;
7
8pub struct MatchInit {
10 pub pattern: String,
11 pub field_specs: Vec<FieldSpec>,
12 pub field_names: Vec<Option<String>>,
13 pub normalized_names: Vec<Option<String>>,
14 pub captures: Vec<Option<String>>,
15 pub named_captures: HashMap<String, String>,
16 pub span: (usize, usize),
17 pub field_spans: HashMap<String, (usize, usize)>,
18}
19
20#[pyclass]
22pub struct Match {
23 #[pyo3(get)]
24 pattern: String,
25 field_specs: Vec<FieldSpec>,
26 field_names: Vec<Option<String>>,
27 normalized_names: Vec<Option<String>>,
28 captures: Vec<Option<String>>, named_captures: HashMap<String, String>, #[pyo3(get)]
31 pub span: (usize, usize),
32 field_spans: HashMap<String, (usize, usize)>, }
34
35#[pymethods]
36impl Match {
37 #[pyo3(signature = (*, extra_types=None))]
39 fn evaluate_result(
40 &self,
41 py: Python,
42 extra_types: Option<HashMap<String, PyObject>>,
43 ) -> PyResult<PyObject> {
44 let custom_converters = extra_types.unwrap_or_default();
45 let mut fixed = Vec::new();
46 let mut named: HashMap<String, PyObject> = HashMap::new();
47
48 for (i, spec) in self.field_specs.iter().enumerate() {
50 let value_str =
51 if let Some(norm_name) = self.normalized_names.get(i).and_then(|n| n.as_ref()) {
52 self.named_captures
53 .get(norm_name.as_str())
54 .map(|s| s.as_str())
55 } else {
56 self.captures
57 .get(i)
58 .and_then(|s| s.as_ref())
59 .map(|s| s.as_str())
60 };
61
62 if let Some(value_str) = value_str {
63 let converted = crate::types::conversion::convert_value(
64 spec,
65 value_str,
66 py,
67 &custom_converters,
68 )?;
69
70 if let Some(original_name) = self.field_names.get(i).and_then(|n| n.as_ref()) {
71 if original_name.contains('[') {
73 let path = crate::parser::parse_field_path(original_name);
75 crate::parser::matching::insert_nested_dict(
76 &mut named, &path, converted, py,
77 )?;
78 } else {
79 if let Some(existing_value) = named.get(original_name.as_str()) {
82 let existing_obj = existing_value.clone_ref(py);
83 let converted_obj = converted.clone_ref(py);
84 let are_equal: bool = existing_obj
85 .bind(py)
86 .eq(converted_obj.bind(py))
87 .unwrap_or(false);
88 if !are_equal {
89 return Err(error::repeated_name_error(original_name));
90 }
91 }
92 named.insert(original_name.clone(), converted);
93 }
94 } else {
95 fixed.push(converted);
96 }
97 }
98 }
99
100 let parse_result =
101 ParseResult::new_with_spans(fixed, named, self.span, self.field_spans.clone());
102 Py::new(py, parse_result)?.into_py_any(py)
104 }
105}
106
107impl Match {
108 pub fn new(init: MatchInit) -> Self {
109 Self {
110 pattern: init.pattern,
111 field_specs: init.field_specs,
112 field_names: init.field_names,
113 normalized_names: init.normalized_names,
114 captures: init.captures,
115 named_captures: init.named_captures,
116 span: init.span,
117 field_spans: init.field_spans,
118 }
119 }
120}