oauth1_request_derive_ios/
lib.rs1#![warn(rust_2018_idioms)]
2
3#![doc(html_root_url = "https://docs.rs/oauth1-request-derive-ios/0.0.1")]
18
19#[macro_use]
20mod meta;
21
22mod container;
23mod field;
24mod method_body;
25mod util;
26
27use proc_macro2::{Span, TokenStream};
28use proc_macro_crate::FoundCrate;
29use proc_macro_error::{abort, abort_if_dirty, emit_error, proc_macro_error};
30use quote::{quote, quote_spanned};
31use syn::spanned::Spanned;
32use syn::{
33 parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Fields, GenericParam, Generics,
34 Ident,
35};
36
37use self::container::ContainerMeta;
38use self::field::Field;
39use self::method_body::MethodBody;
40
41#[proc_macro_error]
49#[proc_macro_derive(Request, attributes(oauth1))]
50pub fn derive_oauth1_authorize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
51 let input = parse_macro_input!(input as DeriveInput);
52 expand_derive_oauth1_authorize(input).into()
53}
54
55fn expand_derive_oauth1_authorize(mut input: DeriveInput) -> TokenStream {
56 let name = &input.ident;
57 let span = input.span();
58
59 let meta = ContainerMeta::new(input.attrs);
60
61 let use_oauth1_request_ios = if let Some(krate) = meta.krate {
62 quote! {
63 use #krate as _oauth1_request_ios;
64 }
65 } else {
66 let krate;
67 let krate = match proc_macro_crate::crate_name("oauth1-request-ios") {
68 Ok(FoundCrate::Name(k)) => {
69 krate = k;
70 &*krate
71 }
72 Ok(FoundCrate::Itself) => {
74 krate = std::env::var("CARGO_CRATE_NAME").unwrap();
75 &*krate
76 }
77 Err(proc_macro_crate::Error::CargoManifestDirNotSet) => "oauth1_request_ios",
78 Err(e) => panic!("{:?}", e),
79 };
80 let krate = Ident::new(krate, Span::call_site());
81 quote! {
82 extern crate #krate as _oauth1_request_ios;
83 }
84 };
85
86 add_trait_bounds(&mut input.generics);
87 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
88
89 proc_macro_error::set_dummy(quote! {
90 const _: () = {
91 #use_oauth1_request_ios
92
93 impl #impl_generics _oauth1_request_ios::Request for #name #ty_generics
94 #where_clause
95 {
96 fn serialize<S>(&self, serializer: S) -> S::Output
97 where
98 S: _oauth1_request_ios::serializer::Serializer,
99 {
100 unimplemented!();
101 }
102 }
103 };
104 });
105
106 let fields = match input.data {
107 Data::Struct(DataStruct {
108 fields: Fields::Named(fields),
109 ..
110 }) => fields,
111 _ => abort!(span, "expected a struct with named fields"),
112 };
113
114 let mut fields: Vec<_> = fields.named.into_iter().map(Field::new).collect();
115
116 fields.sort_by_cached_key(|f| f.name().string_value());
117 fields.iter().fold(String::new(), |prev_name, f| {
118 let name = f.name();
119 let (name, span) = (name.string_value(), name.span());
120 if name == prev_name {
121 emit_error!(span, "duplicate parameter \"{}\"", name);
122 }
123 name
124 });
125
126 abort_if_dirty();
127
128 let body = MethodBody::new(&fields);
129
130 quote_spanned! {Span::mixed_site()=>
131 const _: () = {
132 #use_oauth1_request_ios
133
134 #[automatically_derived]
135 impl #impl_generics _oauth1_request_ios::Request for #name #ty_generics
136 #where_clause
137 {
138 fn serialize<_S>(&self, mut serializer: _S) -> _S::Output
141 where
142 _S: _oauth1_request_ios::serializer::Serializer,
143 {
144 #body
145 }
146 }
147 };
148 }
149}
150
151fn add_trait_bounds(generics: &mut Generics) {
152 for param in &mut generics.params {
153 if let GenericParam::Type(ref mut type_param) = *param {
154 type_param.bounds.push(parse_quote!(::core::fmt::Display));
155 }
156 }
157}