zerocopy_derive/
ext.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use proc_macro2::{Span, TokenStream};
10use quote::ToTokens;
11use syn::{Data, DataEnum, DataStruct, DataUnion, Field, Ident, Index, Type, Visibility};
12
13pub(crate) trait DataExt {
14    /// Extracts the names and types of all fields. For enums, extracts the
15    /// names and types of fields from each variant. For tuple structs, the
16    /// names are the indices used to index into the struct (ie, `0`, `1`, etc).
17    ///
18    /// FIXME: Extracting field names for enums doesn't really make sense. Types
19    /// makes sense because we don't care about where they live - we just care
20    /// about transitive ownership. But for field names, we'd only use them when
21    /// generating is_bit_valid, which cares about where they live.
22    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
23
24    fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>>;
25
26    fn tag(&self) -> Option<Ident>;
27}
28
29impl DataExt for Data {
30    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
31        match self {
32            Data::Struct(strc) => strc.fields(),
33            Data::Enum(enm) => enm.fields(),
34            Data::Union(un) => un.fields(),
35        }
36    }
37
38    fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
39        match self {
40            Data::Struct(strc) => strc.variants(),
41            Data::Enum(enm) => enm.variants(),
42            Data::Union(un) => un.variants(),
43        }
44    }
45
46    fn tag(&self) -> Option<Ident> {
47        match self {
48            Data::Struct(strc) => strc.tag(),
49            Data::Enum(enm) => enm.tag(),
50            Data::Union(un) => un.tag(),
51        }
52    }
53}
54
55impl DataExt for DataStruct {
56    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
57        map_fields(&self.fields)
58    }
59
60    fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
61        vec![self.fields()]
62    }
63
64    fn tag(&self) -> Option<Ident> {
65        None
66    }
67}
68
69impl DataExt for DataEnum {
70    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
71        map_fields(self.variants.iter().flat_map(|var| &var.fields))
72    }
73
74    fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
75        self.variants.iter().map(|var| map_fields(&var.fields)).collect()
76    }
77
78    fn tag(&self) -> Option<Ident> {
79        Some(Ident::new("___ZerocopyTag", Span::call_site()))
80    }
81}
82
83impl DataExt for DataUnion {
84    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
85        map_fields(&self.fields.named)
86    }
87
88    fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
89        vec![self.fields()]
90    }
91
92    fn tag(&self) -> Option<Ident> {
93        None
94    }
95}
96
97fn map_fields<'a>(
98    fields: impl 'a + IntoIterator<Item = &'a Field>,
99) -> Vec<(&'a Visibility, TokenStream, &'a Type)> {
100    fields
101        .into_iter()
102        .enumerate()
103        .map(|(idx, f)| {
104            (
105                &f.vis,
106                f.ident
107                    .as_ref()
108                    .map(ToTokens::to_token_stream)
109                    .unwrap_or_else(|| Index::from(idx).to_token_stream()),
110                &f.ty,
111            )
112        })
113        .collect()
114}