1use crate::ast::{Match, SegmentKind, Service, Version};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Binding {
26 Segment(String),
28 Path(Vec<String>),
30 Unbound,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Part<'p> {
37 Segment(&'p str),
38 UnknownId,
40 UnknownRun,
42}
43
44pub const UNKNOWN_ID: &str = "\u{0}*";
46pub const UNKNOWN_RUN: &str = "\u{0}**";
48
49#[derive(Debug, Clone)]
52pub struct Covering<'a> {
53 pub chain: Vec<&'a Match>,
54 pub bindings: Vec<(String, Binding)>,
55}
56
57impl Covering<'_> {
58 pub fn target(&self) -> &Match {
60 self.chain
61 .last()
62 .expect("a covering has at least one match")
63 }
64}
65
66pub fn covering<'a>(version: Version, service: &'a Service, path: &[&str]) -> Vec<Covering<'a>> {
71 let parts: Vec<Part<'_>> = path.iter().map(|s| Part::Segment(s)).collect();
72 covering_parts(version, service, &parts)
73}
74
75pub fn covering_parts<'a>(
77 version: Version,
78 service: &'a Service,
79 path: &[Part<'_>],
80) -> Vec<Covering<'a>> {
81 let mut found = Vec::new();
82 for m in &service.body.matches {
83 visit(
84 version,
85 m,
86 path,
87 0,
88 &mut Vec::new(),
89 &mut Vec::new(),
90 &mut found,
91 );
92 }
93 found
94}
95
96fn visit<'a>(
97 version: Version,
98 m: &'a Match,
99 path: &[Part<'_>],
100 at: usize,
101 chain: &mut Vec<&'a Match>,
102 bindings: &mut Vec<(String, Binding)>,
103 found: &mut Vec<Covering<'a>>,
104) {
105 chain.push(m);
106 let mut covered = false;
107 for (end, taken) in prefixes(version, m, path, at) {
108 let depth = bindings.len();
109 bindings.extend(taken);
110 if end == path.len() && !covered {
111 covered = true;
112 found.push(Covering {
113 chain: chain.clone(),
114 bindings: bindings.clone(),
115 });
116 }
117 for inner in &m.body.matches {
118 visit(version, inner, path, end, chain, bindings, found);
119 }
120 bindings.truncate(depth);
121 }
122 chain.pop();
123}
124
125fn prefixes(
128 version: Version,
129 m: &Match,
130 path: &[Part<'_>],
131 at: usize,
132) -> Vec<(usize, Vec<(String, Binding)>)> {
133 let mut out = Vec::new();
134 take(version, &m.path, path, at, &mut Vec::new(), &mut out);
135 out
136}
137
138fn take(
139 version: Version,
140 segments: &[crate::ast::Segment],
141 path: &[Part<'_>],
142 at: usize,
143 bound: &mut Vec<(String, Binding)>,
144 out: &mut Vec<(usize, Vec<(String, Binding)>)>,
145) {
146 let Some((first, rest)) = segments.split_first() else {
147 out.push((at, bound.clone()));
148 return;
149 };
150 match &first.kind {
151 SegmentKind::Literal(literal) => {
152 if path.get(at) == Some(&Part::Segment(literal)) {
153 take(version, rest, path, at + 1, bound, out);
154 }
155 }
156 SegmentKind::Wildcard(name) => {
157 let binding = match path.get(at) {
158 Some(Part::Segment(segment)) => Binding::Segment((*segment).to_owned()),
159 Some(Part::UnknownId) => Binding::Unbound,
160 Some(Part::UnknownRun) | None => return,
161 };
162 bound.push((name.clone(), binding));
163 take(version, rest, path, at + 1, bound, out);
164 bound.pop();
165 }
166 SegmentKind::Recursive(name) => {
167 let fewest = match version {
168 Version::V1 => 1,
169 Version::V2 => 0,
170 };
171 for end in at + fewest..=path.len() {
172 let segments = path[at..end]
173 .iter()
174 .map(|part| match part {
175 Part::Segment(s) => (*s).to_owned(),
176 Part::UnknownId => UNKNOWN_ID.to_owned(),
177 Part::UnknownRun => UNKNOWN_RUN.to_owned(),
178 })
179 .collect();
180 bound.push((name.clone(), Binding::Path(segments)));
181 take(version, rest, path, end, bound, out);
182 bound.pop();
183 }
184 }
185 }
186}