overpass_lib/query/
query.rs1use std::{
2 collections::{HashMap, HashSet},
3 fmt::Write,
4};
5use chrono::{DateTime, Utc};
6use crate::{OverpassQL, OverpassQLError, Set, Bbox, OverpassQLNamed, Namer};
7#[cfg(doc)]
8use crate::{Element};
9
10#[derive(Debug, Clone, Copy, Default)]
14pub enum QueryVerbosity {
15 #[default]
19 Body,
20
21 Ids,
23
24 Skeleton,
26
27 Tags,
29
30 }
32
33impl OverpassQL for QueryVerbosity {
34 fn fmt_oql(&self, f: &mut impl Write) -> Result<(), OverpassQLError> {
35 match self {
36 Self::Body => write!(f, "out;"),
38 Self::Ids => write!(f, "out ids;"),
39 Self::Tags => write!(f, "out tags;"),
40 Self::Skeleton => write!(f, "out skel;"),
41 }?;
43 Ok(())
44 }
45}
46
47#[derive(Debug, Default)]
49pub struct Query<'a> {
50 pub timeout_s: Option<u32>,
53
54 pub max_size: Option<u32>,
57
58 pub search_bbox: Option<Bbox>,
61
62 pub as_of_date: Option<DateTime<Utc>>,
65
66 pub diff: Option<(DateTime<Utc>, Option<DateTime<Utc>>)>,
69
70 pub verbosity: QueryVerbosity,
73
74 pub set: Set<'a>,
76}
77
78impl<'a> From<Set<'a>> for Query<'a> {
79 fn from(value: Set<'a>) -> Self {
80 Self {
81 set: value,
82 ..Default::default()
83 }
84 }
85}
86
87impl<'a> AsRef<Query<'a>> for Query<'a> {
88 fn as_ref(&self) -> &Query<'a> {
89 self
90 }
91}
92
93fn resolve_ordering<'a, 'b>(query_set: &'b Set<'a>)
95-> Result<Vec<&'b Set<'a>>, OverpassQLError>
96where 'a: 'b {
97 let mut refs = evaluate_refs(query_set, HashMap::new());
99
100 let mut back_refs = HashMap::new();
102 for (a, b) in refs.iter() {
103 for c in b {
104 back_refs.entry(*c).or_insert(HashSet::new()).insert(*a);
105 }
106 }
107
108 let mut output = vec![];
109
110 while refs.len() > 0 {
111 let next_outputs = refs
113 .extract_if(|_, v| v.len() == 0)
114 .map(|(k, _)| k)
115 .collect::<Vec<_>>();
116
117 if next_outputs.len() == 0 {
119 return Err(OverpassQLError::CircularReference);
120 }
121
122 for next in next_outputs {
123 output.push(next);
125
126 if let Some(next_refs) = back_refs.remove(next) {
128 for referent in next_refs.iter() {
129 refs.get_mut(referent).unwrap().remove(next);
130 }
131 }
132
133 }
134 }
135
136 Ok(output)
137}
138
139fn evaluate_refs<'a, 'b>(
141 set: &'b Set<'a>,
142 mut refs: HashMap<&'b Set<'a>, HashSet<&'b Set<'a>>>,
143) -> HashMap<&'b Set<'a>, HashSet<&'b Set<'a>>>
144where 'a: 'b {
145 let deps = refs.entry(set).or_insert(HashSet::new());
146 let fresh = set.dependencies()
147 .filter(|s| deps.insert(s))
148 .collect::<Vec<_>>();
149
150 for i in fresh {
151 refs = evaluate_refs(i, refs);
152 }
153
154 refs
155}
156
157impl<'a> OverpassQL for Query<'a> {
158 fn fmt_oql(&self, f: &mut impl Write) -> Result<(), OverpassQLError> {
159 if let Some(d) = self.timeout_s {
160 write!(f, "[timeout:{}]", d)?;
161 }
162 if let Some(s) = self.max_size {
163 write!(f, "[maxsize:{s}]")?;
164 }
165 if let Some(bbox) = self.search_bbox {
166 write!(f, "[bbox:")?;
167 bbox.fmt_oql(f)?;
168 write!(f, "]")?;
169 }
170 if let Some(d) = self.as_of_date {
171 write!(f, r#"[date:"{d}"]"#)?;
172 }
173 if let Some((a, mayb)) = self.diff {
174 if let Some(b) = mayb {
175 write!(f, r#"[diff:"{a}","{b}"]"#)?;
176 } else {
177 write!(f, r#"[diff:"{a}"]"#)?;
178 }
179 }
180 write!(f, "[out:json];")?;
181
182 let mut namer = Namer::new(&self.set);
183 for set in resolve_ordering(&self.set)? {
184 set.fmt_oql_named(f, &mut namer)?;
185 write!(f, ";")?;
186 }
187
188 self.verbosity.fmt_oql(f)
189 }
190}
191
192#[cfg(test)]
193mod test {
194 use std::borrow::Cow;
195 use super::*;
196 use crate::{FilterSet, FilterType, TagFilter};
197
198 #[test]
199 fn resolve_ordering() {
200 let q1 = Set::Filter(FilterSet {
201 filter_type: FilterType::NodeOrWay,
202 tag_filters: HashSet::from([
203 TagFilter::equals("public_transport", "platform"),
204 ]),
205 ..Default::default()
206 });
207 let q2 = Set::Filter(FilterSet {
208 filter_type: FilterType::Node,
209 inputs: HashSet::from([Cow::Borrowed(&q1)]),
210 ..Default::default()
211 });
212
213 assert_eq!(super::resolve_ordering(&q2).unwrap(), vec![&q1, &q2]);
214 }
215
216 #[test]
217 fn fmt_oql() {
218 let q1 = Set::Filter(FilterSet {
219 filter_type: FilterType::NodeOrWay,
220 tag_filters: HashSet::from([
221 TagFilter::equals("public_transport", "platform"),
222 ]),
223 ..Default::default()
224 });
225 let q2 = Set::Filter(FilterSet {
226 filter_type: FilterType::Node,
227 inputs: HashSet::from([Cow::Borrowed(&q1)]),
228 ..Default::default()
229 });
230 let q = Query::from(q2);
231
232 assert_eq!(q.to_oql(), vec![
233 "[out:json];",
234 r#"nw["public_transport"="platform"]->.a;"#,
235 "node.a;",
236 "out;"
237 ].join(""));
238 }
239}