1#![allow(dead_code)]
2extern crate proc_macro;
3mod lit;
4
5macro_rules! builtin_keys {
6 ($($key: ident),* $(,)?) => {
7 #[allow(non_camel_case_types)]
8 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9 #[repr(u8)]
10 enum StaticKey{
11 $($key,)*
12 }
13 fn get_static_key(key: &str) -> Option<StaticKey> {
14 match key {
15 $(stringify!($key) => Some(StaticKey::$key),)*
16 _ => None
17 }
18 }
19 };
20}
21
22builtin_keys! {
23 msg,
24 err,
25 error,
26 cause,
27 method,
28 status,
29 size,
30 time,
31 count,
32 total,
33 ms,
34 id,
35 user_id,
36 object_id,
37 caller,
38 target,
39 duration,
40 timezone,
41 content_type,
42 conn_id,
43 path,
44 length,
45 on,
46 kind,
47 sensor_id,
48 handler,
49 timestamp,
50 elapsed,
51 camera_id,
52 system_id,
53 next
54}
55
56use lit::{literal_inline, InlineKind};
57use proc_macro::{
58 token_stream::IntoIter, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream,
59 TokenTree,
60};
61
62fn chr(ch: char) -> TokenTree {
63 TokenTree::Punct(Punct::new(ch, Spacing::Alone))
64}
65fn j(ch: char) -> TokenTree {
66 TokenTree::Punct(Punct::new(ch, Spacing::Joint))
67}
68
69fn braced(ts: TokenStream) -> TokenTree {
70 TokenTree::Group(Group::new(Delimiter::Brace, ts))
71}
72
73macro_rules! tok {
74 ($ident:ident) => {
75 TokenTree::Ident(Ident::new(stringify!($ident), Span::call_site()))
76 };
77 (_) => {
78 TokenTree::Ident(Ident::new("_", Span::call_site()))
79 };
80 (()) => {
81 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::default()))
82 };
83 (( $($tt:tt)*) ) => {
84 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::from_iter(toks!($($tt)*))))
85 };
86 ({$($tt:tt)*}) => { $($tt)* };
87 ([$com:literal]) => {
88 TokenTree::Literal(Literal::string($com))
89 };
90 ([$com:literal; $ident:ident]) => {
91 TokenTree::Literal(Literal::$ident($com))
92 };
93 ([_, $span: expr]) => {
94 TokenTree::Ident(Ident::new("_", $span))
95 };
96 ([$ident:ident, $span: expr]) => {
97 TokenTree::Ident(Ident::new(stringify!($ident), $span))
98 };
99 (<) => { chr('<') };
100 (%) => { j(':') };
101 (:) => { chr(':') };
102 (>) => { chr('>') };
103 (.) => { chr('.') };
104 (;) => { chr(';') };
105 (&) => { chr('&') };
106 (=) => { chr('=') };
107 (,) => { chr(',') };
108 ($com:ident; $tt:tt) => {
109 TokenTree::from($com.$tt.clone())
110 };
111}
112
113macro_rules! toks {
114 ($($tt:tt)*) => {
115 [$(tok!($tt)),*]
116 }
117}
118
119fn is_char(tt: &TokenTree, ch: char) -> bool {
120 if let TokenTree::Punct(p) = tt {
121 if p.as_char() == ch {
122 return true;
123 }
124 }
125 false
126}
127
128#[derive(Default)]
130struct SpanAttributes {
131 current: Option<Vec<TokenTree>>,
132 start: Option<Vec<TokenTree>>,
133 end: Option<Vec<TokenTree>>,
134 parent: Option<Vec<TokenTree>>,
135}
136
137struct Codegen {
138 out: TokenStream,
139 collect: Vec<TokenTree>,
140 builder: Ident,
141 error: Option<(Span, Box<str>)>,
142 message: Option<Box<str>>,
143 span: SpanAttributes,
144}
145
146enum ExprKind {
147 Debug,
148 Display,
149 Normal,
150}
151
152fn munch_expr(input: &mut IntoIter, output: &mut Vec<TokenTree>) -> ExprKind {
153 output.clear();
154 let mut kind = ExprKind::Normal;
155 let Some(first) = input.next() else {
156 return kind;
157 };
158 'lemmy: {
159 if let TokenTree::Punct(tt) = &first {
160 match tt.as_char() {
161 '%' => {
162 kind = ExprKind::Display;
163 break 'lemmy;
164 }
165 '?' => {
166 kind = ExprKind::Debug;
167 break 'lemmy;
168 }
169 _ => (),
170 }
171 }
172 output.push(first);
173 }
174 while let Some(tok) = input.next() {
175 if is_char(&tok, ',') {
176 break;
177 }
178 output.push(tok);
179 }
180 return kind;
181}
182
183impl Codegen {
184 fn new(level: &str, builder: Ident) -> Codegen {
185 let mut codegen = Codegen {
186 out: TokenStream::new(),
187 collect: Vec::with_capacity(64),
188 builder,
189 error: None,
190 message: None,
191 span: SpanAttributes::default(),
192 };
193 let log_level = TokenTree::Ident(Ident::new(level, Span::call_site()));
194 codegen.out.extend(toks![
195 use kvlog%:encoding%:Encode;
196 let mut log = kvlog%:global_logger();
197 let mut {codegen.fields()} = log.encoder.append_now(
198 kvlog%:LogLevel%:{log_level}
199 );
200 ]);
201 codegen
202 }
203 fn fields(&self) -> TokenTree {
204 self.builder.clone().into()
205 }
206 fn runtime_field_from_key(&self, field: Ident) -> TokenTree {
207 let field_text = field.to_string();
208 if let Some(static_key) = get_static_key(&field_text) {
209 let key = TokenTree::Literal(Literal::u8_unsuffixed(static_key as u8));
210 TokenTree::Group(Group::new(
211 Delimiter::Parenthesis,
212 TokenStream::from_iter(toks![{ self.fields() }.raw_key({ key })]),
213 ))
214 } else {
215 let key = TokenTree::Literal(Literal::string(&field_text));
216 TokenTree::Group(Group::new(
217 Delimiter::Parenthesis,
218 TokenStream::from_iter(toks![{ self.fields() }.dynamic_key({ key })]),
219 ))
220 }
221 }
222 fn emit_field_value(&mut self, expr: TokenTree, field: Ident) {
223 let method = TokenTree::Ident(Ident::new("encode_log_value_into", field.span()));
224 self.collect.extend_from_slice(&toks![
225 { expr }.{method}{self.runtime_field_from_key(field)};
226 ]);
227 }
228 fn emit_display_field_value(&mut self, expr: TokenTree, field: Ident) {
229 self.collect.extend_from_slice(&toks![
230 { self.runtime_field_from_key(field) }.value_via_display(&{ expr });
231 ]);
232 }
233 fn emit_debug_field_value(&mut self, expr: TokenTree, field: Ident) {
234 self.collect.extend_from_slice(&toks![
235 { self.runtime_field_from_key(field) }.value_via_debug(&{ expr });
236 ]);
237 }
238 fn error(&mut self, span: Span, msg: &str) {
239 if self.error.is_none() {
240 self.error = Some((span, msg.into()))
241 }
242 }
243 fn error_fmt(&mut self, span: Span, msg: Box<str>) {
244 if self.error.is_none() {
245 self.error = Some((span, msg))
246 }
247 }
248 fn finish_creating(mut self) -> TokenStream {
249 let target_key = TokenTree::Literal(Literal::u8_unsuffixed(StaticKey::target as u8));
250 let message_key = TokenTree::Literal(Literal::u8_unsuffixed(StaticKey::msg as u8));
251 self.collect.extend_from_slice(&toks![
252 (module_path {chr('!')} ()).encode_log_value_into(
253 {self.fields()}.raw_key({target_key})
254 );
255
256 ]);
257 let fields = self.fields();
258 self.out.extend(std::mem::take(&mut self.collect));
259
260 if let Some(message) = &self.message {
261 self.out.extend(toks![
262 ({TokenTree::Literal(Literal::string(message))}).encode_log_value_into(
263 {fields.clone()}.raw_key({message_key})
264 );
265 ]);
266 };
267 let base_set = (self.span.current.is_some() as u8)
268 + (self.span.start.is_some() as u8)
269 + (self.span.end.is_some() as u8);
270
271 if base_set > 1 {
272 self.error(
273 Span::call_site(),
274 "More then one 'start', 'end', 'current' field for span specified.".into(),
275 )
276 }
277 if self.span.parent.is_some() && self.span.start.is_none() {
278 self.error(
279 Span::call_site(),
280 "`span.parent` can only be specified with span.start".into(),
281 )
282 }
283
284 if let Some(current) = self.span.current {
285 let tok = TokenTree::Group(Group::new(
286 Delimiter::Parenthesis,
287 TokenStream::from_iter(current),
288 ));
289 self.out.extend(toks![ {fields}.apply_span {tok}; ]);
290 } else if let Some(start) = self.span.start {
291 if let Some(parent) = self.span.parent {
292 let mut args = TokenStream::from_iter(start);
293 args.extend([TokenTree::Punct(Punct::new(',', Spacing::Alone))]);
294 args.extend(parent);
295 let tok = TokenTree::Group(Group::new(Delimiter::Parenthesis, args));
296 self.out
297 .extend(toks![ {fields}.start_span_with_parent {tok}; ]);
298 } else {
299 let tok = TokenTree::Group(Group::new(
300 Delimiter::Parenthesis,
301 TokenStream::from_iter(start),
302 ));
303 self.out.extend(toks![ {fields}.start_span {tok}; ]);
304 }
305 } else if let Some(end) = self.span.end {
306 let tok = TokenTree::Group(Group::new(
307 Delimiter::Parenthesis,
308 TokenStream::from_iter(end),
309 ));
310 self.out.extend(toks![ {fields}.end_span {tok}; ]);
311 } else {
312 self.out.extend(toks![ {fields}.apply_current_span(); ]);
313 }
314
315 self.out.extend(toks![ log.poke(); ]);
316
317 if let Some((span, msg)) = self.error {
318 let mut group = TokenTree::Group(Group::new(
319 Delimiter::Parenthesis,
320 TokenStream::from_iter([TokenTree::Literal(Literal::string(&msg))]),
321 ));
322 let mut punc = TokenTree::Punct(Punct::new('!', Spacing::Alone));
323 punc.set_span(span);
324 group.set_span(span);
325
326 self.out.extend([
327 TokenTree::Ident(Ident::new("compile_error", span)),
328 punc,
329 group,
330 TokenTree::Punct(Punct::new(';', Spacing::Alone)),
331 ]);
332 }
334 TokenStream::from_iter([braced(self.out)])
335 }
336 fn parse_if(&mut self, ident: Ident, tokens: &mut IntoIter) {
337 let span = ident.span();
338 self.collect.push(ident.into());
339 loop {
340 let Some(tok) = tokens.next() else {
341 self.error(span, "Expected Block for if statement".into());
342 return;
343 };
344 if let TokenTree::Group(group) = &tok {
345 if group.delimiter() == Delimiter::Brace {
346 let mut tmp = std::mem::take(&mut self.collect);
347 self.parse_values(group.stream().into_iter());
348 std::mem::swap(&mut tmp, &mut self.collect);
349 self.collect.push(TokenTree::Group(Group::new(
350 Delimiter::Brace,
351 TokenStream::from_iter(tmp),
352 )));
353 return;
354 }
355 }
356 self.collect.push(tok);
357 }
358 }
359 fn parse_span_attrib(&mut self, span: Ident, tokens: &mut IntoIter) {
360 let Some(tok) = tokens.next() else {
361 self.error(
362 span.span(),
363 "Eof will parsing span attribute expected".into(),
364 );
365 return;
366 };
367 let TokenTree::Ident(ident) = tok else {
368 self.error(Span::call_site(), "Expected span field".into());
369 return;
370 };
371 let Some(eq_tok) = tokens.next() else {
372 self.error(ident.span(), "Eof after span expected `=`".into());
373 return;
374 };
375 if !is_char(&eq_tok, '=') {
376 self.error(eq_tok.span(), "expected `=` after span field".into());
377 return;
378 }
379 let field = ident.to_string();
380 let mut output = Vec::default();
381 let _expr = munch_expr(tokens, &mut output);
382 let attrib = match field.as_str() {
383 "current" => &mut self.span.current,
384 "start" => &mut self.span.start,
385 "end" => &mut self.span.end,
386 "parent" => &mut self.span.parent,
387 other => {
388 self.error_fmt(ident.span(), format!("unknown span field {}", other).into());
389 return;
390 }
391 };
392 if attrib.is_some() {
393 self.error_fmt(
394 ident.span(),
395 format!("Span `{}` specified more than once", field.as_str()).into(),
396 );
397 return;
398 }
399 *attrib = Some(output);
400 }
401
402 fn parse_values(&mut self, mut tokens: IntoIter) {
403 let mut tmp: Vec<TokenTree> = Vec::with_capacity(8);
404 let mut last_was_block_set = false;
405 while let Some(tok) = tokens.next() {
406 let last_was_block = last_was_block_set;
407 last_was_block_set = false;
408 let key = match tok {
409 TokenTree::Group(value) => {
410 self.error(value.span(), "unexpected span");
411 continue;
412 }
413 TokenTree::Ident(ident) => ident,
414 TokenTree::Punct(punct) => {
415 let ch = punct.as_char();
416 if ch == ',' && last_was_block {
417 continue;
418 }
419 if ch != '?' && ch != '%' {
420 self.error(punct.span(), "Unexpected punct");
421 continue;
422 }
423 let Some(tt) = tokens.next() else {
424 self.error(punct.span(), "Unexpected EOF");
425 return;
426 };
427 let TokenTree::Ident(key_expr) = tt else {
428 self.error(tt.span(), "Expected name");
429 continue;
430 };
431
432 if ch == '?' {
433 self.emit_debug_field_value(TokenTree::Ident(key_expr.clone()), key_expr);
434 } else {
435 self.emit_display_field_value(TokenTree::Ident(key_expr.clone()), key_expr);
436 }
437
438 if let Some(tt) = tokens.next() {
439 if !is_char(&tt, ',') {
440 self.error(tt.span(), "Expected ,");
441 return;
442 }
443 }
444 continue;
445 }
446 TokenTree::Literal(lit) => {
447 if let InlineKind::String(lit) = literal_inline(lit.to_string()) {
448 self.message = Some(lit);
449 } else {
450 self.error(lit.span(), "Expected string message ");
451 return;
452 }
453 if let Some(tt) = tokens.next() {
454 if !is_char(&tt, ',') {
455 self.error(tt.span(), "Expected ,");
456 continue;
457 }
458 }
459 continue;
460 }
461 };
462 let key_name = key.to_string();
463 if key_name == "if" {
464 self.parse_if(key, &mut tokens);
465 last_was_block_set = true;
466 continue;
467 }
468 let Some(tt) = tokens.next() else {
469 self.emit_field_value(TokenTree::Ident(key.clone()), key);
470 return;
471 };
472 match &tt {
473 TokenTree::Punct(punct) => {
474 let ch = punct.as_char();
475 if ch == ',' {
476 self.emit_field_value(TokenTree::Ident(key.clone()), key);
477 continue;
478 }
479 if ch == '.' && key_name == "span" {
480 self.parse_span_attrib(key, &mut tokens);
481 continue;
482 }
483 if ch != '=' {
484 self.error(punct.span(), "Expected =");
485 continue;
486 }
487 tmp.clear();
488 let kind = munch_expr(&mut tokens, &mut tmp);
489 if tmp.is_empty() {
490 self.error(punct.span(), "Expected Expression following =");
491 continue;
492 }
493 let expr = TokenStream::from_iter(tmp.drain(..));
494 let expr = TokenTree::Group(Group::new(Delimiter::Parenthesis, expr));
495 match kind {
496 ExprKind::Debug => {
497 self.emit_debug_field_value(expr, key);
498 }
499 ExprKind::Display => {
500 self.emit_display_field_value(expr, key);
501 }
502 ExprKind::Normal => {
503 self.emit_field_value(expr, key);
504 }
505 }
506 }
507 _ => {
508 self.error(tt.span(), "Expected =");
509 continue;
510 }
511 }
512 }
513 }
514}
515
516fn emit_log(input: TokenStream, level: &str) -> TokenStream {
517 let toks = input.into_iter();
518 let mut codegen = Codegen::new(level, Ident::new("fields", Span::call_site()));
519 codegen.parse_values(toks);
520 codegen.finish_creating()
521}
522
523#[proc_macro]
524pub fn info(input: TokenStream) -> TokenStream {
525 emit_log(input, "Info")
526}
527
528#[proc_macro]
529pub fn debug(input: TokenStream) -> TokenStream {
530 emit_log(input, "Debug")
531}
532
533#[proc_macro]
534pub fn error(input: TokenStream) -> TokenStream {
535 emit_log(input, "Error")
536}
537
538#[proc_macro]
539pub fn warn(input: TokenStream) -> TokenStream {
540 emit_log(input, "Warn")
541}