1#![cfg_attr(lints_enabled, feature(proc_macro_diagnostic), feature(negative_impls))]
2
3use proc_macro::TokenStream;
4use proc_macro2::{Span, TokenStream as TokenStream2};
5use quote::{quote, ToTokens};
6use syn::{
7 braced, bracketed, parse,
8 parse::{Parse, ParseStream},
9 parse_macro_input,
10 spanned::Spanned,
11 token, Error as SynError, Expr, Ident, Index, ItemEnum, ItemStruct, LitBool, LitInt, LitStr,
12 Member, Result as SynResult, Token,
13};
14
15#[cfg(lints_enabled)]
16use proc_macro::{Diagnostic, Level};
18
19mod util {
20 use std::iter::Iterator;
21
22 #[must_use]
23 #[inline(always)]
24 pub fn iter_len<T, I: Iterator<Item = T> + Clone>(iter: &I) -> usize {
30 iter.clone().count()
31 }
32}
33
34enum JsonValue {
35 Object(JsonObject),
36 Array(JsonArray),
37 String(LitStr),
38 Bool(bool),
39 Expr(Expr),
40 Null,
41}
42
43#[derive(Clone)]
44enum JsonKey {
45 Expr(Expr),
46 Lit(String),
47}
48
49impl PartialEq for JsonKey {
50 fn eq(&self, other: &Self) -> bool {
51 match (self, other) {
52 (Self::Lit(s1), Self::Lit(s2)) => *s1 == *s2,
53 (Self::Expr(e1), Self::Expr(e2)) => quote!(#e1).to_string() == quote!(#e2).to_string(),
54 _ => false,
55 }
56 }
57}
58
59struct JsonKeyValue {
60 key: JsonKey,
61 value: JsonValue,
62 key_span: Span,
63}
64
65struct JsonObject {
66 pairs: Vec<JsonKeyValue>,
67}
68
69struct JsonArray {
70 elements: Vec<JsonValue>,
71}
72
73impl Parse for JsonKeyValue {
74 fn parse(input: ParseStream) -> SynResult<Self> {
75 let (key, span) = if input.peek(LitStr) {
76 let item = input.parse::<LitStr>()?;
77 (JsonKey::Lit(item.value()), item.span())
78 } else if cfg!(feature = "exprs-as-keys") {
79 let item = input.parse::<Expr>()?;
80 (JsonKey::Expr(item.clone()), item.span())
81 } else {
82 let item = input.parse::<Ident>()?;
83 (JsonKey::Lit(item.to_string()), item.span())
84 };
85 input.parse::<Token![:]>()?;
86
87 Ok(JsonKeyValue {
88 key,
89 value: input.parse()?,
90 key_span: span,
91 })
92 }
93}
94
95impl Parse for JsonObject {
96 fn parse(input: ParseStream) -> SynResult<Self> {
97 let content;
98 let _ = braced!(content in input);
99 let mut pairs = Vec::new();
100
101 while !content.is_empty() {
102 let pair = content.parse()?;
103 pairs.push(pair);
104 let _ = content.parse::<Token![,]>();
105 }
106
107 {
108 let mut map: Vec<(JsonKey, Span)> = Vec::new();
109 for JsonKeyValue { key, key_span, .. } in &pairs {
110 #[cfg(lints_enabled)]
111 if let Some((key, span2)) = map.iter().find(|(key2, _)| *key2 == *key) {
112 Diagnostic::spanned(
113 key_span.unwrap(),
114 Level::Error,
115 format!(
116 "duplicate key {} in object",
117 match key {
118 JsonKey::Lit(str) => str.clone(),
119 JsonKey::Expr(expr) => format!("expression `{}`", quote!(#expr)),
120 }
121 ),
122 )
123 .help("remove this repeated key")
124 .span_note(span2.unwrap(), "key first defined here")
125 .emit();
126 continue;
127 }
128 map.push((key.clone(), *key_span));
129 }
130 }
131
132 Ok(JsonObject { pairs })
133 }
134}
135
136impl Parse for JsonArray {
137 fn parse(input: ParseStream) -> SynResult<Self> {
138 let content;
139 let _brackets = bracketed!(content in input);
140 let mut elements = Vec::new();
141
142 while !content.is_empty() {
143 let elem = content.parse()?;
144 elements.push(elem);
145 if content.peek(Token![,]) {
146 content.parse::<Token![,]>()?;
147 }
148 }
149
150 Ok(JsonArray {
151 elements,
152 })
154 }
155}
156
157fn check_int_overflow(int: LitInt) {
173 let upper = const { 2i64.pow(53) };
174 let lower = -upper;
175 let (clamped, fits) = int.base10_parse::<i64>().map_or((0, false), |v| {
176 (v.clamp(lower, upper), (lower..upper).contains(&v))
177 });
178 if !fits {
179 let mut d = Diagnostic::spanned(
180 int.span().unwrap(),
181 Level::Note,
182 "value may be outside safe integer range of most programming languages",
183 )
184 .note("keeping this value may result in a loss of precision in other languages")
185 .note("-2^53 to 2^53 is the JavaScript safe integer range");
186 if clamped != 0 {
187 d = d.help(format!("change this value or show it clamped: {clamped}"))
188 }
189 d.emit();
190 }
191}
192
193impl Parse for JsonValue {
194 fn parse(input: ParseStream) -> SynResult<Self> {
195 if input.peek(LitStr) {
196 Ok(JsonValue::String(input.parse()?))
197 } else if input.peek(LitBool) {
198 Ok(JsonValue::Bool(input.parse::<LitBool>()?.value))
199 } else if input.peek(token::Brace) {
200 Ok(JsonValue::Object(input.parse()?))
201 } else if input.peek(token::Bracket) {
202 Ok(JsonValue::Array(input.parse()?))
203 } else if input
204 .fork()
205 .parse::<Ident>()
206 .map(|v| v.to_string())
207 .is_ok_and(|v| v == "undefined" || v == "null")
208 {
209 input.parse::<Ident>()?;
210 Ok(JsonValue::Null)
211 } else {
212 #[cfg(lints_enabled)]
213 if let Ok(int) = input.fork().parse::<LitInt>() {
214 check_int_overflow(int)
215 }
216 Ok(JsonValue::Expr(input.parse()?))
217 }
218 }
219}
220
221impl ToTokens for JsonValue {
222 fn to_tokens(&self, tokens: &mut TokenStream2) {
223 match self {
224 JsonValue::Object(obj) => obj.to_tokens(tokens),
225 JsonValue::Array(arr) => arr.to_tokens(tokens),
226 JsonValue::String(litstr) => quote!(format!("\"{}\"", #litstr)).to_tokens(tokens),
227 JsonValue::Bool(b) => (*b).to_tokens(tokens),
228 JsonValue::Expr(expr) => {
229 quote!((::json_proc::ToJson::to_json_string(&(#expr)))).to_tokens(tokens);
230 }
231 JsonValue::Null => quote!("null").to_tokens(tokens),
232 }
233 }
234}
235
236impl ToTokens for JsonKey {
237 fn to_tokens(&self, tokens: &mut TokenStream2) {
238 match self {
239 Self::Lit(str) => quote!(#str).to_tokens(tokens),
240 Self::Expr(expr) => expr.to_tokens(tokens),
241 }
242 }
243}
244
245impl ToTokens for JsonObject {
247 fn to_tokens(&self, tokens: &mut TokenStream2) {
248 if self.pairs.is_empty() {
249 return quote!("{}".to_string()).to_tokens(tokens);
250 }
251 let pairs = &self.pairs;
252 let mut pairs_tokens = Vec::new();
253 for pair in pairs {
254 let key = &pair.key;
255 let value = &pair.value;
256 pairs_tokens.push(quote!(format!("\"{}\":{}", #key, #value)));
257 }
258 let output = quote! {{
259 let mut string = String::with_capacity(2);
261 string.push('{');
262 #(
263 string.push_str(&#pairs_tokens);
264 string.push(',');
265 )*
266 let _ = string.pop();
267 string.push('}');
268 string
269 }};
270 output.to_tokens(tokens);
271 }
272}
273
274impl ToTokens for JsonArray {
275 fn to_tokens(&self, tokens: &mut TokenStream2) {
276 if self.elements.is_empty() {
277 return quote!("[]").to_tokens(tokens);
278 }
279 let elements = &self.elements;
280 let mut elements_tokens = Vec::new();
281 for elem in elements {
282 elements_tokens.push(quote!(#elem));
283 }
284 let output = quote! {{
285 let mut string = String::with_capacity(2);
287 string.push('[');
288 #(
289 string.push_str(&#elements_tokens);
290 string.push(',');
291 )*
292 let _ = string.pop();
293 string.push(']');
294 string
295 }};
296 output.to_tokens(tokens);
297 }
298}
299
300#[cfg_attr(lints_enabled, doc = "Lints and properly ")]
303#[cfg_attr(not(lints_enabled), doc = "Properly ")]
304#[cfg_attr(
314 not(lints_enabled),
315 doc = "Keep in mind, lints are only enabled on the Nightly channel of Rust."
316)]
317#[cfg_attr(
318 lints_enabled,
319 doc = "Lints are currently enabled because you are using the Nightly channel of Rust."
320)]
321#[proc_macro]
352pub fn json(input: TokenStream) -> TokenStream {
353 let json_value = parse_macro_input!(input as JsonValue);
354
355 quote!(#json_value).into()
356}
357
358#[proc_macro_derive(ToJson)]
381pub fn json_derive(item: TokenStream) -> TokenStream {
383 if let Ok(mut input) = parse::<ItemStruct>(item.clone()) {
384 let ident = &input.ident;
385 let mut members = input.fields.members().peekable();
386 input.generics.make_where_clause();
387 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
388 let mut where_clause = input.generics.where_clause.clone().unwrap();
389 let type_generics = input.generics.type_params().map(|v| v.ident.clone());
390 let path = quote!(::json_proc::ToJson).into();
391 let path = parse_macro_input!(path as syn::Path);
392 for ty in type_generics {
393 let mut bounds = syn::punctuated::Punctuated::new();
394 bounds.push(syn::TypeParamBound::Trait(syn::TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, lifetimes: None, path: path.clone() }));
395 where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType { lifetimes: None, bounded_ty: syn::Type::Verbatim(quote!(#ty)), colon_token: Token), bounds }))
396 }
397
398 let fn_impl = if members
399 .peek()
400 .is_some_and(|v| matches!(v, Member::Unnamed(_)))
401 {
402 if util::iter_len(&members) == 1 {
403 quote!(::json_proc::ToJson::to_json_string(&self.0))
405 } else {
406 quote! {{
408 let mut string = String::with_capacity(2);
410 string.push('[');
411 #(
412 string.push_str(&(::json_proc::ToJson::to_json_string(&self.#members)));
413 string.push(',');
414 )*
415 let _ = string.pop();
416 string.push(']');
417 string
418 }}
419 }
420 } else if members.peek().is_some() {
421 quote! {{
423 let mut string = String::with_capacity(2);
425 string.push('{');
426 #(
427 string.push('"');
428 string.push_str(stringify!(#members));
429 string.push('"');
430 string.push(':');
431 string.push_str(&(::json_proc::ToJson::to_json_string(&self.#members)));
432 string.push(',');
433 )*
434 let _ = string.pop();
435 string.push('}');
436 string
437 }}
438 } else {
439 quote!(stringify!(#ident).to_string())
440 };
441 quote! {
442 impl #impl_generics ToJson for #ident #ty_generics #where_clause {
443 fn to_json_string(&self) -> String {
444 #fn_impl
445 }
446 }
447 }
448 .into()
449 } else if let Ok(mut input) = parse::<ItemEnum>(item.clone()) {
450 let ident = input.ident;
451 let variants = input.variants.iter().peekable();
452 input.generics.make_where_clause();
453 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
454 let mut where_clause = input.generics.where_clause.clone().unwrap();
455 let type_generics = input.generics.type_params().map(|v| v.ident.clone());
456 let path = quote!(::json_proc::ToJson).into();
457 let path = parse_macro_input!(path as syn::Path);
458 for ty in type_generics {
459 let mut bounds = syn::punctuated::Punctuated::new();
460 bounds.push(syn::TypeParamBound::Trait(syn::TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, lifetimes: None, path: path.clone() }));
461 where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType { lifetimes: None, bounded_ty: syn::Type::Verbatim(quote!(#ty)), colon_token: Token), bounds }))
462 }
463
464 let mut streams: Vec<TokenStream2> = Vec::new();
465 for var in variants {
466 let varident = &var.ident;
468 let mut members = var.fields.members().peekable();
469 let iter_len = util::iter_len(&members);
470 let this_impl = if iter_len == 0 {
471 quote!(Self::#varident => stringify!(#ident).to_string())
472 } else if members
473 .peek()
474 .is_some_and(|v| matches!(v, Member::Unnamed(_)))
475 {
476 if iter_len == 1 {
477 quote!(Self::#varident(a) => ::json_proc::ToJson::to_json_string(a))
479 } else {
480 let members = members.map(|v| match v {
482 Member::Unnamed(i) => {
483 Ident::new(format!("arg{}", i.index).as_str(), i.span)
484 }
485 Member::Named(_) => unreachable!(),
486 });
487 let members2 = members.clone();
488 quote!(Self::#varident( #(#members2),* ) => {
489 let mut string = String::with_capacity(2);
491 string.push('[');
492 #(
493 string.push_str(&(::json_proc::ToJson::to_json_string(#members)));
494 string.push(',');
495 )*
496 let _ = string.pop();
497 string.push(']');
498 string
499 })
500 }
501 } else {
502 let members2 = members.clone();
504 quote!(Self::#varident { #(#members2),* } => {
505 let mut string = String::with_capacity(2);
507 string.push('{');
508 #(
509 string.push('"');
510 string.push_str(stringify!(#members));
511 string.push('"');
512 string.push(':');
513 string.push_str(&(::json_proc::ToJson::to_json_string(#members)));
514 string.push(',');
515 )*
516 let _ = string.pop();
517 string.push('}');
518 string
519 })
520 };
521
522 streams.push(this_impl)
523 }
524
525 quote! {
526 impl #impl_generics ToJson for #ident #ty_generics #where_clause {
527 fn to_json_string(&self) -> String {
528 match self {
529 #(#streams),*
530 }
531 }
532 }
533 }
534 .into()
535 } else {
536 SynError::new(
537 TokenStream2::from(item).span(),
538 "expected struct or enum for deriving ToJson",
539 )
540 .into_compile_error()
541 .into()
542 }
543}
544
545#[doc(hidden)]
546#[proc_macro]
547pub fn tuple_impl(_: TokenStream) -> TokenStream {
549 const LETTERS: [char; 12] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'];
550 let mut streams = Vec::with_capacity(12);
551 for i in 0..12 {
552 let letters = &LETTERS[..(i + 1)]
553 .iter()
554 .map(|ch| Ident::new(&ch.to_string(), Span::call_site()))
555 .collect::<Vec<Ident>>();
556 let nums = (0..(i + 1)).map(Index::from).collect::<Vec<Index>>();
557 let doc_attr = if i == 0 {
558 quote!(#[doc = "`ToJson` is implemented for tuples up to size 12."])
559 } else {
560 quote!(#[doc(hidden)])
561 };
562 streams.push(quote! {
563 #doc_attr
564 impl<#(#letters),*> crate::ToJson for (#(#letters,)*)
565 where
566 #(
567 #letters: crate::ToJson
568 ),*
569 {
570 fn to_json_string(&self) -> String {
571 let mut string = String::with_capacity(3);
572 string.push('[');
573 #(
574 string.push_str(&(crate::ToJson::to_json_string(&self.#nums)));
575 string.push(',');
576 )*
577 let _ = string.pop();
578 string.push(']');
579 string
580 }
581 }
582 });
583 }
584 quote!(#(#streams)*).into()
585}