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.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(#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 syn::Ident::new("mapack", proc_macro2::Span::call_site())
417}