1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! serde_syn is a serde backend for parsing Rust syntax inside procedural
//! macros. For example, you can deserialize parameters for a custom derive out
//! of attributes and directly into structs. The goal is to eliminate the
//! parsing boilerplate that goes into writing procedural macros.
//!
//! The interface to serde_syn is fairly minimal. Here are a few ways to use it:
//! - syn requires a
//!   [`Parser`](https://docs.rs/syn/1.0/syn/parse/trait.Parser.html)
//!   implementation to process syntax inside already parsed/visited
//!   [attributes](https://docs.rs/syn/1.0/syn/struct.Attribute.html#method.parse_args_with).
//!   The [`parser`] function creates exactly that!
//! - If you are working directly with `proc_macro`/`proc_macro2` token streams
//!   or strings, you should also use [`parser`].
//! - If you are [implementing syn's `Parse`
//!   trait](https://docs.rs/syn/1.0/syn/parse/index.html) yourself, you should
//!   use the [`from_stream`] function which takes in a
//!   [`ParseStream`](https://docs.rs/syn/1.0/syn/parse/type.ParseStream.html).
//!
//! Lots of pre-made configurations exist inside the [`config`] module for
//! common syntaxes (JSON-like, attribute-like, expression-like, etc) or you can
//! combine flags to build your own.
//!
//! ## Example derive implementation
//! Here you can see a simple derive macro implementation. For more examples,
//! see [the examples
//! directory](https://gitlab.com/samsartor/serde_syn/tree/master/examples).
//!
//! ```
//! # extern crate proc_macro;
//! #
//! # use serde_syn::{parser, config};
//! # use syn::{parse_macro_input, DeriveInput};
//! # use serde_derive::{Deserialize};
//! # use proc_macro::TokenStream;
//! # use proc_macro2::Span;
//! # use quote::quote;
//! #
//! /// The format of `named` attributes.
//! #[derive(Deserialize)]
//! struct Props {
//!     rename: Option<String>, // #[named(rename="hello")]
//!     lowercase: Option<()>,  // #[named(lowercase)]
//! }
//!
//! # const IGNORE_TOKENS: &str = stringify! {
//! #[proc_macro_derive(NamedType, attributes(named))]
//! # };
//! pub fn my_macro(input: TokenStream) -> TokenStream {
//!     let input = parse_macro_input!(input as DeriveInput);
//!     let mut name = input.ident.to_string();
//!
//!     for attr in input.attrs.iter().filter(|a| a.path.is_ident("named")) {
//!         let parser = parser::<Props>(config::RUSTY_META);
//!         let props = match attr.parse_args_with(parser) {
//!             Ok(props) => props,
//!             Err(err) => return err.to_compile_error().into(),
//!         };
//!
//!         if let Some(rename) = props.rename { name = rename; }
//!         if props.lowercase.is_some() { name = name.to_lowercase(); }
//!     }
//!
//!     let ident = &input.ident;
//!     (quote! {
//!         impl NamedType for #ident {
//!             fn name() -> &'static str { #name }
//!         }
//!     }).into()
//! }
//! ```
//!
//! ## Error handling
//! Deserialization errors are automatically assigned a "span" (the area of the
//! source code that could not be parsed) before being returned from [`parser`]
//! and [`from_stream`] as ordinary
//! [`syn::Error`](https://docs.rs/syn/1.0/syn/struct.Error.html)s. When that
//! error is reported to the Rust compiler, the correct regions of code will be
//! highlighted:
//!
//! ```text
//! error: unknown field `lowrcase`, expected `rename` or `lowercase`
//!   --> named_type.rs:4:13
//!    |
//! 4  |     #[named(lowrcase)]
//!    |             ^^^^^^^^
//! ```
//!
//! If you use [`Deserializer`] directly, serde_syn will do its best to assign a
//! span but it is always possible to create an error with no span using serde's
//! required `custom` function.
//!
//!
//! ## Limitations
//! serde_syn is early in development and so still has some gotchyas. For
//! example, serde_syn will throw an error if you try to deserialize into a
//! [serde_json `Value`](https://docs.serde.rs/serde_json/value/enum.Value.html)
//! since it doesn't yet support self-description.
//!
//! If you find any bugs, have any ideas, or wind up with free time to help
//! random open source projects, please drop by [the
//! repository](https://gitlab.com/samsartor/serde_syn/).


mod error;
mod de;
pub mod config;

pub use error::Error;
pub use de::*;
pub use config::Config;

use syn::Error as SynError;
use syn::parse::{Parser, ParseStream};
use serde::{de::Deserialize, de::DeserializeOwned};

/// Create a Syn `Parser` which can deserialize the type `T` from a token
/// stream or string.
///
///
/// ```
/// # use serde_syn::{parser, config};
/// # use syn::parse::Parser;
/// # use quote::quote;
/// let from_string: Vec<String> = parser(config::RUSTY)
///     .parse_str("[\"Hello\", \"World\"]")?;
///
/// let from_tokens: Vec<String> = parser(config::RUSTY)
///     .parse2(quote! { ["Hello", "World"] })?;
/// # Ok::<(), syn::Error>(())
/// ```
pub fn parser<T: DeserializeOwned>(config: Config) -> impl Parser<Output=T> {
    // Really odd lifetimes here. Somehow this combination of annotations and
    // closures compiles.
    move |stream: ParseStream| -> syn::Result<T> {
        from_stream::<T>(config, stream)
    }
}

/// Deserialize the type `T` from Rust syntax. This will almost always be used
/// from inside a `Parse` implementation. To deserialize a token stream or
/// string, consider using [`parser`].
///
/// For example, to parse attributes of the form
/// ```text
/// #[text("Hello"), count(10)]
/// #[count = 50]
/// #[{ text: "Hello" }]
/// ```
///
/// you could create a procedual macro that uses `from_stream`:
/// ```
/// # extern crate proc_macro;
/// use proc_macro::TokenStream;
/// use syn::{parse_macro_input, bracketed, Token};
/// use syn::parse::{Parse, ParseStream};
/// use serde_derive::Deserialize;
/// use serde_syn::{from_stream, config};
///
/// #[derive(Deserialize)]
/// struct MyMacroAttr {
///     text: Option<String>,
///     #[serde(default)]
///     count: usize,
/// }
///
/// impl Parse for MyMacroAttr {
///     fn parse(input: ParseStream) -> syn::Result<Self> {
///         let pound_token: Token![#] = input.parse()?;
///         let content;
///         let bracket_token = bracketed!(content in input);
///         from_stream(config::RUSTY_META, &content)
///     }
/// }
///
/// # const IGNORE_TOKENS: &str = stringify! {
/// #[proc_macro]
/// # };
/// pub fn my_macro(input: TokenStream) -> TokenStream {
///     let attributes = parse_macro_input!(input as MyMacroAttr);
///
///     /* your macro here */
/// #   TokenStream::new()
/// }
/// ```
pub fn from_stream<'a, T>(config: Config, tokens: ParseStream<'a>)
    -> Result<T, SynError>
    where T: Deserialize<'a>
{
    let span = tokens.cursor().span();
    Deserialize::deserialize(Deserializer::new(config, tokens))
        .map_err(|e| e.to_syn(span))
}