Skip to main content

mapack_macros/
lib.rs

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, #ci::Error> {
143                            #{point_decode(s, layer)}
144                        }
145
146                        pub fn decode_layer(
147                            zom: u8, tx: u32, ty: u32,
148                            layer: &#ci::Layer
149                        ) -> Result<Vec<Self>, #ci::Error> {
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>) -> Result<Self, #ci::Error> {
206                if pbf.is_empty() {
207                }
208                let mut tile = Self::new();
209                let vec_tile = <#ci::Tile as #ci::protobuf::Message>::parse_from_bytes(&pbf)?;
210                if vec_tile.layers.is_empty() { return Ok(tile); }
211
212                for layer in vec_tile.layers.iter() {#{
213                    tile_decode(s, &tile.layers)
214                }}
215
216                Ok(tile)
217            }
218
219            #[allow(dead_code)]
220            pub fn encode(&self) -> Result<Vec<u8>, #ci::Error> {
221                let mut vec_tile = #ci::Tile::default();
222
223                #{for layer in tile.layers.iter() {
224                    quote_into!(s += 'a: {#{tile_encode(s, layer)}});
225                }}
226
227                let r = #ci::protobuf::Message::write_to_bytes(&vec_tile)?;
228                Ok(r)
229            }
230        }
231    }
232
233    s.into()
234}
235
236fn tile_encode(s: &mut TokenStream2, Layer { ident, name, fields }: &Layer) {
237    let keys_len = fields.len();
238    let ci = crate_ident();
239    let name_str = name.to_string();
240
241    quote_into! {s +=
242        let mut values = Vec::<#ci::Value>::with_capacity(self.#name.len() * #keys_len);
243        let mut features = Vec::<#ci::Feature>::with_capacity(self.#name.len());
244
245        for point in self.#name.iter() {
246            #{for Field { key, .. } in fields.iter() {
247                let ptv = format_ident!("encode_{key}");
248                let val = format_ident!("{key}_value");
249                quote_into! {s +=
250                    let #val = values.len() as u32;
251                    values.push(point.#ptv());
252                }
253            }}
254
255            features.push(#ci::Feature {
256                id: point.id,
257                tags: vec![#{for (idx, Field { key, .. }) in fields.iter().enumerate() {
258                    let idx = idx as u32;
259                    let val = format_ident!("{key}_value");
260                    quote_into!(s += #idx, #val,);
261                }}],
262                geometry: point.coordinate.to_geometry().to_vec(),
263                type_: Some(#ci::protobuf::EnumOrUnknown::new(#ci::GeomType::POINT)),
264                ..Default::default()
265            });
266        }
267
268        vec_tile.layers.push(#ci::Layer {
269            name: Some(String::from(#name_str)),
270            extent: Some(4096),
271            version: Some(2),
272            features,
273            keys: #ident::KEYS.map(|k| k.to_string()).to_vec(),
274            values,
275            ..Default::default()
276        });
277    }
278}
279
280fn tile_decode(s: &mut TokenStream2, layers: &[Layer]) {
281    quote_into! {s +=
282        if layer.version() != 2 { continue }
283
284        match layer.name() {
285            #{for Layer { name, ident, .. } in layers {
286                let name_str = name.to_string();
287                quote_into! {s += #name_str => {
288                    tile.#name = #ident::decode_layer(zom, tx, ty, layer)?;
289                }}
290            }}
291            _ => {}
292        }
293
294        continue;
295    }
296}
297
298fn point_auto_encode(s: &mut TokenStream2, field: &Field) {
299    let ci = crate_ident();
300
301    let Field { ident, ty, .. } = field;
302    let ty_str = ty.to_token_stream().to_string();
303
304    if ty.segments.last().unwrap().ident == "Gene" {
305        quote_into!(s += #ci::Value::from_string(self.#ident.as_hex()));
306        return;
307    }
308    match ty_str.as_str() {
309        "bool" => quote_into!(s += #ci::Value::from_bool(self.#ident)),
310        "u8" | "u16" | "u32" | "u64" => {
311            quote_into!(s += #ci::Value::from_uint(self.#ident as u64))
312        }
313        "i8" | "i16" | "i32" | "i64" => {
314            quote_into!(s += #ci::Value::from_int(self.#ident as i64))
315        }
316        "String" => {
317            quote_into!(s += #ci::Value::from_string(self.#ident.clone()))
318        }
319        _ => quote_into! {s +=
320            compile_error!(concat!("bad prop type for auto encoding: ", #ty_str));
321        },
322    }
323}
324
325fn point_auto_decode(s: &mut TokenStream2, field: &Field) {
326    let ty = &field.ty;
327    let ty_str = ty.to_token_stream().to_string();
328    if ty.segments.last().unwrap().ident == "Gene" {
329        quote_into!(s += v.string_value().parse::<#ty>().ok());
330        return;
331    }
332    match ty_str.as_str() {
333        "bool" => quote_into!(s += Some(v.bool_value())),
334        "String" => quote_into! {s += Some(v.string_value().to_string())},
335        "u8" | "u16" | "u32" | "u64" => {
336            quote_into!(s += Some(v.uint_value() as #ty))
337        }
338        "i8" | "i16" | "i32" | "i64" => {
339            quote_into!(s += Some(v.int_value() as #ty))
340        }
341        _ => quote_into! {s +=
342            compile_error!(concat!("bad prop type for auto decoding: ", #ty_str));
343        },
344    }
345}
346
347fn point_decode(s: &mut TokenStream2, Layer { fields, .. }: &Layer) {
348    let ci = crate_ident();
349
350    quote_into! {s +=
351        if feature.geometry.len() != 3 {
352            return Err(#ci::Error::BadGeomerty);
353        }
354
355        let tags = &feature.tags;
356        // if tags.is_empty() {
357        //     return Err("no tags");
358        // }
359        if tags.len() % 2 != 0 {
360            return Err(#ci::Error::BadTagsLength);
361        }
362
363        let geometry: [u32; 3] = feature.geometry.clone().try_into().unwrap();
364        let mut point = Self::new(#ci::Coordinate::from_geometry(zom, tx, ty, geometry));
365        point.id = feature.id;
366
367        let mut tags_iter = tags.iter();
368        loop {
369            let Some(k) = tags_iter.next() else { break };
370            let Some(v) = tags_iter.next() else { break };
371            let k = *k as usize;
372            let v = *v as usize;
373            if k >= Self::KEYS.len() || v >= values.len() {
374                return Err(#ci::Error::InvalidTag);
375            }
376            let v = &values[v];
377
378            match Self::KEYS[k] {
379                #{for Field { ident, key, .. } in fields {
380                    let pfv = format_ident!("decode_{key}");
381                    quote_into! {s += #key => {
382                        if let Some(value) = Self::#pfv(v) {
383                            point.#ident = value;
384                        } else {
385                            // return Err(concat!("could not decode ", #key, "s value"));
386                            return Err(#ci::Error::DecodeFailed(#key));
387                        }
388                    }}
389                }}
390                _ => unreachable!()
391            }
392        }
393
394        Ok(point)
395    }
396}
397
398fn to_camelcase(input: &str) -> String {
399    let mut out = String::with_capacity(input.len());
400    for word in input.split('_') {
401        let (h, r) = word.split_at(1);
402        out.push_str(&h.to_uppercase());
403        out.push_str(&r.to_lowercase());
404    }
405
406    out
407}
408
409fn crate_ident() -> syn::Ident {
410    // let found_crate = crate_name("shah").unwrap();
411    // let name = match &found_crate {
412    //     FoundCrate::Itself => "shah",
413    //     FoundCrate::Name(name) => name,
414    // };
415
416    syn::Ident::new("mapack", proc_macro2::Span::call_site())
417}