blob: b4ba8ed887145a0fccfc5a6e332ab4ca8454aeec [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
proc_macro2::TokenStream as TokenStream2,
quote::{quote, TokenStreamExt},
syn,
};
pub fn gen_visit_str(ty: Option<TokenStream2>, expected: &syn::LitStr) -> TokenStream2 {
let ret = match ty {
Some(ty) => quote!(#ty(value)),
None => quote!(value),
};
quote! {
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let value = value
.parse()
.map_err(|e| match e {
ParseError::InvalidValue => {
E::invalid_value(serde::de::Unexpected::Str(value), &#expected)
}
ParseError::InvalidLength => {
E::invalid_length(value.len(), &#expected)
}
e => {
panic!("unexpected parse error: {:?}", e);
}
})?;
Ok(#ret)
}
}
}
pub fn gen_visit_seq(
ty: TokenStream2,
inner_type: &syn::Path,
expected: &syn::LitStr,
min_length: Option<usize>,
unique_items: bool,
) -> TokenStream2 {
let inner = {
let mut tokens = quote!();
tokens.append_all(quote! {
let mut elements = vec![];
while let Some(e) = seq.next_element::<#inner_type>()? {
elements.push(e);
}
});
if let Some(min_length) = min_length {
tokens.append_all(quote! {
if elements.len() < #min_length {
return Err(serde::de::Error::invalid_length(elements.len(), &#expected));
}
});
}
if unique_items {
tokens.append_all(quote! {
let mut items = std::collections::HashSet::new();
for e in &elements {
if !items.insert(e) {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Other(
"array with duplicate element"),
&#expected)
);
}
}
Ok(#ty(elements))
});
} else {
tokens.append_all(quote! {
Ok(#ty(elements))
});
}
tokens
};
let mut tokens = quote!();
tokens.append_all(quote! {
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>
{
#inner
}
});
tokens
}
pub fn extract_inner_type(
ast: &syn::DeriveInput,
attr: syn::MetaNameValue,
inner_type: &mut Option<syn::LitStr>,
) -> Result<(), syn::Error> {
match attr.lit {
syn::Lit::Str(l) => {
if inner_type.is_some() {
return Err(syn::Error::new_spanned(ast, "duplicate `inner_type` attribute"));
}
*inner_type = Some(l);
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"`inner_type` attribute value must be string",
));
}
}
Ok(())
}
pub fn extract_min_length(
ast: &syn::DeriveInput,
attr: syn::MetaNameValue,
min_length: &mut Option<usize>,
) -> Result<(), syn::Error> {
match attr.lit {
syn::Lit::Int(l) => {
if min_length.is_some() {
return Err(syn::Error::new_spanned(ast, "duplicate `min_length` attribute"));
}
let l: usize = l.base10_parse().map_err(|_| {
syn::Error::new_spanned(ast, "`min_length` attribute is not base 10")
})?;
*min_length = Some(l);
}
_ => {
return Err(syn::Error::new_spanned(ast, "`min_length` attribute value must be int"));
}
}
Ok(())
}
pub fn extract_unique_items(
ast: &syn::DeriveInput,
attr: syn::MetaNameValue,
unique_items: &mut Option<bool>,
) -> Result<(), syn::Error> {
match attr.lit {
syn::Lit::Bool(b) => {
if unique_items.is_some() {
return Err(syn::Error::new_spanned(ast, "duplicate `unique_items` attribute"));
}
*unique_items = Some(b.value);
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"`unique_items` attribute value must be bool",
));
}
}
Ok(())
}
pub fn ident_from_path(path: &syn::Path) -> String {
path.get_ident().map(|i| i.to_string()).unwrap_or_else(|| String::new())
}
pub fn extract_expected(
ast: &syn::DeriveInput,
attr: syn::MetaNameValue,
expected: &mut Option<syn::LitStr>,
) -> Result<(), syn::Error> {
match attr.lit {
syn::Lit::Str(l) => {
if expected.is_some() {
return Err(syn::Error::new_spanned(ast, "duplicate `expected` attribute"));
}
*expected = Some(l);
}
_ => {
return Err(syn::Error::new_spanned(ast, "`expected` attribute value must be string"));
}
}
Ok(())
}