1 // Copyright 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use syn::{ 16 parse::{Parse, ParseStream}, 17 Expr, LitStr, Token, 18 }; 19 20 /// Custom keywords 21 pub mod kw { 22 syn::custom_keyword!(package); 23 syn::custom_keyword!(class); 24 syn::custom_keyword!(method_name); 25 syn::custom_keyword!(panic_returns); 26 } 27 28 /// Arguments to the attribute 29 pub enum MetaArg { 30 Package { 31 package_token: kw::package, 32 _eq_token: Token![=], 33 value: LitStr, 34 }, 35 Class { 36 class_token: kw::class, 37 _eq_token: Token![=], 38 value: LitStr, 39 }, 40 MethodName { 41 method_name_token: kw::method_name, 42 _eq_token: Token![=], 43 value: LitStr, 44 }, 45 PanicReturns { 46 panic_returns_token: kw::panic_returns, 47 _eq_token: Token![=], 48 value: Expr, 49 }, 50 } 51 52 impl Parse for MetaArg { parse(stream: ParseStream<'_>) -> syn::Result<Self>53 fn parse(stream: ParseStream<'_>) -> syn::Result<Self> { 54 let lookahead = stream.lookahead1(); 55 if lookahead.peek(kw::package) { 56 Ok(MetaArg::Package { 57 package_token: stream.parse::<kw::package>()?, 58 _eq_token: stream.parse()?, 59 value: stream.parse()?, 60 }) 61 } else if lookahead.peek(kw::class) { 62 Ok(MetaArg::Class { 63 class_token: stream.parse::<kw::class>()?, 64 _eq_token: stream.parse()?, 65 value: stream.parse()?, 66 }) 67 } else if lookahead.peek(kw::method_name) { 68 Ok(MetaArg::MethodName { 69 method_name_token: stream.parse::<kw::method_name>()?, 70 _eq_token: stream.parse()?, 71 value: stream.parse()?, 72 }) 73 } else if lookahead.peek(kw::panic_returns) { 74 Ok(MetaArg::PanicReturns { 75 panic_returns_token: stream.parse::<kw::panic_returns>()?, 76 _eq_token: stream.parse()?, 77 value: stream.parse()?, 78 }) 79 } else { 80 Err(lookahead.error()) 81 } 82 } 83 } 84 85 #[cfg(test)] 86 #[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] 87 mod tests { 88 use super::*; 89 use syn::parse_quote; 90 91 #[test] parse_meta_arg_package()92 fn parse_meta_arg_package() { 93 let MetaArg::Package { value, .. } = parse_quote!(package = "com.example") else { 94 panic!("failed to parse") 95 }; 96 97 assert_eq!("com.example", value.value()); 98 } 99 100 #[test] parse_meta_arg_class()101 fn parse_meta_arg_class() { 102 let MetaArg::Class { value, .. } = parse_quote!(class = "Foo") else { 103 panic!("failed to parse") 104 }; 105 106 assert_eq!("Foo", value.value()); 107 } 108 109 #[test] parse_meta_arg_panic_returns()110 fn parse_meta_arg_panic_returns() { 111 let MetaArg::PanicReturns { value, .. } = parse_quote!(panic_returns = { foo() }) else { 112 panic!("failed to parse") 113 }; 114 115 let syn::Expr::Block(_) = value else { 116 panic!("not a block expression") 117 }; 118 } 119 } 120