dcbor_pattern/pattern/structure/
map_pattern.rs1use std::ops::RangeBounds;
2
3use dcbor::prelude::*;
4
5use crate::{
6    Interval,
7    pattern::{Matcher, Path, Pattern, vm::Instr},
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum MapPattern {
13    Any,
15    Constraints(Vec<(Pattern, Pattern)>),
18    Length(Interval),
20}
21
22impl MapPattern {
23    pub fn any() -> Self {
25        MapPattern::Any
26    }
27
28    pub fn with_key_value_constraints(
31        constraints: Vec<(Pattern, Pattern)>,
32    ) -> Self {
33        MapPattern::Constraints(constraints)
34    }
35
36    pub fn with_length(length: usize) -> Self {
39        MapPattern::Length(Interval::new(length..=length))
40    }
41
42    pub fn with_length_range<R: RangeBounds<usize>>(range: R) -> Self {
45        MapPattern::Length(Interval::new(range))
46    }
47
48    pub fn with_length_interval(interval: Interval) -> Self {
51        MapPattern::Length(interval)
52    }
53}
54
55impl Matcher for MapPattern {
56    fn paths(&self, haystack: &CBOR) -> Vec<Path> {
57        match haystack.as_case() {
59            CBORCase::Map(map) => {
60                match self {
61                    MapPattern::Any => {
62                        vec![vec![haystack.clone()]]
64                    }
65                    MapPattern::Constraints(constraints) => {
66                        for (key_pattern, value_pattern) in constraints {
68                            let mut found_match = false;
69                            for (key, value) in map.iter() {
70                                if key_pattern.matches(key)
71                                    && value_pattern.matches(value)
72                                {
73                                    found_match = true;
74                                    break;
75                                }
76                            }
77                            if !found_match {
78                                return vec![];
79                            }
80                        }
81                        vec![vec![haystack.clone()]]
82                    }
83                    MapPattern::Length(interval) => {
84                        if interval.contains(map.len()) {
85                            vec![vec![haystack.clone()]]
86                        } else {
87                            vec![]
88                        }
89                    }
90                }
91            }
92            _ => {
93                vec![]
95            }
96        }
97    }
98
99    fn compile(
100        &self,
101        code: &mut Vec<Instr>,
102        literals: &mut Vec<Pattern>,
103        captures: &mut Vec<String>,
104    ) {
105        self.collect_capture_names(captures);
107
108        let idx = literals.len();
109        literals.push(Pattern::Structure(
110            crate::pattern::StructurePattern::Map(self.clone()),
111        ));
112        code.push(Instr::MatchStructure(idx));
113    }
114
115    fn collect_capture_names(&self, names: &mut Vec<String>) {
116        match self {
117            MapPattern::Any => {
118                }
120            MapPattern::Constraints(constraints) => {
121                for (key_pattern, value_pattern) in constraints {
123                    key_pattern.collect_capture_names(names);
124                    value_pattern.collect_capture_names(names);
125                }
126            }
127            MapPattern::Length(_) => {
128                }
130        }
131    }
132
133    fn paths_with_captures(
134        &self,
135        haystack: &CBOR,
136    ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
137        let CBORCase::Map(map) = haystack.as_case() else {
139            return (vec![], std::collections::HashMap::new());
140        };
141
142        match self {
143            MapPattern::Any => {
144                (vec![vec![haystack.clone()]], std::collections::HashMap::new())
146            }
147            MapPattern::Constraints(constraints) => {
148                let mut all_captures = std::collections::HashMap::new();
150                let mut all_constraints_satisfied = true;
151
152                for (key_pattern, value_pattern) in constraints {
153                    let mut constraint_satisfied = false;
154
155                    for (key, value) in map.iter() {
156                        let (key_paths, key_captures) =
157                            key_pattern.paths_with_captures(key);
158                        let (value_paths, value_captures) =
159                            value_pattern.paths_with_captures(value);
160
161                        if !key_paths.is_empty() && !value_paths.is_empty() {
162                            constraint_satisfied = true;
163
164                            for (name, capture_paths) in key_captures {
166                                let updated_paths: Vec<Path> = capture_paths
167                                    .iter()
168                                    .map(|_capture_path| {
169                                        vec![haystack.clone(), key.clone()]
170                                    })
171                                    .collect();
172                                all_captures
173                                    .entry(name)
174                                    .or_insert_with(Vec::new)
175                                    .extend(updated_paths);
176                            }
177
178                            for (name, capture_paths) in value_captures {
180                                let updated_paths: Vec<Path> = capture_paths
181                                    .iter()
182                                    .map(|_capture_path| {
183                                        vec![haystack.clone(), value.clone()]
184                                    })
185                                    .collect();
186                                all_captures
187                                    .entry(name)
188                                    .or_insert_with(Vec::new)
189                                    .extend(updated_paths);
190                            }
191                            break; }
193                    }
194
195                    if !constraint_satisfied {
196                        all_constraints_satisfied = false;
197                        break;
198                    }
199                }
200
201                if all_constraints_satisfied {
202                    (vec![vec![haystack.clone()]], all_captures)
203                } else {
204                    (vec![], all_captures)
205                }
206            }
207            _ => {
208                (self.paths(haystack), std::collections::HashMap::new())
210            }
211        }
212    }
213}
214
215impl std::fmt::Display for MapPattern {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        match self {
218            MapPattern::Any => write!(f, "{{*}}"),
219            MapPattern::Constraints(constraints) => {
220                write!(f, "{{")?;
221                for (i, (key_pattern, value_pattern)) in
222                    constraints.iter().enumerate()
223                {
224                    if i > 0 {
225                        write!(f, ", ")?;
226                    }
227                    write!(f, "{}: {}", key_pattern, value_pattern)?;
228                }
229                write!(f, "}}")
230            }
231            MapPattern::Length(interval) => {
232                write!(f, "{{{}}}", interval)
233            }
234        }
235    }
236}