Skip to main content

syn_serde/
lib.rs

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