| 49 | } |
| 50 | |
| 51 | fn check_token(token: proc_macro2::TokenTree, arg: &str) -> Option<String> { |
| 52 | // this detects the '(...)' part in #[serde(rename_all = "UPPERCASE", tag = "type")] |
| 53 | // we can use this to get the value of a particular argument |
| 54 | // or to see if it exists at all |
| 55 | let proc_macro2::TokenTree::Group(group) = token else { |
| 56 | return None; |
| 57 | }; |
| 58 | |
| 59 | // Make sure the delimiter is what we're expecting, otherwise return right away. |
| 60 | if group.delimiter() != proc_macro2::Delimiter::Parenthesis { |
| 61 | return None; |
| 62 | } |
| 63 | |
| 64 | // First check to see if the group is a `MetaNameValue`, (.e.g `feature = "nightly"`) |
| 65 | match Parser::parse2( |
| 66 | Punctuated::<MetaNameValue, Token![,]>::parse_terminated, |
| 67 | group.stream(), |
| 68 | ) { |
| 69 | Ok(name_value_pairs) => { |
| 70 | // If so move the pairs into an iterator |
| 71 | name_value_pairs |
| 72 | .into_iter() |
| 73 | // checking that the `path` component is of length 1 equal to the given arg. |
| 74 | .find(|nvp| nvp.path.is_ident(arg)) |
| 75 | // If it is, get the `value` component, ("nightly" from the example above). |
| 76 | .map(|nvp| nvp.value.to_token_stream().to_string()) |
| 77 | // Then remove the literal quotes around the value. |
| 78 | .map(|value| value[1..value.len() - 1].to_owned()) |
| 79 | } |
| 80 | Err(_) => { |
| 81 | // Otherwise, check to see if the group is a `Expr` of `Punctuated<_, P>` attributes, |
| 82 | // separated by `P`, `Token![,]` in this case. |
| 83 | // (.e.g `default, skip_serializing`) |
| 84 | Parser::parse2( |
| 85 | Punctuated::<Expr, Token![,]>::parse_terminated, |
| 86 | group.stream(), |
| 87 | ) |
| 88 | // If the expression cannot be parsed, return None |
| 89 | .map_or(None, |comma_seperated_values| { |
| 90 | // Otherwise move the pairs into an iterator |
| 91 | comma_seperated_values |
| 92 | .into_iter() |
| 93 | // Checking each is a `ExprPath`, object, yielding elements while the method |
| 94 | // returns true. |
| 95 | .map_while(check_expression_is_path) |
| 96 | // Check if any yielded paths equal `arg` |
| 97 | .any(|expr_path| { |
| 98 | if let Some(last) = expr_path.path.segments.last() { |
| 99 | last.ident.to_string().eq(arg) |
| 100 | } else { |
| 101 | false |
| 102 | } |
| 103 | }) |
| 104 | // If so, return `Some(arg)`, otherwise `None`. |
| 105 | .then_some(arg.to_owned()) |
| 106 | }) |
| 107 | } |
| 108 | } |