1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{format_ident, ToTokens};
4use quote_into::quote_into;
5
6#[derive(Debug, Clone)]
7struct Field {
8 ident: syn::Ident,
9 ty: syn::Path,
10 key: String,
11 auto_encode: bool,
12 auto_decode: bool,
13}
14
15impl syn::parse::Parse for Field {
16 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
17 let mut auto_encode = true;
18 let mut auto_decode = true;
19
20 let attrs = input.call(syn::Attribute::parse_outer)?;
21 for attr in attrs {
22 if let syn::Meta::Path(mp) = attr.meta {
23 match mp.to_token_stream().to_string().as_str() {
24 "no_decode" => auto_decode = false,
25 "no_encode" => auto_encode = false,
26 _ => {}
27 }
28 }
29 }
30
31 let ident: syn::Ident = input.parse()?;
32 input.parse::<syn::Token![:]>()?;
33 let ty: syn::Path = input.parse()?;
34
35 let key = ident.to_string();
36 if key == "id" {
37 return Err(syn::Error::new(input.span(), "key `id` is reserved"));
38 }
39 Ok(Self { ident, ty, key, auto_decode, auto_encode })
40 }
41}
42
43#[derive(Debug)]
44struct Layer {
45 ident: syn::Ident,
46 name: syn::Ident,
47 fields: Vec<Field>,
48}
49
50impl syn::parse::Parse for Layer {
51 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
52 let name = input.parse::<syn::Ident>()?;
53
54 let mut layer = Self {
55 ident: format_ident!("Point{}", to_camelcase(&name.to_string())),
56 name,
57 fields: Vec::new(),
58 };
59
60 input.parse::<syn::Token![:]>()?;
61
62 let content;
63 syn::braced!(content in input);
64 let fields = content.parse_terminated(Field::parse, syn::Token![,])?;
65 layer.fields = fields.iter().cloned().collect();
66
67 Ok(layer)
68 }
69}
70
71#[derive(Debug)]
72struct Tile {
73 layers: Vec<Layer>,
74}
75
76impl syn::parse::Parse for Tile {
77 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
78 let mut layers = Vec::<Layer>::new();
79 loop {
80 if input.is_empty() {
81 break;
82 }
83
84 let layer: Layer = input.parse()?;
85 layers.push(layer);
86
87 if input.is_empty() {
88 break;
89 }
90 input.parse::<syn::Token![,]>()?;
91 }
92
93 Ok(Self { layers })
94 }
95}
96
97#[proc_macro]
98pub fn mapack(code: TokenStream) -> TokenStream {
99 let tile = syn::parse_macro_input!(code as Tile);
100 let mut s = TokenStream2::new();
101 let ci = crate_ident();
102
103 quote_into! {s +=
104 #{
105 for layer in tile.layers.iter() {
106 let Layer { ident, fields, name } = layer;
107 let name_str = name.to_string();
108 let keys_len = fields.len();
109 quote_into! {s +=
110 #[derive(Debug, Clone)]
111 pub struct #ident {
112 pub coordinate: #ci::Coordinate,
113 pub id: Option<u64>,
114
115 #{for Field { ident, ty, .. } in fields.iter() {
116 quote_into!(s += pub #ident: #ty,);
117 }}
118 }
119
120 impl #ident {
121 pub const NAME: &str = #name_str;
122 pub const KEYS: [&str; #keys_len] = [
123 #{for Field { key, .. } in fields {
124 quote_into!(s += #key,)}
125 }
126 ];
127
128 pub fn new(coordinate: #ci::Coordinate) -> Self {
129 Self {
130 coordinate,
131 id: None,
132 #{for Field { ident, ty, .. } in fields.iter() {
133 quote_into!(s += #ident: #ty::default(),);
134 }}
135 }
136 }
137
138 #[allow(dead_code)]
139 pub fn decode_point(
140 zom: u8, tx: u32, ty: u32,
141 feature: &#ci::Feature, values: &[#ci::Value],
142 ) -> Result<Self, &'static str> {
143 #{point_decode(s, layer)}
144 }
145
146 pub fn decode_layer(
147 zom: u8, tx: u32, ty: u32,
148 layer: &#ci::Layer
149 ) -> #ci::protobuf::Result<Vec<Self>> {
150 let mut points = Vec::<Self>::with_capacity(layer.features.len());
151
152 for feature in layer.features.iter() {
153 match Self::decode_point(zom, tx, ty, feature, &layer.values) {
154 Ok(v) => points.push(v),
155 Err(e) => {
156 println!("found an invalid marker: {e:?}")
157 }
158 }
159 }
160
161 Ok(points)
162 }
163
164 #{for field in fields {
165 let Field {ty, key, ..} = field;
166 if field.auto_decode {
167 let ident = format_ident!("decode_{key}");
168 quote_into! {s +=
169 fn #ident(v: &#ci::Value) -> Option<#ty> {
170 #{point_auto_decode(s, field);}
171 }
172 }
173 }
174 if field.auto_encode {
175 let ident = format_ident!("encode_{}", field.key);
176 quote_into! {s +=
177 fn #ident(&self) -> #ci::Value {
178 #{point_auto_encode(s, field);}
179 }
180 }
181 }
182 }}
183 }
184 }
185 }
186 }
187
188 #[derive(Debug)]
189 pub struct Tile {
190 #{for Layer { ident, name, .. } in tile.layers.iter() {
191 quote_into!(s += pub #name: Vec<#ident>, );
192 }}
193 }
194
195 impl Tile {
196 pub fn new() -> Self {
197 Self {#{
198 for Layer { name, .. } in tile.layers.iter() {
199 quote_into!(s += #name: Vec::new(), );
200 }
201 }}
202 }
203
204 #[allow(dead_code)]
205 pub fn decode(zom: u8, tx: u32, ty: u32, pbf: Vec<u8>) -> #ci::protobuf::Result<Self> {
206 let mut tile = Self::new();
207 let vec_tile = <#ci::Tile as #ci::protobuf::Message>::parse_from_bytes(&pbf)?;
208 if vec_tile.layers.is_empty() { return Ok(tile); }
209
210 for layer in vec_tile.layers.iter() {#{
211 tile_decode(s, &tile.layers)
212 }}
213
214 Ok(tile)
215 }
216
217 #[allow(dead_code)]
218 pub fn encode(&self) -> #ci::protobuf::Result<Vec<u8>> {
219 let mut vec_tile = #ci::Tile::default();
220
221 #{for layer in tile.layers.iter() {
222 quote_into!(s += 'a: {#{tile_encode(s, layer)}});
223 }}
224
225 #ci::protobuf::Message::write_to_bytes(&vec_tile)
226 }
227 }
228 }
229
230 s.into()
231}
232
233fn tile_encode(s: &mut TokenStream2, Layer { ident, name, fields }: &Layer) {
234 let keys_len = fields.len();
235 let ci = crate_ident();
236 let name_str = name.to_string();
237
238 quote_into! {s +=
239 let mut values = Vec::<#ci::Value>::with_capacity(self.#name.len() * #keys_len);
240 let mut features = Vec::<#ci::Feature>::with_capacity(self.#name.len());
241
242 for point in self.#name.iter() {
243 #{for Field { key, .. } in fields.iter() {
244 let ptv = format_ident!("encode_{key}");
245 let val = format_ident!("{key}_value");
246 quote_into! {s +=
247 let #val = values.len() as u32;
248 values.push(point.#ptv());
249 }
250 }}
251
252 features.push(#ci::Feature {
253 id: point.id,
254 tags: vec![#{for (idx, Field { key, .. }) in fields.iter().enumerate() {
255 let idx = idx as u32;
256 let val = format_ident!("{key}_value");
257 quote_into!(s += #idx, #val,);
258 }}],
259 geometry: point.coordinate.to_geometry().to_vec(),
260 type_: Some(#ci::protobuf::EnumOrUnknown::new(#ci::GeomType::POINT)),
261 ..Default::default()
262 });
263 }
264
265 vec_tile.layers.push(#ci::Layer {
266 name: Some(String::from(#name_str)),
267 extent: Some(4096),
268 version: Some(2),
269 features,
270 keys: #ident::KEYS.map(|k| k.to_string()).to_vec(),
271 values,
272 ..Default::default()
273 });
274 }
275}
276
277fn tile_decode(s: &mut TokenStream2, layers: &[Layer]) {
278 quote_into! {s +=
279 if layer.version() != 2 { continue }
280
281 match layer.name() {
282 #{for Layer { name, ident, .. } in layers {
283 let name_str = name.to_string();
284 quote_into! {s += #name_str => {
285 tile.#name = #ident::decode_layer(zom, tx, ty, layer)?;
286 }}
287 }}
288 _ => {}
289 }
290
291 continue;
292 }
293}
294
295fn point_auto_encode(s: &mut TokenStream2, field: &Field) {
296 let ci = crate_ident();
297
298 let Field { ident, ty, .. } = field;
299 let ty_str = ty.to_token_stream().to_string();
300
301 if ty.segments.last().unwrap().ident == "Gene" {
302 quote_into!(s += #ci::Value::from_string(self.#ident.as_hex()));
303 return;
304 }
305 match ty_str.as_str() {
306 "bool" => quote_into!(s += #ci::Value::from_bool(self.#ident)),
307 "u8" | "u16" | "u32" | "u64" => {
308 quote_into!(s += #ci::Value::from_uint(self.#ident as u64))
309 }
310 "String" => {
311 quote_into!(s += #ci::Value::from_string(self.#ident.clone()))
312 }
313 _ => quote_into! {s +=
314 compile_error!(concat!("bad prop type for auto encoding: ", #ty_str));
315 },
316 }
317}
318
319fn point_auto_decode(s: &mut TokenStream2, field: &Field) {
320 let ty = &field.ty;
321 let ty_str = ty.to_token_stream().to_string();
322 if ty.segments.last().unwrap().ident == "Gene" {
323 quote_into!(s += v.string_value().parse::<#ty>().ok());
324 return;
325 }
326 match ty_str.as_str() {
327 "bool" => quote_into!(s += Some(v.bool_value())),
328 "String" => quote_into! {s += Some(v.string_value().to_string())},
329 "u8" | "u16" | "u32" | "u64" => {
330 quote_into!(s += Some(v.uint_value() as #ty))
331 }
332 _ => quote_into! {s +=
333 compile_error!(concat!("bad prop type for auto decoding: ", #ty_str));
334 },
335 }
336}
337
338fn point_decode(s: &mut TokenStream2, Layer { fields, .. }: &Layer) {
339 let ci = crate_ident();
340
341 quote_into! {s +=
342 if feature.geometry.len() != 3 {
343 return Err("bad geometry");
344 }
345
346 let tags = &feature.tags;
347 if tags.len() % 2 != 0 {
351 return Err("bad tags length");
352 }
353
354 let geometry: [u32; 3] = feature.geometry.clone().try_into().unwrap();
355 let mut point = Self::new(#ci::Coordinate::from_geometry(zom, tx, ty, geometry));
356 point.id = feature.id;
357
358 let mut tags_iter = tags.iter();
359 loop {
360 let Some(k) = tags_iter.next() else { break };
361 let Some(v) = tags_iter.next() else { break };
362 let k = *k as usize;
363 let v = *v as usize;
364 if k >= Self::KEYS.len() || v >= values.len() {
365 return Err("invalid tags");
366 }
367 let v = &values[v];
368
369 match Self::KEYS[k] {
370 #{for Field { ident, key, .. } in fields {
371 let pfv = format_ident!("decode_{key}");
372 quote_into! {s += #key => {
373 if let Some(value) = Self::#pfv(v) {
374 point.#ident = value;
375 } else {
376 return Err(concat!("could not decode ", #key, "s value"));
377 }
378 }}
379 }}
380 _ => unreachable!()
381 }
382 }
383
384 Ok(point)
385 }
386}
387
388fn to_camelcase(input: &str) -> String {
389 let mut out = String::with_capacity(input.len());
390 for word in input.split('_') {
391 let (h, r) = word.split_at(1);
392 out.push_str(&h.to_uppercase());
393 out.push_str(&r.to_lowercase());
394 }
395
396 out
397}
398
399fn crate_ident() -> syn::Ident {
400 syn::Ident::new("mapack", proc_macro2::Span::call_site())
407}