syn_codegen/
lib.rs

1//! # Data structures that describe Syn's syntax tree.
2//!
3//! The Syn syntax tree is made up of more than 200 types. Occasionally it can
4//! come up that you need to implement some behavior across them all.
5//!
6//! - For example [the Rust integration for AST Explorer][astexplorer] wants to
7//!   turn a syntax tree from Syn into a JavaScript value understood by the
8//!   platform's existing cross-language syntax tree visualization code.
9//!
10//!   [astexplorer]: https://astexplorer.net/#/gist/388150a52f74d45a355d2b5e865ded96/0c6d563f28d900472f699c21a1845ad20ae9927f
11//!
12//! - As another example from within Syn itself, the traits and implementations
13//!   of the [`visit`], [`visit_mut`], and [`fold`] modules can be generated
14//!   programmatically from a description of the syntax tree.
15//!
16//!   [`visit`]: https://docs.rs/syn/2.0/syn/visit/index.html
17//!   [`visit_mut`]: https://docs.rs/syn/2.0/syn/visit_mut/index.html
18//!   [`fold`]: https://docs.rs/syn/2.0/syn/fold/index.html
19//!
20//! To make this type of code as easy as possible to implement in any language,
21//! every Syn release comes with a machine-readable description of that version
22//! of the syntax tree as a JSON file [syn.json]. This `syn-codegen` crate
23//! provides the canonical data structures for parsing and making use of the
24//! representation in syn.json from Rust code.
25//!
26//! [syn.json]: https://raw.githubusercontent.com/dtolnay/syn/master/syn.json
27//!
28//! ## Example
29//!
30//! ```
31//! use syn_codegen::Definitions;
32//!
33//! # const IGNORE: &str = stringify! {
34//! const SYN: &str = include_str!("syn.json");
35//! # };
36//! # const SYN: &str = include_str!("../../syn.json");
37//!
38//! fn main() {
39//!     let defs: Definitions = serde_json::from_str(SYN).unwrap();
40//!
41//!     for node in &defs.types {
42//!         println!("syn::{}", node.ident);
43//!     }
44//! }
45//! ```
46
47#![doc(html_root_url = "https://docs.rs/syn-codegen/0.4.2")]
48
49use indexmap::IndexMap;
50use semver::Version;
51#[cfg(feature = "serde")]
52use serde::de::{Deserialize, Deserializer};
53#[cfg(feature = "serde")]
54use serde_derive::{Deserialize, Serialize};
55use std::collections::{BTreeMap, BTreeSet};
56
57/// Top-level content of the syntax tree description.
58#[derive(Clone, Debug, PartialEq)]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60pub struct Definitions {
61    /// The Syn version whose syntax tree is described by this data.
62    pub version: Version,
63
64    /// Syntax tree types defined by Syn.
65    pub types: Vec<Node>,
66
67    /// Token types defined by Syn (keywords as well as punctuation).
68    ///
69    /// The keys in the map are the Rust type name for the token. The values in
70    /// the map are the printed token representation.
71    ///
72    /// These tokens are accessible in the Syn public API as `syn::token::#name`
73    /// or alternatively `syn::Token![#repr]`.
74    pub tokens: BTreeMap<String, String>,
75}
76
77/// Syntax tree type defined by Syn.
78#[derive(Clone, Debug, PartialEq)]
79#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80pub struct Node {
81    /// Name of the type.
82    ///
83    /// This type is accessible in the Syn public API as `syn::#name`.
84    pub ident: String,
85
86    /// Features behind which this type is cfg gated.
87    pub features: Features,
88
89    /// Content of the data structure.
90    #[cfg_attr(
91        feature = "serde",
92        serde(
93            flatten,
94            skip_serializing_if = "is_private",
95            deserialize_with = "private_if_absent"
96        )
97    )]
98    pub data: Data,
99
100    #[cfg_attr(
101        feature = "serde",
102        serde(skip_serializing_if = "is_true", default = "bool_true")
103    )]
104    pub exhaustive: bool,
105}
106
107/// Content of a syntax tree data structure.
108#[derive(Clone, Debug, PartialEq)]
109#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110pub enum Data {
111    /// This is an opaque type with no publicly accessible structure.
112    Private,
113
114    /// This type is a braced struct with named fields.
115    #[cfg_attr(feature = "serde", serde(rename = "fields"))]
116    Struct(Fields),
117
118    /// This type is an enum.
119    #[cfg_attr(feature = "serde", serde(rename = "variants"))]
120    Enum(Variants),
121}
122
123/// Fields of a braced struct syntax tree node with named fields.
124///
125/// The keys in the map are the field names.
126pub type Fields = IndexMap<String, Type>;
127
128/// Variants of an enum syntax tree node.
129///
130/// The keys in the map are the variant names.
131///
132/// Variants are unit variants if they hold no data and tuple variants
133/// otherwise. The Syn syntax tree does not make use of braced variants.
134pub type Variants = IndexMap<String, Vec<Type>>;
135
136/// Type of a struct field or tuple variant field in the syntax tree.
137#[derive(Clone, Debug, PartialEq)]
138#[cfg_attr(
139    feature = "serde",
140    derive(Serialize, Deserialize),
141    serde(rename_all = "lowercase")
142)]
143pub enum Type {
144    /// Syntax tree type defined by Syn.
145    ///
146    /// This name will match the ident of some `Node`.
147    Syn(String),
148
149    /// Type defined by the Rust language or standard library.
150    ///
151    /// All such types used by Syn are accessible in the Rust prelude and can be
152    /// used without a qualifying path in most Rust code.
153    Std(String),
154
155    /// Type defined by proc-macro2.
156    ///
157    /// The type is accessible in the proc-macro2 public API as
158    /// `proc_macro2::#name`.
159    #[cfg_attr(feature = "serde", serde(rename = "proc_macro2"))]
160    Ext(String),
161
162    /// Keyword or punctuation token type defined by Syn.
163    ///
164    /// This name will match one of the keys in the `tokens` map.
165    Token(String),
166
167    /// Grouping token defined by Syn.
168    ///
169    /// The type is accessible in the Syn public API as `syn::token::#name`.
170    Group(String),
171
172    /// Punctuated list.
173    ///
174    /// This refers to `syn::punctuated::Punctuated<T, P>` with the specified
175    /// element type and punctuation.
176    Punctuated(Punctuated),
177
178    /// `std::option::Option`
179    Option(Box<Type>),
180
181    /// `std::boxed::Box`
182    Box(Box<Type>),
183
184    /// `std::vec::Vec`
185    Vec(Box<Type>),
186
187    /// Rust tuple with two or more fields.
188    Tuple(Vec<Type>),
189}
190
191/// Type of a punctuated list.
192///
193/// This refers to `syn::punctuated::Punctuated<#element, #punct>`.
194///
195/// The punct string will match one of the keys in the `tokens` map.
196#[derive(Clone, Debug, PartialEq)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198pub struct Punctuated {
199    pub element: Box<Type>,
200    pub punct: String,
201}
202
203/// Features behind which a syntax tree type is cfg gated.
204#[derive(Clone, Debug, Default, PartialEq)]
205#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
206pub struct Features {
207    /// Type is accessible if at least one of these features is enabled against
208    /// the Syn dependency.
209    pub any: BTreeSet<String>,
210}
211
212#[cfg(feature = "serde")]
213fn is_private(data: &Data) -> bool {
214    match data {
215        Data::Private => true,
216        Data::Struct(_) | Data::Enum(_) => false,
217    }
218}
219
220#[cfg(feature = "serde")]
221fn private_if_absent<'de, D>(deserializer: D) -> Result<Data, D::Error>
222where
223    D: Deserializer<'de>,
224{
225    let option = Option::deserialize(deserializer)?;
226    Ok(option.unwrap_or(Data::Private))
227}
228
229#[cfg(feature = "serde")]
230fn is_true(b: &bool) -> bool {
231    *b
232}
233
234#[cfg(feature = "serde")]
235fn bool_true() -> bool {
236    true
237}