blob: 082beffed6a550b9faa37a0f44263ef72998669b [file] [log] [blame]
// Copyright 2020 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 crate::common::{
extract_expected, extract_min_length, extract_unique_items, gen_visit_seq, ident_from_path,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens, TokenStreamExt};
use syn::punctuated::Punctuated;
use syn::{Meta, Token};
pub fn impl_derive_checked_vec(ast: syn::DeriveInput) -> Result<TokenStream2, syn::Error> {
let attrs = parse_checked_vec_attributes(&ast)?;
struct DeserializeAndSerialize<'a> {
ty: &'a syn::Path,
inner_type: &'a syn::Path,
expected: &'a syn::LitStr,
min_length: Option<usize>,
unique_items: bool,
}
impl ToTokens for DeserializeAndSerialize<'_> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let ty = self.ty;
let inner_type = self.inner_type;
let expected = self.expected;
let min_length = self.min_length;
let unique_items = self.unique_items;
let visit_seq =
gen_visit_seq(quote!(#ty), inner_type, expected, min_length, unique_items);
tokens.append_all(quote! {
impl<'de> serde::de::Deserialize<'de> for #ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = #ty;
fn expecting(
&self,
formatter: &mut fmt::Formatter<'_>
) -> fmt::Result {
formatter.write_str(#expected)
}
#visit_seq
}
deserializer.deserialize_seq(Visitor)
}
}
impl serde::ser::Serialize for #ty {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
self.0.serialize(serializer)
}
}
});
}
}
let deserialize_and_serialize = DeserializeAndSerialize {
ty: &attrs.ty,
inner_type: &attrs.inner_type,
expected: &attrs.expected,
min_length: attrs.min_length,
unique_items: attrs.unique_items,
};
let tokens = quote! {
#deserialize_and_serialize
};
Ok(tokens)
}
/// Attributes extracted from the `derive(CheckedVec)` macro.
struct CheckedVecAttributes {
/// Type of the struct.
ty: syn::Path,
/// Type inside the Vec.
inner_type: syn::Path,
/// `expecting` string to return from the deserializer.
expected: syn::LitStr,
/// The minimum length of the vector, if any.
min_length: Option<usize>,
/// Whether all items in the vector must be unique.
unique_items: bool,
}
fn parse_checked_vec_attributes(
ast: &syn::DeriveInput,
) -> Result<CheckedVecAttributes, syn::Error> {
let inner_type;
match &ast.data {
syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unnamed(fields), .. }) => {
inner_type = get_vec_inner_type(&fields).map_err(|_| {
syn::Error::new_spanned(
ast,
"CheckedVec must be derived on a struct with one unnamed Vec field",
)
})?;
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"CheckedVec must be derived on a struct with one unnamed Vec field",
));
}
}
let mut expected = None;
let mut min_length = None;
let mut unique_items = None;
for attr in &ast.attrs {
if !attr.path().is_ident("checked_vec") {
continue;
}
match &attr.meta {
syn::Meta::List(l) => {
let nested = l.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in nested {
match meta {
syn::Meta::NameValue(attr) => {
let ident = ident_from_path(&attr.path);
match &ident as &str {
"expected" => extract_expected(ast, attr, &mut expected)?,
"min_length" => extract_min_length(ast, attr, &mut min_length)?,
"unique_items" => {
extract_unique_items(ast, attr, &mut unique_items)?
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"`checked_vec` attribute is not valid",
));
}
}
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"`checked_vec` attribute must contain name-value pairs",
))?
}
}
}
}
_ => {
return Err(syn::Error::new_spanned(
ast,
"`checked_vec` attribute value must be a list",
));
}
}
}
let ty = ast.ident.clone().into();
let expected =
expected.ok_or_else(|| syn::Error::new_spanned(ast, "`expected` attribute is missing"))?;
let unique_items = unique_items.unwrap_or(false);
Ok(CheckedVecAttributes { ty, inner_type, expected, min_length, unique_items })
}
#[derive(Debug)]
enum ParseError {
InvalidAttributes,
}
fn get_vec_inner_type(fields: &syn::FieldsUnnamed) -> Result<syn::Path, ParseError> {
if fields.unnamed.len() != 1 {
return Err(ParseError::InvalidAttributes);
}
let field = fields.unnamed.first().unwrap();
match &field.ty {
syn::Type::Path(ty) => {
if ty.path.segments.len() != 1 {
return Err(ParseError::InvalidAttributes);
}
let seg = &ty.path.segments.first().unwrap();
if &seg.ident.to_string() != "Vec" {
return Err(ParseError::InvalidAttributes);
}
match &seg.arguments {
syn::PathArguments::AngleBracketed(a) => {
if a.args.len() != 1 {
return Err(ParseError::InvalidAttributes);
}
match a.args.first().unwrap() {
syn::GenericArgument::Type(syn::Type::Path(ty)) => {
return Ok(ty.path.clone());
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
Err(ParseError::InvalidAttributes)
}