syn_serde/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4<!-- tidy:crate-doc:start -->
5Library to serialize and deserialize [Syn] syntax trees.
6
7## Usage
8
9Add this to your `Cargo.toml`:
10
11```toml
12[dependencies]
13syn-serde = "0.3"
14```
15
16*Compiler support: requires rustc 1.56+*
17
18## Examples
19
20```toml
21[dependencies]
22syn-serde = { version = "0.3", features = ["json"] }
23syn = { version = "2", features = ["full"] }
24```
25
26```rust
27use syn_serde::json;
28
29let syn_file: syn::File = syn::parse_quote! {
30    fn main() {
31        println!("Hello, world!");
32    }
33};
34
35println!("{}", json::to_string_pretty(&syn_file));
36```
37
38This prints the following JSON:
39
40```json
41{
42  "items": [
43    {
44      "fn": {
45        "ident": "main",
46        "inputs": [],
47        "output": null,
48        "stmts": [
49          {
50            "semi": {
51              "macro": {
52                "path": {
53                  "segments": [
54                    {
55                      "ident": "println"
56                    }
57                  ]
58                },
59                "delimiter": "paren",
60                "tokens": [
61                  {
62                    "lit": "\"Hello, world!\""
63                  }
64                ]
65              }
66            }
67          }
68        ]
69      }
70    }
71  ]
72}
73```
74
75### Rust source file -> JSON representation of the syntax tree
76
77The [`rust2json`] example parse a Rust source file into a `syn_serde::File`
78and print out a JSON representation of the syntax tree.
79
80### JSON file -> Rust syntax tree
81
82The [`json2rust`] example parse a JSON file into a `syn_serde::File` and
83print out a Rust syntax tree.
84
85## Optional features
86
87- **`json`** — Provides functions for JSON <-> Rust serializing and
88  deserializing.
89
90## Relationship to Syn
91
92syn-serde is a fork of [Syn], and syn-serde provides a set of data structures
93similar but not identical to [Syn]. All data structures provided by syn-serde
94can be converted to the data structures of [Syn] and [proc-macro2].
95
96The data structures of syn-serde 0.3 is compatible with the data structures of
97[Syn] 2.x.
98
99[Syn]: https://github.com/dtolnay/syn
100[proc-macro2]: https://github.com/alexcrichton/proc-macro2
101[`rust2json`]: https://github.com/taiki-e/syn-serde/tree/HEAD/examples/rust2json
102[`json2rust`]: https://github.com/taiki-e/syn-serde/tree/HEAD/examples/json2rust
103
104<!-- tidy:crate-doc:end -->
105*/
106
107#![doc(test(
108    no_crate_inject,
109    attr(
110        deny(warnings, rust_2018_idioms, single_use_lifetimes),
111        allow(dead_code, unused_variables)
112    )
113))]
114#![forbid(unsafe_code)]
115#![warn(
116    rust_2018_idioms,
117    single_use_lifetimes,
118    unreachable_pub,
119    clippy::pedantic,
120    // Lints that may help when writing public library.
121    // missing_debug_implementations,
122    // missing_docs,
123    clippy::alloc_instead_of_core,
124    // clippy::exhaustive_enums, // TODO
125    // clippy::exhaustive_structs, // TODO
126    clippy::impl_trait_in_params,
127    // clippy::missing_inline_in_public_items,
128    // clippy::std_instead_of_alloc,
129    clippy::std_instead_of_core,
130)]
131#![allow(
132    clippy::enum_glob_use,
133    clippy::missing_errors_doc,
134    clippy::module_name_repetitions,
135    clippy::needless_doctest_main,
136    clippy::struct_excessive_bools,
137    clippy::used_underscore_binding,
138    clippy::wildcard_imports
139)]
140#![cfg_attr(docsrs, feature(doc_cfg))]
141
142#[macro_use]
143mod macros;
144
145#[path = "gen/ast_struct.rs"]
146mod ast_struct;
147
148#[path = "gen/ast_enum.rs"]
149mod ast_enum;
150
151#[path = "gen/convert.rs"]
152mod convert;
153
154mod attr {
155    #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
156    pub use crate::{
157        ast_enum::{AttrStyle, Meta},
158        ast_struct::{Attribute, MetaList, MetaNameValue},
159    };
160}
161#[doc(hidden)]
162pub use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
163
164mod data;
165pub(crate) use crate::data::assert_struct_semi;
166#[doc(hidden)]
167pub use crate::data::{Field, Fields, FieldsNamed, FieldsUnnamed, Variant};
168
169mod expr;
170#[doc(hidden)]
171pub use crate::expr::{
172    Arm, Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak,
173    ExprCall, ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprGroup,
174    ExprIf, ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall,
175    ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry,
176    ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, Index, Label,
177    Member, RangeLimits,
178};
179
180mod file {
181    #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
182    pub use crate::ast_struct::File;
183}
184#[doc(hidden)]
185pub use crate::file::File;
186
187mod generics;
188#[doc(hidden)]
189pub use crate::generics::{
190    BoundLifetimes, ConstParam, GenericParam, Generics, LifetimeParam, PredicateLifetime,
191    PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause,
192    WherePredicate,
193};
194
195mod item;
196#[doc(hidden)]
197pub use crate::item::{
198    FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType,
199    ImplItem, ImplItemConst, ImplItemFn, ImplItemMacro, ImplItemType, ImplRestriction, Item,
200    ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod,
201    ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Receiver,
202    Signature, StaticMutability, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro,
203    TraitItemType, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic,
204};
205
206mod lifetime {
207    #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
208    pub use crate::ast_struct::Lifetime;
209}
210#[doc(hidden)]
211pub use crate::lifetime::Lifetime;
212
213mod lit;
214#[doc(hidden)]
215pub use crate::lit::{
216    Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr, StrStyle,
217};
218
219mod mac {
220    #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
221    pub use crate::{ast_enum::MacroDelimiter, ast_struct::Macro};
222}
223#[doc(hidden)]
224pub use crate::mac::{Macro, MacroDelimiter};
225
226mod op {
227    #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
228    pub use crate::ast_enum::{BinOp, UnOp};
229}
230#[doc(hidden)]
231pub use crate::op::{BinOp, UnOp};
232
233mod pat;
234#[doc(hidden)]
235pub use crate::expr::{
236    ExprConst as PatConst, ExprLit as PatLit, ExprMacro as PatMacro, ExprPath as PatPath,
237    ExprRange as PatRange,
238};
239#[doc(hidden)]
240pub use crate::pat::{
241    FieldPat, Pat, PatIdent, PatOr, PatParen, PatReference, PatRest, PatSlice, PatStruct, PatTuple,
242    PatTupleStruct, PatType, PatWild,
243};
244
245mod path;
246#[doc(hidden)]
247pub use crate::path::{
248    AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
249    ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
250};
251
252mod restriction;
253#[doc(hidden)]
254pub use crate::restriction::{FieldMutability, VisRestricted, Visibility};
255
256mod stmt;
257#[doc(hidden)]
258pub use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
259
260mod ty;
261#[doc(hidden)]
262pub use crate::ty::{
263    Abi, BareFnArg, BareVariadic, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup,
264    TypeImplTrait, TypeMacro, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice,
265    TypeTraitObject, TypeTuple,
266};
267
268mod token_stream;
269#[doc(hidden)]
270pub use crate::token_stream::{
271    Delimiter, Group, Ident, Literal, Punct, Spacing, TokenStream, TokenTree,
272};
273
274#[cfg(feature = "json")]
275#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
276pub mod json;
277
278mod sealed {
279    pub trait Sealed {}
280}
281
282// =============================================================================
283// Syn trait
284
285/// A trait for the data structures of [Syn] and [proc-macro2].
286///
287/// [Syn]: https://github.com/dtolnay/syn
288/// [proc-macro2]: https://github.com/alexcrichton/proc-macro2
289#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
290pub trait Syn: Sized + sealed::Sealed {
291    type Adapter: Serialize + for<'de> Deserialize<'de>;
292
293    /// Converts a `Syn` type into an adapter.
294    ///
295    /// # Examples
296    ///
297    /// ```rust
298    /// # #[cfg(feature = "json")]
299    /// # fn dox() {
300    /// use syn_serde::Syn;
301    ///
302    /// let syn_file: syn::File = syn::parse_quote! {
303    ///     fn main() {
304    ///         println!("Hello, world!");
305    ///     }
306    /// };
307    ///
308    /// let serializable_file = syn_file.to_adapter();
309    /// println!("{}", serde_json::to_string_pretty(&serializable_file).unwrap());
310    /// # }
311    /// ```
312    fn to_adapter(&self) -> Self::Adapter;
313
314    /// Converts an adapter into a `Syn` type.
315    ///
316    /// # Examples
317    ///
318    /// ```rust
319    /// # #[cfg(feature = "json")]
320    /// # fn dox() -> Result<(), Box<dyn std::error::Error>> {
321    /// use syn_serde::Syn;
322    ///
323    /// // `struct Unit;`
324    /// let json = r#"{
325    ///   "struct": {
326    ///     "ident": "Unit",
327    ///     "fields": "unit"
328    ///   }
329    /// }"#;
330    ///
331    /// let serializable_file: <syn::File as Syn>::Adapter = serde_json::from_str(json)?;
332    /// let syn_file = syn::File::from_adapter(&serializable_file);
333    /// # Ok(())
334    /// # }
335    /// ```
336    fn from_adapter(adapter: &Self::Adapter) -> Self;
337}
338
339// =============================================================================
340
341use core::ops;
342
343use proc_macro2::Span;
344use serde::{de::Deserialize, ser::Serialize};
345use serde_derive::{Deserialize, Serialize};
346
347type Punctuated<T> = Vec<T>;
348
349fn default<T>() -> T
350where
351    T: Default,
352{
353    T::default()
354}
355
356fn default_or_none<T>(x: bool) -> Option<T>
357where
358    T: Default,
359{
360    if x {
361        Some(T::default())
362    } else {
363        None
364    }
365}
366
367fn not<T>(x: T) -> T::Output
368where
369    T: ops::Not,
370{
371    !x
372}
373
374// https://github.com/rust-lang/rust/issues/51443
375trait RefInto<U>: Sized {
376    fn ref_into<'a>(&'a self) -> U
377    where
378        &'a Self: Into<U>,
379    {
380        self.into()
381    }
382}
383
384impl<T, U> RefInto<U> for T {}
385
386trait MapInto<U, M> {
387    type T;
388
389    fn ref_map<'a, F>(&'a self, f: F) -> M
390    where
391        Self::T: 'a,
392        F: FnMut(&'a Self::T) -> U;
393
394    fn map_into<'a>(&'a self) -> M
395    where
396        Self::T: 'a,
397        &'a Self::T: Into<U>,
398    {
399        self.ref_map(Into::into)
400    }
401}
402
403impl<T, U> MapInto<U, Vec<U>> for Vec<T> {
404    type T = T;
405
406    fn ref_map<'a, F>(&'a self, f: F) -> Vec<U>
407    where
408        F: FnMut(&'a Self::T) -> U,
409    {
410        self.iter().map(f).collect()
411    }
412}
413
414impl<T, U, P> MapInto<U, syn::punctuated::Punctuated<U, P>> for Vec<T>
415where
416    P: Default,
417{
418    type T = T;
419
420    fn ref_map<'a, F>(&'a self, f: F) -> syn::punctuated::Punctuated<U, P>
421    where
422        F: FnMut(&'a Self::T) -> U,
423    {
424        self.iter().map(f).collect()
425    }
426}
427
428impl<T, U, P> MapInto<U, Vec<U>> for syn::punctuated::Punctuated<T, P>
429where
430    P: Default,
431{
432    type T = T;
433
434    fn ref_map<'a, F>(&'a self, f: F) -> Vec<U>
435    where
436        F: FnMut(&'a Self::T) -> U,
437    {
438        self.iter().map(f).collect()
439    }
440}
441
442impl<T, U> MapInto<U, Option<U>> for Option<T> {
443    type T = T;
444
445    fn ref_map<'a, F>(&'a self, f: F) -> Option<U>
446    where
447        F: FnMut(&'a Self::T) -> U,
448    {
449        self.as_ref().map(f)
450    }
451}
452
453impl<T, U> MapInto<U, Box<U>> for Box<T> {
454    type T = T;
455
456    fn ref_map<'a, F>(&'a self, mut f: F) -> Box<U>
457    where
458        F: FnMut(&'a Self::T) -> U,
459    {
460        Box::new(f(self))
461    }
462}