woops
This commit is contained in:
parent
51be5dcf55
commit
1453885ed2
50 changed files with 17263 additions and 1 deletions
|
@ -1 +0,0 @@
|
||||||
Subproject commit dff506bb8a83702e2dc82b17177dda43e6de0f3a
|
|
22
syn-serde/.editorconfig
Normal file
22
syn-serde/.editorconfig
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# EditorConfig configuration
|
||||||
|
# https://editorconfig.org
|
||||||
|
|
||||||
|
# Top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file, utf-8 charset
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Match rust/toml, set 4 space indentation
|
||||||
|
[*.{rs,toml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Match json/yaml/markdown, set 2 space indentation
|
||||||
|
[*.{json,yml,md}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
4
syn-serde/.gitattributes
vendored
Normal file
4
syn-serde/.gitattributes
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
|
||||||
|
|
||||||
|
* text=auto eol=lf
|
||||||
|
*.rs rust
|
1
syn-serde/.github/CODEOWNERS
vendored
Normal file
1
syn-serde/.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* @taiki-e
|
10
syn-serde/.github/bors.toml
vendored
Normal file
10
syn-serde/.github/bors.toml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
status = [
|
||||||
|
"test (1.31.0)",
|
||||||
|
"test (stable)",
|
||||||
|
"test (beta)",
|
||||||
|
"test (nightly)",
|
||||||
|
"style (clippy)",
|
||||||
|
"style (rustfmt)",
|
||||||
|
"style (rustdoc)",
|
||||||
|
]
|
||||||
|
delete_merged_branches = true
|
95
syn-serde/.github/workflows/ci.yml
vendored
Normal file
95
syn-serde/.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- staging
|
||||||
|
- trying
|
||||||
|
schedule:
|
||||||
|
- cron: '00 01 * * *'
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Dwarnings
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust:
|
||||||
|
# This is the minimum supported Rust version of this crate.
|
||||||
|
# When updating this, the reminder to update the minimum supported
|
||||||
|
# Rust version in README.md.
|
||||||
|
#
|
||||||
|
# Tests are not run as tests may require newer versions of Rust.
|
||||||
|
- 1.31.0
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
- nightly
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Install Rust
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
. ./ci/install-rust.sh ${{ matrix.rust }}
|
||||||
|
- name: Install cargo-hack
|
||||||
|
if: matrix.rust != '1.31.0'
|
||||||
|
run: |
|
||||||
|
cargo install cargo-hack
|
||||||
|
- name: cargo check
|
||||||
|
if: matrix.rust == '1.31.0'
|
||||||
|
run: |
|
||||||
|
cargo check --all-features
|
||||||
|
- name: cargo test
|
||||||
|
if: matrix.rust != '1.31.0'
|
||||||
|
run: |
|
||||||
|
cargo test --all-features
|
||||||
|
- name: cargo hack check --each-feature
|
||||||
|
if: matrix.rust != '1.31.0'
|
||||||
|
run: |
|
||||||
|
cargo hack check --all --each-feature --no-dev-deps
|
||||||
|
# Refs: https://github.com/rust-lang/cargo/issues/5657
|
||||||
|
- name: cargo check -Zminimal-versions
|
||||||
|
if: matrix.rust == 'nightly'
|
||||||
|
run: |
|
||||||
|
cargo update -Zminimal-versions
|
||||||
|
cargo hack check --all --all-features --no-dev-deps --ignore-private
|
||||||
|
|
||||||
|
style:
|
||||||
|
name: style
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
component:
|
||||||
|
- clippy
|
||||||
|
- rustfmt
|
||||||
|
- rustdoc
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Install Rust
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
. ./ci/install-rust.sh
|
||||||
|
- name: Install component
|
||||||
|
if: matrix.component != 'rustdoc'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
. ./ci/install-component.sh ${{ matrix.component }}
|
||||||
|
- name: cargo clippy
|
||||||
|
if: matrix.component == 'clippy'
|
||||||
|
run: |
|
||||||
|
cargo clippy --all --all-features --all-targets
|
||||||
|
- name: cargo fmt -- --check
|
||||||
|
if: matrix.component == 'rustfmt'
|
||||||
|
run: |
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
- name: cargo doc
|
||||||
|
if: matrix.component == 'rustdoc'
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: -Dwarnings
|
||||||
|
run: |
|
||||||
|
cargo doc --no-deps --all --all-features
|
7
syn-serde/.gitignore
vendored
Normal file
7
syn-serde/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# For platform and editor specific settings, it is recommended to add to
|
||||||
|
# a global .gitignore file.
|
||||||
|
# Refs: https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore
|
21
syn-serde/CHANGELOG.md
Normal file
21
syn-serde/CHANGELOG.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
This project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
# [0.2.0] - 2019-09-16
|
||||||
|
|
||||||
|
* [Removed error from `to_string` / `to_vec`.][e949263]
|
||||||
|
|
||||||
|
[e949263]: https://github.com/taiki-e/syn-serde/commit/e9492636eb7d58565fc415e55fd824b06b37f3d3
|
||||||
|
|
||||||
|
# [0.1.0] - 2019-09-16
|
||||||
|
|
||||||
|
Initial release
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/taiki-e/syn-serde/compare/v0.2.0...HEAD
|
||||||
|
[0.2.0]: https://github.com/taiki-e/syn-serde/compare/v0.1.0...v0.2.0
|
||||||
|
[0.1.0]: https://github.com/taiki-e/syn-serde/releases/tag/v0.1.0
|
36
syn-serde/Cargo.toml
Normal file
36
syn-serde/Cargo.toml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
[package]
|
||||||
|
name = "syn-serde"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["David Tolnay <dtolnay@gmail.com>", "Taiki Endo <te316e89@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "Apache-2.0 OR MIT"
|
||||||
|
repository = "https://github.com/taiki-e/syn-serde"
|
||||||
|
homepage = "https://github.com/taiki-e/syn-serde"
|
||||||
|
documentation = "https://docs.rs/syn-serde"
|
||||||
|
keywords = ["serde", "serialization", "syn"]
|
||||||
|
categories = ["development-tools::procedural-macro-helpers", "encoding"]
|
||||||
|
readme = "README.md"
|
||||||
|
description = """
|
||||||
|
Library to serialize and deserialize Syn syntax trees.
|
||||||
|
"""
|
||||||
|
|
||||||
|
[features]
|
||||||
|
json = ["serde_json"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = { version = "1.0", default-features = false }
|
||||||
|
serde = { version = "1.0.99", features = ["derive"] }
|
||||||
|
serde_derive = "1.0.99" # This is necessary to make `-Z minimal-versions` build successful.
|
||||||
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
syn = { version = "1.0.5", default-features = false, features = ["full"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
quote = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
|
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
|
||||||
|
[package.metadata.playground]
|
||||||
|
all-features = true
|
201
syn-serde/LICENSE-APACHE
Normal file
201
syn-serde/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
23
syn-serde/LICENSE-MIT
Normal file
23
syn-serde/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
130
syn-serde/README.md
Normal file
130
syn-serde/README.md
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
# syn-serde
|
||||||
|
|
||||||
|
[![crates-badge]][crates-url]
|
||||||
|
[![docs-badge]][docs-url]
|
||||||
|
[![license-badge]][license]
|
||||||
|
[![rustc-badge]][rustc-url]
|
||||||
|
|
||||||
|
[crates-badge]: https://img.shields.io/crates/v/syn-serde.svg
|
||||||
|
[crates-url]: https://crates.io/crates/syn-serde
|
||||||
|
[docs-badge]: https://docs.rs/syn-serde/badge.svg
|
||||||
|
[docs-url]: https://docs.rs/syn-serde
|
||||||
|
[license-badge]: https://img.shields.io/crates/l/syn-serde.svg
|
||||||
|
[license]: #license
|
||||||
|
[rustc-badge]: https://img.shields.io/badge/rustc-1.31+-lightgray.svg
|
||||||
|
[rustc-url]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
|
||||||
|
|
||||||
|
Library to serialize and deserialize [Syn] syntax trees.
|
||||||
|
|
||||||
|
[**Documentation**][docs-url]
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add this to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
syn-serde = "0.2"
|
||||||
|
```
|
||||||
|
|
||||||
|
The current syn-serde requires Rust 1.31 or later.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
syn-serde = { version = "0.2", features = ["json"] }
|
||||||
|
syn = { version = "1", features = ["full"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use syn_serde::json;
|
||||||
|
|
||||||
|
let syn_file: syn::File = syn::parse_quote! {
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", json::to_string_pretty(&syn_file));
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints the following JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"fn": {
|
||||||
|
"ident": "main",
|
||||||
|
"inputs": [],
|
||||||
|
"output": null,
|
||||||
|
"stmts": [
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"macro": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "println"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delimiter": "paren",
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"lit": "\"Hello, world!\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rust source file -> JSON representation of the syntax tree
|
||||||
|
|
||||||
|
The [`rust2json`] example parse a Rust source file into a `syn_serde::File`
|
||||||
|
and print out a JSON representation of the syntax tree.
|
||||||
|
|
||||||
|
[`rust2json`]: examples/rust2json
|
||||||
|
|
||||||
|
### JSON file -> Rust syntax tree
|
||||||
|
|
||||||
|
The [`json2rust`] example parse a JSON file into a `syn_serde::File` and
|
||||||
|
print out a Rust syntax tree.
|
||||||
|
|
||||||
|
[`json2rust`]: examples/json2rust
|
||||||
|
|
||||||
|
## Optional features
|
||||||
|
|
||||||
|
* **`json`** — Provides functions for JSON <-> Rust serializing and
|
||||||
|
deserializing.
|
||||||
|
|
||||||
|
## Relationship to Syn
|
||||||
|
|
||||||
|
syn-serde is a fork of [Syn], and syn-serde provides a set of data structures
|
||||||
|
similar but not identical to [Syn]. All data structures provided by syn-serde
|
||||||
|
can be converted to the data structures of [Syn] and [proc-macro2].
|
||||||
|
|
||||||
|
The data structures of syn-serde 0.2 is compatible with the data structures of [Syn] 1.0.
|
||||||
|
|
||||||
|
[Syn]: https://github.com/dtolnay/syn
|
||||||
|
[proc-macro2]: https://github.com/alexcrichton/proc-macro2
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|
29
syn-serde/ci/install-component.sh
Normal file
29
syn-serde/ci/install-component.sh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
component="${1}"
|
||||||
|
|
||||||
|
if ! rustup component add "${component}" 2>/dev/null; then
|
||||||
|
# If the component is unavailable on the latest nightly,
|
||||||
|
# use the latest toolchain with the component available.
|
||||||
|
# Refs: https://github.com/rust-lang/rustup-components-history#the-web-part
|
||||||
|
target=$(curl -sSf "https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/${component}")
|
||||||
|
echo "'${component}' is unavailable on the default toolchain, use the toolchain 'nightly-${target}' instead"
|
||||||
|
|
||||||
|
rustup update "nightly-${target}" --no-self-update
|
||||||
|
rustup default "nightly-${target}"
|
||||||
|
|
||||||
|
echo "Query rust and cargo versions:"
|
||||||
|
rustup -V
|
||||||
|
rustc -V
|
||||||
|
cargo -V
|
||||||
|
|
||||||
|
rustup component add "${component}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Query component versions:"
|
||||||
|
case "${component}" in
|
||||||
|
clippy | miri) cargo "${component}" -V ;;
|
||||||
|
rustfmt) "${component}" -V ;;
|
||||||
|
esac
|
20
syn-serde/ci/install-rust.sh
Normal file
20
syn-serde/ci/install-rust.sh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
toolchain="${1:-nightly}"
|
||||||
|
|
||||||
|
if rustup -V 2>/dev/null; then
|
||||||
|
rustup set profile minimal
|
||||||
|
rustup update "${toolchain}" --no-self-update
|
||||||
|
rustup default "${toolchain}"
|
||||||
|
else
|
||||||
|
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${toolchain}"
|
||||||
|
export PATH=${PATH}:${HOME}/.cargo/bin
|
||||||
|
echo "##[add-path]${HOME}/.cargo/bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Query rust and cargo versions:"
|
||||||
|
rustup -V
|
||||||
|
rustc -V
|
||||||
|
cargo -V
|
17
syn-serde/codegen/Cargo.toml
Normal file
17
syn-serde/codegen/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "syn-serde-internal-codegen"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["David Tolnay <dtolnay@gmail.com>", "Nika Layzell <nika@thelayzells.com>", "Taiki Endo <te316e89@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false # this is an internal crate which should never be published
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
# Prefer that `cargo clean` in syn-serde's directory does not require a rebuild of
|
||||||
|
# rustfmt in the codegen directory.
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
quote = "1.0"
|
||||||
|
rustfmt = { package = "rustfmt-nightly", git = "https://github.com/rust-lang-nursery/rustfmt" }
|
||||||
|
serde_json = "1.0"
|
||||||
|
syn-codegen = { git = "https://github.com/dtolnay/syn" }
|
12
syn-serde/codegen/README.md
Normal file
12
syn-serde/codegen/README.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# syn_serde_codegen
|
||||||
|
|
||||||
|
This is an internal (not published on crates.io) crate which is used to generate
|
||||||
|
the files in the `gen/` directory of `syn-serde`. It is used to ensure that the
|
||||||
|
implementations for `Syn` remain in sync with the
|
||||||
|
actual AST.
|
||||||
|
|
||||||
|
To run this program, run `cargo run` in this directory, and the `gen/` folder
|
||||||
|
will be re-generated.
|
||||||
|
|
||||||
|
This program is slow, and is therefore not run when building `syn-serde` as part of
|
||||||
|
the build script to save on compile time.
|
40
syn-serde/codegen/src/file.rs
Normal file
40
syn-serde/codegen/src/file.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::Result;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use std::{fmt, fs, io::Write, path::Path};
|
||||||
|
|
||||||
|
pub(crate) fn write<P: AsRef<Path>>(path: P, content: TokenStream) -> Result<()> {
|
||||||
|
let mut formatted = Vec::new();
|
||||||
|
writeln!(
|
||||||
|
formatted,
|
||||||
|
"// This file is @generated by syn-serde-internal-codegen.\n\
|
||||||
|
// It is not intended for manual editing.\n"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut config = rustfmt::Config::default();
|
||||||
|
config.set().emit_mode(rustfmt::EmitMode::Stdout);
|
||||||
|
config.set().verbose(rustfmt::Verbosity::Quiet);
|
||||||
|
config.set().format_macro_matchers(true);
|
||||||
|
config.set().normalize_doc_attributes(true);
|
||||||
|
|
||||||
|
let mut session = rustfmt::Session::new(config, Some(&mut formatted));
|
||||||
|
session.format(rustfmt::Input::Text(content.to_string())).map_err(RustfmtError)?;
|
||||||
|
drop(session);
|
||||||
|
|
||||||
|
if path.as_ref().is_file() && fs::read(&path)? == formatted {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(path, formatted)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RustfmtError(rustfmt::ErrorKind);
|
||||||
|
|
||||||
|
impl fmt::Display for RustfmtError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RustfmtError {}
|
20
syn-serde/codegen/src/gen.rs
Normal file
20
syn-serde/codegen/src/gen.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use syn_codegen::{Definitions, Node};
|
||||||
|
|
||||||
|
pub(crate) fn traverse(
|
||||||
|
defs: &Definitions,
|
||||||
|
node: fn(&mut TokenStream, &Node, &Definitions),
|
||||||
|
) -> TokenStream {
|
||||||
|
let mut types = defs.types.clone();
|
||||||
|
types.sort_by(|a, b| a.ident.cmp(&b.ident));
|
||||||
|
|
||||||
|
let mut traits = TokenStream::new();
|
||||||
|
for s in types {
|
||||||
|
if s.ident == "Reserved" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
node(&mut traits, &s, defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
traits
|
||||||
|
}
|
32
syn-serde/codegen/src/main.rs
Normal file
32
syn-serde/codegen/src/main.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Based on https://github.com/dtolnay/syn/tree/1.0.5/codegen.
|
||||||
|
//
|
||||||
|
// This crate generates the Syn trait in syn-serde programmatically from
|
||||||
|
// the syntax tree description.
|
||||||
|
|
||||||
|
#![recursion_limit = "128"]
|
||||||
|
#![warn(rust_2018_idioms, unreachable_pub)]
|
||||||
|
|
||||||
|
const SYN_JSON: &str = "../syn.json";
|
||||||
|
|
||||||
|
mod file;
|
||||||
|
mod gen;
|
||||||
|
mod serde;
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = try_main() {
|
||||||
|
eprintln!("error: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_main() -> Result<()> {
|
||||||
|
let defs = fs::read_to_string(SYN_JSON)?;
|
||||||
|
let defs = serde_json::from_str(&defs)?;
|
||||||
|
|
||||||
|
serde::generate(&defs)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
293
syn-serde/codegen/src/serde.rs
Normal file
293
syn-serde/codegen/src/serde.rs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
use crate::{file, gen, Result};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
use syn_codegen::{Data, Definitions, Node, Type};
|
||||||
|
|
||||||
|
const SERDE_SRC: &str = "../src/gen/mod.rs";
|
||||||
|
|
||||||
|
const IGNORED_TYPES: &[&str] = &[
|
||||||
|
/* we don't have them */
|
||||||
|
"DeriveInput",
|
||||||
|
"Data",
|
||||||
|
"DataStruct",
|
||||||
|
"DataEnum",
|
||||||
|
"DataUnion",
|
||||||
|
/* private */
|
||||||
|
"LitByte",
|
||||||
|
"LitByteStr",
|
||||||
|
"LitChar",
|
||||||
|
"LitFloat",
|
||||||
|
"LitInt",
|
||||||
|
"LitStr",
|
||||||
|
/* cannot be implemented by codegen */
|
||||||
|
"Type",
|
||||||
|
"UseTree",
|
||||||
|
"Visibility",
|
||||||
|
"Receiver",
|
||||||
|
/* optimize */
|
||||||
|
"Generics",
|
||||||
|
"ExprMatch",
|
||||||
|
"Arm",
|
||||||
|
"TraitItemMethod",
|
||||||
|
"ItemStruct",
|
||||||
|
"ReturnType",
|
||||||
|
];
|
||||||
|
const EMPTY_STRUCTS: &[&str] = &["TypeInfer", "TypeNever", "UseGlob", "VisCrate", "VisPublic"];
|
||||||
|
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
|
fn visit(ty: &Type, name: &TokenStream) -> (Option<TokenStream>, TokenStream) {
|
||||||
|
match ty {
|
||||||
|
Type::Box(_) | Type::Vec(_) | Type::Punctuated(_) => {
|
||||||
|
let from = Some(quote!(#name.map_into()));
|
||||||
|
let into = quote!(#name.map_into());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Option(t) => match &**t {
|
||||||
|
Type::Token(_) | Type::Group(_) => {
|
||||||
|
let from = Some(quote!(#name.is_some()));
|
||||||
|
let into = quote!(default_or_none(#name));
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Tuple(t) => {
|
||||||
|
let mut from_expr = Vec::new();
|
||||||
|
let mut from_pat = Vec::new();
|
||||||
|
let mut into_expr = Vec::new();
|
||||||
|
let mut into_pat = Vec::new();
|
||||||
|
|
||||||
|
for (i, t) in t.iter().enumerate() {
|
||||||
|
let id = format_ident!("_{}", i);
|
||||||
|
let (from, into) = visit(t, "e!((*#id)));
|
||||||
|
|
||||||
|
from_pat.push(id.clone());
|
||||||
|
into_expr.push(into);
|
||||||
|
if from.is_some() {
|
||||||
|
into_pat.push(id);
|
||||||
|
from_expr.push(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(from_pat.len(), into_expr.len());
|
||||||
|
assert_eq!(into_pat.len(), from_expr.len());
|
||||||
|
assert_ne!(into_pat.len(), 0);
|
||||||
|
|
||||||
|
if into_pat.len() == 1 {
|
||||||
|
let from = Some(quote!(#name.ref_map(|(#(#from_pat),*)| #(#from_expr),*)));
|
||||||
|
let into = quote!(#name.ref_map(|#(#into_pat),*| (#(#into_expr),*)));
|
||||||
|
(from, into)
|
||||||
|
} else {
|
||||||
|
let from = Some(quote!(#name.ref_map(|(#(#from_pat),*)| (#(#from_expr),*))));
|
||||||
|
let into = quote!(#name.ref_map(|(#(#into_pat),*)| (#(#into_expr),*)));
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Box(_) | Type::Vec(_) | Type::Punctuated(_) => {
|
||||||
|
let from = Some(quote!(#name.ref_map(MapInto::map_into)));
|
||||||
|
let into = quote!(#name.ref_map(MapInto::map_into));
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Std(t) if t == "String" => {
|
||||||
|
// `From<&String> for String` requires Rust 1.36 or later.
|
||||||
|
// Refs: https://github.com/rust-lang/rust/pull/59825
|
||||||
|
let from = Some(quote!(#name.ref_map(ToString::to_string)));
|
||||||
|
let into = quote!(#name.ref_map(ToString::to_string));
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let from = Some(quote!(#name.map_into()));
|
||||||
|
let into = quote!(#name.map_into());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Type::Token(_) | Type::Group(_) => {
|
||||||
|
let from = None;
|
||||||
|
let into = quote!(default());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Syn(t) if t == "Reserved" => {
|
||||||
|
let from = None;
|
||||||
|
let into = quote!(default());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Ext(t) if t == "Span" => {
|
||||||
|
let from = None;
|
||||||
|
let into = quote!(proc_macro2::Span::call_site());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Syn(_) | Type::Ext(_) => {
|
||||||
|
let from = Some(quote!(#name.ref_into()));
|
||||||
|
let into = quote!(#name.ref_into());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Std(_) => {
|
||||||
|
let from = Some(quote!(#name.into()));
|
||||||
|
let into = quote!(#name.into());
|
||||||
|
(from, into)
|
||||||
|
}
|
||||||
|
Type::Tuple(t) => unreachable!("Type::Tuple: {:?}", t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
|
fn node(traits: &mut TokenStream, node: &Node, _defs: &Definitions) {
|
||||||
|
if IGNORED_TYPES.contains(&&*node.ident) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = format_ident!("{}", &node.ident);
|
||||||
|
|
||||||
|
let mut from_impl = TokenStream::new();
|
||||||
|
let mut into_impl = TokenStream::new();
|
||||||
|
|
||||||
|
match &node.data {
|
||||||
|
Data::Enum(variants) => {
|
||||||
|
let mut from_variants = TokenStream::new();
|
||||||
|
let mut into_variants = TokenStream::new();
|
||||||
|
|
||||||
|
for (variant, fields) in variants {
|
||||||
|
let variant_ident = format_ident!("{}", variant);
|
||||||
|
|
||||||
|
if fields.is_empty() {
|
||||||
|
from_variants.extend(quote! {
|
||||||
|
syn::#ty::#variant_ident => {
|
||||||
|
#ty::#variant_ident
|
||||||
|
}
|
||||||
|
});
|
||||||
|
into_variants.extend(quote! {
|
||||||
|
#ty::#variant_ident => {
|
||||||
|
syn::#ty::#variant_ident
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let mut from_expr = Vec::new();
|
||||||
|
let mut from_pat = Vec::new();
|
||||||
|
let mut into_expr = Vec::new();
|
||||||
|
let mut into_pat = Vec::new();
|
||||||
|
|
||||||
|
for (i, t) in fields.iter().enumerate() {
|
||||||
|
let id = format_ident!("_{}", i);
|
||||||
|
let (from, into) = visit(t, "e!((*#id)));
|
||||||
|
|
||||||
|
from_pat.push(id.clone());
|
||||||
|
into_expr.push(into);
|
||||||
|
if from.is_some() {
|
||||||
|
into_pat.push(id);
|
||||||
|
from_expr.push(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if from_expr.is_empty() {
|
||||||
|
from_variants.extend(quote! {
|
||||||
|
syn::#ty::#variant_ident(..) => {
|
||||||
|
#ty::#variant_ident
|
||||||
|
}
|
||||||
|
});
|
||||||
|
into_variants.extend(quote! {
|
||||||
|
#ty::#variant_ident => {
|
||||||
|
syn::#ty::#variant_ident(#(#into_expr),*)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
from_variants.extend(quote! {
|
||||||
|
syn::#ty::#variant_ident(#(#from_pat),*) => {
|
||||||
|
#ty::#variant_ident(#(#from_expr),*)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
into_variants.extend(quote! {
|
||||||
|
#ty::#variant_ident(#(#into_pat),*) => {
|
||||||
|
syn::#ty::#variant_ident(#(#into_expr),*)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonexhaustive =
|
||||||
|
if node.exhaustive { None } else { Some(quote!(_ => unreachable!())) };
|
||||||
|
|
||||||
|
from_impl.extend(quote! {
|
||||||
|
match node {
|
||||||
|
#from_variants
|
||||||
|
#nonexhaustive
|
||||||
|
}
|
||||||
|
});
|
||||||
|
into_impl.extend(quote! {
|
||||||
|
match node {
|
||||||
|
#into_variants
|
||||||
|
#nonexhaustive
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Data::Struct(fields) => {
|
||||||
|
let mut from_fields = TokenStream::new();
|
||||||
|
let mut into_fields = TokenStream::new();
|
||||||
|
|
||||||
|
for (field, ty) in fields {
|
||||||
|
let id = format_ident!("{}", field);
|
||||||
|
let ref_toks = quote!(node.#id);
|
||||||
|
|
||||||
|
let (from, into) = visit(&ty, &ref_toks);
|
||||||
|
|
||||||
|
if from.is_some() {
|
||||||
|
from_fields.extend(quote! {
|
||||||
|
#id: #from,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
into_fields.extend(quote! {
|
||||||
|
#id: #into,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!fields.is_empty(), "fields.is_empty: {}", ty);
|
||||||
|
if !from_fields.is_empty() {
|
||||||
|
from_impl.extend(quote! {
|
||||||
|
#ty {
|
||||||
|
#from_fields
|
||||||
|
}
|
||||||
|
});
|
||||||
|
into_impl.extend(quote! {
|
||||||
|
syn::#ty {
|
||||||
|
#into_fields
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
assert!(EMPTY_STRUCTS.contains(&&*node.ident), "from_fields.is_empty(): {}", ty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Data::Private => unreachable!("Data::Private: {}", ty),
|
||||||
|
}
|
||||||
|
|
||||||
|
traits.extend(quote! {
|
||||||
|
syn_trait_impl!(syn::#ty);
|
||||||
|
impl From<&syn::#ty> for #ty {
|
||||||
|
fn from(node: &syn::#ty) -> Self {
|
||||||
|
#from_impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&#ty> for syn::#ty {
|
||||||
|
fn from(node: &#ty) -> Self {
|
||||||
|
#into_impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate(defs: &Definitions) -> Result<()> {
|
||||||
|
let traits = gen::traverse(defs, node);
|
||||||
|
file::write(
|
||||||
|
SERDE_SRC,
|
||||||
|
quote! {
|
||||||
|
// Unreachable code is generated sometimes without the full feature.
|
||||||
|
#![allow(unreachable_code, unused_variables, unused_parens)]
|
||||||
|
#![allow(
|
||||||
|
clippy::double_parens,
|
||||||
|
clippy::identity_conversion,
|
||||||
|
clippy::just_underscores_and_digits,
|
||||||
|
)]
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#traits
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
9
syn-serde/examples/README.md
Normal file
9
syn-serde/examples/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
### [`rust2json`](rust2json)
|
||||||
|
|
||||||
|
**Rust -> JSON**.
|
||||||
|
Little utility to parse a Rust source file into a `syn_serde::File` and print out a JSON representation of the syntax tree.
|
||||||
|
|
||||||
|
### [`json2rust`](json2rust)
|
||||||
|
|
||||||
|
**JSON -> Rust**.
|
||||||
|
Little utility to parse a JSON file into a `syn_serde::File` and print out a Rust syntax tree.
|
12
syn-serde/examples/json2rust/Cargo.toml
Normal file
12
syn-serde/examples/json2rust/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "json2rust"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["Taiki Endo <te316e89@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0"
|
||||||
|
syn-serde = { path = "../..", features = ["json"] }
|
||||||
|
syn = { version = "1.0", features = ["full"] }
|
||||||
|
tempfile = "3.1"
|
7
syn-serde/examples/json2rust/README.md
Normal file
7
syn-serde/examples/json2rust/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Parse a JSON file into a `syn_serde::File` and print out a Rust syntax tree.
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo run -- json2rust_main.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The output is the same as which deleted blank line and comment from [src/main.rs](src/main.rs).
|
1147
syn-serde/examples/json2rust/json2rust_main.json
Normal file
1147
syn-serde/examples/json2rust/json2rust_main.json
Normal file
File diff suppressed because it is too large
Load diff
54
syn-serde/examples/json2rust/src/main.rs
Normal file
54
syn-serde/examples/json2rust/src/main.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
|
use quote::ToTokens;
|
||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
io::{self, BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
use syn_serde::json;
|
||||||
|
use tempfile::Builder;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = try_main() {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_main() -> Result<()> {
|
||||||
|
let mut args = env::args_os();
|
||||||
|
let _ = args.next(); // executable name
|
||||||
|
|
||||||
|
let filepath = match (args.next(), args.next()) {
|
||||||
|
(Some(arg1), None) => PathBuf::from(arg1),
|
||||||
|
_ => {
|
||||||
|
println!("Usage: rust2json path/to/filename.rs");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = fs::read_to_string(&filepath)?;
|
||||||
|
let syntax: syn::File = json::from_str(&json)?;
|
||||||
|
|
||||||
|
let outdir = Builder::new().prefix("json2rust").tempdir()?;
|
||||||
|
let outfile_path = outdir.path().join("expanded");
|
||||||
|
fs::write(&outfile_path, syntax.into_token_stream().to_string())?;
|
||||||
|
|
||||||
|
// Run rustfmt
|
||||||
|
// https://github.com/dtolnay/cargo-expand/blob/0.4.9/src/main.rs#L181-L182
|
||||||
|
let rustfmt_config_path = outdir.path().join("rustfmt.toml");
|
||||||
|
fs::write(rustfmt_config_path, "normalize_doc_attributes = true\n")?;
|
||||||
|
|
||||||
|
// Ignore any errors.
|
||||||
|
let _status = Command::new("rustfmt").arg(&outfile_path).stderr(Stdio::null()).status();
|
||||||
|
|
||||||
|
let writer = io::stdout();
|
||||||
|
let mut writer = BufWriter::new(writer.lock());
|
||||||
|
writer.write_all(fs::read_to_string(&outfile_path)?.as_bytes())?;
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
10
syn-serde/examples/rust2json/Cargo.toml
Normal file
10
syn-serde/examples/rust2json/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "rust2json"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["Taiki Endo <te316e89@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn-serde = { path = "../..", features = ["json"] }
|
||||||
|
syn = { version = "1.0", features = ["full"] }
|
7
syn-serde/examples/rust2json/README.md
Normal file
7
syn-serde/examples/rust2json/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Parse a Rust source file into a `syn_serde::File` and print out a JSON representation of the syntax tree.
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo run -- src/main.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
The output is the same as [rust2json_main.json](rust2json_main.json).
|
782
syn-serde/examples/rust2json/rust2json_main.json
Normal file
782
syn-serde/examples/rust2json/rust2json_main.json
Normal file
|
@ -0,0 +1,782 @@
|
||||||
|
{
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "inner",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "warn"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "parenthesis",
|
||||||
|
"stream": [
|
||||||
|
{
|
||||||
|
"ident": "rust_2018_idioms"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"use": {
|
||||||
|
"tree": {
|
||||||
|
"path": {
|
||||||
|
"ident": "std",
|
||||||
|
"tree": {
|
||||||
|
"group": [
|
||||||
|
{
|
||||||
|
"ident": "env"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "fs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": {
|
||||||
|
"ident": "io",
|
||||||
|
"tree": {
|
||||||
|
"group": [
|
||||||
|
{
|
||||||
|
"ident": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "BufWriter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Write"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": {
|
||||||
|
"ident": "path",
|
||||||
|
"tree": {
|
||||||
|
"ident": "PathBuf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"use": {
|
||||||
|
"tree": {
|
||||||
|
"path": {
|
||||||
|
"ident": "syn_serde",
|
||||||
|
"tree": {
|
||||||
|
"ident": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"ident": "Result",
|
||||||
|
"generics": {
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "std"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "result"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Result",
|
||||||
|
"arguments": {
|
||||||
|
"angle_bracketed": {
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Box",
|
||||||
|
"arguments": {
|
||||||
|
"angle_bracketed": {
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"trait_object": {
|
||||||
|
"dyn": true,
|
||||||
|
"bounds": [
|
||||||
|
{
|
||||||
|
"trait": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "std"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fn": {
|
||||||
|
"ident": "main",
|
||||||
|
"inputs": [],
|
||||||
|
"output": null,
|
||||||
|
"stmts": [
|
||||||
|
{
|
||||||
|
"expr": {
|
||||||
|
"if": {
|
||||||
|
"cond": {
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"tuple_struct": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Err"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pat": {
|
||||||
|
"elems": [
|
||||||
|
{
|
||||||
|
"ident": {
|
||||||
|
"ident": "e"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "try_main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then_branch": [
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"macro": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "eprintln"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delimiter": "paren",
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"lit": "\"{}\""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": ",",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "std"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "process"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "exit"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"lit": {
|
||||||
|
"int": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fn": {
|
||||||
|
"ident": "try_main",
|
||||||
|
"inputs": [],
|
||||||
|
"output": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Result",
|
||||||
|
"arguments": {
|
||||||
|
"angle_bracketed": {
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"tuple": {
|
||||||
|
"elems": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stmts": [
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"mut": true,
|
||||||
|
"ident": "args"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "env"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "args_os"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"_": {}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"method_call": {
|
||||||
|
"receiver": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "args"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "next",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"ident": "filepath"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"match": {
|
||||||
|
"expr": {
|
||||||
|
"tuple": {
|
||||||
|
"elems": [
|
||||||
|
{
|
||||||
|
"method_call": {
|
||||||
|
"receiver": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "args"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "next",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method_call": {
|
||||||
|
"receiver": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "args"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "next",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"arms": [
|
||||||
|
{
|
||||||
|
"pat": {
|
||||||
|
"tuple": {
|
||||||
|
"elems": [
|
||||||
|
{
|
||||||
|
"tuple_struct": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Some"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pat": {
|
||||||
|
"elems": [
|
||||||
|
{
|
||||||
|
"ident": {
|
||||||
|
"ident": "arg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": {
|
||||||
|
"ident": "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "PathBuf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "from"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "arg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pat": {
|
||||||
|
"_": {}
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"block": {
|
||||||
|
"stmts": [
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"macro": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "println"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delimiter": "paren",
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"lit": "\"Usage: rust2json path/to/filename.rs\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"return": {
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Ok"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"tuple": {
|
||||||
|
"elems": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"ident": "code"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"try": {
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "fs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "read_to_string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"reference": {
|
||||||
|
"expr": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "filepath"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"ident": "syntax"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"try": {
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "syn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "parse_file"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"reference": {
|
||||||
|
"expr": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"ident": "writer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "io"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "stdout"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"let": {
|
||||||
|
"pat": {
|
||||||
|
"ident": {
|
||||||
|
"mut": true,
|
||||||
|
"ident": "writer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "BufWriter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "new"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"method_call": {
|
||||||
|
"receiver": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "writer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "lock",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"try": {
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "to_writer_pretty"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"reference": {
|
||||||
|
"mut": true,
|
||||||
|
"expr": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "writer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": {
|
||||||
|
"expr": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "syntax"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semi": {
|
||||||
|
"try": {
|
||||||
|
"expr": {
|
||||||
|
"method_call": {
|
||||||
|
"receiver": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "writer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "flush",
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": {
|
||||||
|
"call": {
|
||||||
|
"func": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Ok"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"tuple": {
|
||||||
|
"elems": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
39
syn-serde/examples/rust2json/src/main.rs
Normal file
39
syn-serde/examples/rust2json/src/main.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
io::{self, BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
use syn_serde::json;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = try_main() {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_main() -> Result<()> {
|
||||||
|
let mut args = env::args_os();
|
||||||
|
let _ = args.next(); // executable name
|
||||||
|
|
||||||
|
let filepath = match (args.next(), args.next()) {
|
||||||
|
(Some(arg), None) => PathBuf::from(arg),
|
||||||
|
_ => {
|
||||||
|
println!("Usage: rust2json path/to/filename.rs");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let code = fs::read_to_string(&filepath)?;
|
||||||
|
let syntax = syn::parse_file(&code)?;
|
||||||
|
|
||||||
|
let writer = io::stdout();
|
||||||
|
let mut writer = BufWriter::new(writer.lock());
|
||||||
|
json::to_writer_pretty(&mut writer, &syntax)?;
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
115
syn-serde/src/attr.rs
Normal file
115
syn-serde/src/attr.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An attribute like `#[repr(transparent)]`.
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
///
|
||||||
|
/// Rust has six types of attributes.
|
||||||
|
///
|
||||||
|
/// - Outer attributes like `#[repr(transparent)]`. These appear outside or
|
||||||
|
/// in front of the item they describe.
|
||||||
|
/// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
|
||||||
|
/// of the item they describe, usually a module.
|
||||||
|
/// - Outer doc comments like `/// # Example`.
|
||||||
|
/// - Inner doc comments like `//! Please file an issue`.
|
||||||
|
/// - Outer block comments `/** # Example */`.
|
||||||
|
/// - Inner block comments `/*! Please file an issue */`.
|
||||||
|
///
|
||||||
|
/// The `style` field of type `AttrStyle` distinguishes whether an attribute
|
||||||
|
/// is outer or inner. Doc comments and block comments are promoted to
|
||||||
|
/// attributes, as this is how they are processed by the compiler and by
|
||||||
|
/// `macro_rules!` macros.
|
||||||
|
///
|
||||||
|
/// The `path` field gives the possibly colon-delimited path against which
|
||||||
|
/// the attribute is resolved. It is equal to `"doc"` for desugared doc
|
||||||
|
/// comments. The `tokens` field contains the rest of the attribute body as
|
||||||
|
/// tokens.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// #[derive(Copy)] #[crate::precondition x < 5]
|
||||||
|
/// ^^^^^^~~~~~~ ^^^^^^^^^^^^^^^^^^^ ~~~~~
|
||||||
|
/// path tokens path tokens
|
||||||
|
/// ```
|
||||||
|
pub struct Attribute {
|
||||||
|
pub(crate) style: AttrStyle,
|
||||||
|
pub(crate) path: Path,
|
||||||
|
#[serde(default, skip_serializing_if = "TokenStream::is_empty")]
|
||||||
|
pub(crate) tokens: TokenStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Distinguishes between attributes that decorate an item and attributes
|
||||||
|
/// that are contained within an item.
|
||||||
|
///
|
||||||
|
/// # Outer attributes
|
||||||
|
///
|
||||||
|
/// - `#[repr(transparent)]`
|
||||||
|
/// - `/// # Example`
|
||||||
|
/// - `/** Please file an issue */`
|
||||||
|
///
|
||||||
|
/// # Inner attributes
|
||||||
|
///
|
||||||
|
/// - `#![feature(proc_macro)]`
|
||||||
|
/// - `//! # Example`
|
||||||
|
/// - `/*! Please file an issue */`
|
||||||
|
pub enum AttrStyle {
|
||||||
|
Outer,
|
||||||
|
Inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Content of a compile-time structured attribute.
|
||||||
|
///
|
||||||
|
/// ## Path
|
||||||
|
///
|
||||||
|
/// A meta path is like the `test` in `#[test]`.
|
||||||
|
///
|
||||||
|
/// ## List
|
||||||
|
///
|
||||||
|
/// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
|
||||||
|
///
|
||||||
|
/// ## NameValue
|
||||||
|
///
|
||||||
|
/// A name-value meta is like the `path = "..."` in `#[path =
|
||||||
|
/// "sys/windows.rs"]`.
|
||||||
|
pub enum Meta {
|
||||||
|
Path(Path),
|
||||||
|
|
||||||
|
/// A structured list within an attribute, like `derive(Copy, Clone)`.
|
||||||
|
List(MetaList),
|
||||||
|
|
||||||
|
/// A name-value pair within an attribute, like `feature = "nightly"`.
|
||||||
|
NameValue(MetaNameValue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A structured list within an attribute, like `derive(Copy, Clone)`.
|
||||||
|
pub struct MetaList {
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) nested: Punctuated<NestedMeta>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A name-value pair within an attribute, like `feature = "nightly"`.
|
||||||
|
pub struct MetaNameValue {
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) lit: Lit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Element of a compile-time attribute list.
|
||||||
|
pub enum NestedMeta {
|
||||||
|
/// A structured meta item, like the `Copy` in `#[derive(Copy)]` which
|
||||||
|
/// would be a nested `Meta::Word`.
|
||||||
|
Meta(Meta),
|
||||||
|
|
||||||
|
/// A Rust literal, like the `"new_name"` in `#[rename("new_name")]`.
|
||||||
|
Lit(Lit),
|
||||||
|
}
|
||||||
|
}
|
185
syn-serde/src/data.rs
Normal file
185
syn-serde/src/data.rs
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An enum variant.
|
||||||
|
pub struct Variant {
|
||||||
|
/// Attributes tagged on the variant.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
|
||||||
|
/// Name of the variant.
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
|
||||||
|
/// Content stored in the variant.
|
||||||
|
pub(crate) fields: Fields,
|
||||||
|
|
||||||
|
/// Explicit discriminant: `Variant = 1`
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) discriminant: Option<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Data stored within an enum variant or struct.
|
||||||
|
pub enum Fields {
|
||||||
|
/// Named fields of a struct or struct variant such as `Point { x: f64,
|
||||||
|
/// y: f64 }`.
|
||||||
|
Named(FieldsNamed),
|
||||||
|
|
||||||
|
/// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`.
|
||||||
|
Unnamed(FieldsUnnamed),
|
||||||
|
|
||||||
|
/// Unit struct or unit variant such as `None`.
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Named fields of a struct or struct variant such as `Point { x: f64,
|
||||||
|
/// y: f64 }`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct FieldsNamed {
|
||||||
|
pub(crate) named: Punctuated<Field>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct FieldsUnnamed {
|
||||||
|
pub(crate) unnamed: Punctuated<Field>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fields {
|
||||||
|
pub(crate) fn is_named(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Fields::Named(_) => true,
|
||||||
|
Fields::Unnamed(_) | Fields::Unit => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertions
|
||||||
|
// `syn::perse*` functions will detect these, but there is a possibility to
|
||||||
|
// generate incorrect code by subsequent operations.
|
||||||
|
pub(crate) fn assert_struct_semi(fields: &Fields, semi_token: bool) {
|
||||||
|
match fields {
|
||||||
|
// struct foo {};
|
||||||
|
Fields::Named(_) => assert!(!semi_token, "unexpected token: `;`"),
|
||||||
|
// struct foo ()
|
||||||
|
Fields::Unnamed(_) => assert!(
|
||||||
|
semi_token,
|
||||||
|
"unexpected end of input, expected `where` or `;`"
|
||||||
|
),
|
||||||
|
// struct foo
|
||||||
|
Fields::Unit => assert!(
|
||||||
|
semi_token,
|
||||||
|
"unexpected end of input, expected one of: `where`, parentheses, curly braces, `;`"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A field of a struct or enum variant.
|
||||||
|
pub struct Field {
|
||||||
|
/// Attributes tagged on the field.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
|
||||||
|
/// Visibility of the field.
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
|
||||||
|
/// Name of the field, if any.
|
||||||
|
///
|
||||||
|
/// Fields of tuple structs have no names.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) ident: Option<Ident>,
|
||||||
|
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
|
||||||
|
/// Type of the field.
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// The visibility level of an item: inherited or `pub` or
|
||||||
|
/// `pub(restricted)`.
|
||||||
|
pub enum Visibility {
|
||||||
|
/// A public visibility level: `pub`.
|
||||||
|
#[serde(rename = "pub")]
|
||||||
|
Public,
|
||||||
|
|
||||||
|
/// A crate-level visibility: `crate`.
|
||||||
|
Crate,
|
||||||
|
|
||||||
|
/// A visibility level restricted to some path: `pub(self)` or
|
||||||
|
/// `pub(super)` or `pub(crate)` or `pub(in some::module)`.
|
||||||
|
Restricted(VisRestricted),
|
||||||
|
|
||||||
|
/// An inherited visibility, which usually means private.
|
||||||
|
Inherited,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visibility {
|
||||||
|
pub(crate) fn is_inherited(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Visibility::Inherited => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Visibility {
|
||||||
|
fn default() -> Self {
|
||||||
|
Visibility::Inherited
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A visibility level restricted to some path: `pub(self)` or
|
||||||
|
/// `pub(super)` or `pub(crate)` or `pub(in some::module)`.
|
||||||
|
pub struct VisRestricted {
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) in_token: bool,
|
||||||
|
pub(crate) path: Box<Path>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Visibility
|
||||||
|
syn_trait_impl!(syn::Visibility);
|
||||||
|
impl From<&syn::Visibility> for Visibility {
|
||||||
|
fn from(other: &syn::Visibility) -> Self {
|
||||||
|
use super::Visibility::*;
|
||||||
|
use syn::Visibility;
|
||||||
|
match other {
|
||||||
|
Visibility::Public(_) => Public,
|
||||||
|
Visibility::Crate(_) => Crate,
|
||||||
|
Visibility::Restricted(x) => Restricted(x.ref_into()),
|
||||||
|
Visibility::Inherited => Inherited,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Visibility> for syn::Visibility {
|
||||||
|
fn from(other: &Visibility) -> Self {
|
||||||
|
use syn::Visibility::*;
|
||||||
|
match other {
|
||||||
|
Visibility::Public => Public(syn::VisPublic {
|
||||||
|
pub_token: default(),
|
||||||
|
}),
|
||||||
|
Visibility::Crate => Crate(syn::VisCrate {
|
||||||
|
crate_token: default(),
|
||||||
|
}),
|
||||||
|
Visibility::Restricted(x) => Restricted(x.into()),
|
||||||
|
Visibility::Inherited => Inherited,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
779
syn-serde/src/expr.rs
Normal file
779
syn-serde/src/expr.rs
Normal file
|
@ -0,0 +1,779 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A Rust expression.
|
||||||
|
pub enum Expr {
|
||||||
|
/// A slice literal expression: `[a, b, c, d]`.
|
||||||
|
Array(ExprArray),
|
||||||
|
|
||||||
|
/// An assignment expression: `a = compute()`.
|
||||||
|
Assign(ExprAssign),
|
||||||
|
|
||||||
|
/// A compound assignment expression: `counter += 1`.
|
||||||
|
AssignOp(ExprAssignOp),
|
||||||
|
|
||||||
|
/// An async block: `async { ... }`.
|
||||||
|
Async(ExprAsync),
|
||||||
|
|
||||||
|
/// An await expression: `fut.await`.
|
||||||
|
Await(ExprAwait),
|
||||||
|
|
||||||
|
/// A binary operation: `a + b`, `a * b`.
|
||||||
|
Binary(ExprBinary),
|
||||||
|
|
||||||
|
/// A blocked scope: `{ ... }`.
|
||||||
|
Block(ExprBlock),
|
||||||
|
|
||||||
|
/// A box expression: `box f`.
|
||||||
|
Box(ExprBox),
|
||||||
|
|
||||||
|
/// A `break`, with an optional label to break and an optional
|
||||||
|
/// expression.
|
||||||
|
Break(ExprBreak),
|
||||||
|
|
||||||
|
/// A function call expression: `invoke(a, b)`.
|
||||||
|
Call(ExprCall),
|
||||||
|
|
||||||
|
/// A cast expression: `foo as f64`.
|
||||||
|
Cast(ExprCast),
|
||||||
|
|
||||||
|
/// A closure expression: `|a, b| a + b`.
|
||||||
|
Closure(ExprClosure),
|
||||||
|
|
||||||
|
/// A `continue`, with an optional label.
|
||||||
|
Continue(ExprContinue),
|
||||||
|
|
||||||
|
/// Access of a named struct field (`obj.k`) or unnamed tuple struct
|
||||||
|
/// field (`obj.0`).
|
||||||
|
Field(ExprField),
|
||||||
|
|
||||||
|
/// A for loop: `for pat in expr { ... }`.
|
||||||
|
ForLoop(ExprForLoop),
|
||||||
|
|
||||||
|
/// An expression contained within invisible delimiters.
|
||||||
|
///
|
||||||
|
/// This variant is important for faithfully representing the precedence
|
||||||
|
/// of expressions and is related to `None`-delimited spans in a
|
||||||
|
/// `TokenStream`.
|
||||||
|
Group(ExprGroup),
|
||||||
|
|
||||||
|
/// An `if` expression with an optional `else` block: `if expr { ... }
|
||||||
|
/// else { ... }`.
|
||||||
|
///
|
||||||
|
/// The `else` branch expression may only be an `If` or `Block`
|
||||||
|
/// expression, not any of the other types of expression.
|
||||||
|
If(ExprIf),
|
||||||
|
|
||||||
|
/// A square bracketed indexing expression: `vector[2]`.
|
||||||
|
Index(ExprIndex),
|
||||||
|
|
||||||
|
/// A `let` guard: `let Some(x) = opt`.
|
||||||
|
Let(ExprLet),
|
||||||
|
|
||||||
|
/// A literal in place of an expression: `1`, `"foo"`.
|
||||||
|
Lit(ExprLit),
|
||||||
|
|
||||||
|
/// Conditionless loop: `loop { ... }`.
|
||||||
|
Loop(ExprLoop),
|
||||||
|
|
||||||
|
/// A macro invocation expression: `format!("{}", q)`.
|
||||||
|
Macro(ExprMacro),
|
||||||
|
|
||||||
|
/// A `match` expression: `match n { Some(n) => {}, None => {} }`.
|
||||||
|
Match(ExprMatch),
|
||||||
|
|
||||||
|
/// A method call expression: `x.foo::<T>(a, b)`.
|
||||||
|
MethodCall(ExprMethodCall),
|
||||||
|
|
||||||
|
/// A parenthesized expression: `(a + b)`.
|
||||||
|
Paren(ExprParen),
|
||||||
|
|
||||||
|
/// A path like `std::mem::replace` possibly containing generic
|
||||||
|
/// parameters and a qualified self-type.
|
||||||
|
///
|
||||||
|
/// A plain identifier like `x` is a path of length 1.
|
||||||
|
Path(ExprPath),
|
||||||
|
|
||||||
|
/// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
|
||||||
|
Range(ExprRange),
|
||||||
|
|
||||||
|
/// A referencing operation: `&a` or `&mut a`.
|
||||||
|
Reference(ExprReference),
|
||||||
|
|
||||||
|
/// An array literal constructed from one repeated element: `[0u8; N]`.
|
||||||
|
Repeat(ExprRepeat),
|
||||||
|
|
||||||
|
/// A `return`, with an optional value to be returned.
|
||||||
|
Return(ExprReturn),
|
||||||
|
|
||||||
|
/// A struct literal expression: `Point { x: 1, y: 1 }`.
|
||||||
|
///
|
||||||
|
/// The `rest` provides the value of the remaining fields as in `S { a:
|
||||||
|
/// 1, b: 1, ..rest }`.
|
||||||
|
Struct(ExprStruct),
|
||||||
|
|
||||||
|
/// A try-expression: `expr?`.
|
||||||
|
Try(ExprTry),
|
||||||
|
|
||||||
|
/// A try block: `try { ... }`.
|
||||||
|
TryBlock(ExprTryBlock),
|
||||||
|
|
||||||
|
/// A tuple expression: `(a, b, c, d)`.
|
||||||
|
Tuple(ExprTuple),
|
||||||
|
|
||||||
|
/// A type ascription expression: `foo: f64`.
|
||||||
|
Type(ExprType),
|
||||||
|
|
||||||
|
/// A unary operation: `!x`, `*x`.
|
||||||
|
Unary(ExprUnary),
|
||||||
|
|
||||||
|
/// An unsafe block: `unsafe { ... }`.
|
||||||
|
Unsafe(ExprUnsafe),
|
||||||
|
|
||||||
|
/// Tokens in expression position not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
/// A while loop: `while expr { ... }`.
|
||||||
|
While(ExprWhile),
|
||||||
|
|
||||||
|
/// A yield expression: `yield expr`.
|
||||||
|
Yield(ExprYield),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A slice literal expression: `[a, b, c, d]`.
|
||||||
|
pub struct ExprArray {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) elems: Punctuated<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An assignment expression: `a = compute()`.
|
||||||
|
pub struct ExprAssign {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) left: Box<Expr>,
|
||||||
|
pub(crate) right: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A compound assignment expression: `counter += 1`.
|
||||||
|
pub struct ExprAssignOp {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) left: Box<Expr>,
|
||||||
|
pub(crate) op: BinOp,
|
||||||
|
pub(crate) right: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An async block: `async { ... }`.
|
||||||
|
pub struct ExprAsync {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "move")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) capture: bool,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An await expression: `fut.await`.
|
||||||
|
pub struct ExprAwait {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) base: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A binary operation: `a + b`, `a * b`.
|
||||||
|
pub struct ExprBinary {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) left: Box<Expr>,
|
||||||
|
pub(crate) op: BinOp,
|
||||||
|
pub(crate) right: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A blocked scope: `{ ... }`.
|
||||||
|
pub struct ExprBlock {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Label>,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A box expression: `box f`.
|
||||||
|
pub struct ExprBox {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `break`, with an optional label to break and an optional
|
||||||
|
/// expression.
|
||||||
|
pub struct ExprBreak {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Lifetime>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) expr: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A function call expression: `invoke(a, b)`.
|
||||||
|
pub struct ExprCall {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) func: Box<Expr>,
|
||||||
|
pub(crate) args: Punctuated<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A cast expression: `foo as f64`.
|
||||||
|
pub struct ExprCast {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A closure expression: `|a, b| a + b`.
|
||||||
|
pub struct ExprClosure {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "async")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) asyncness: bool,
|
||||||
|
#[serde(rename = "static")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) movability: bool,
|
||||||
|
#[serde(rename = "move")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) capture: bool,
|
||||||
|
pub(crate) inputs: Punctuated<Pat>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) output: ReturnType,
|
||||||
|
pub(crate) body: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `continue`, with an optional label.
|
||||||
|
pub struct ExprContinue {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Lifetime>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Access of a named struct field (`obj.k`) or unnamed tuple struct
|
||||||
|
/// field (`obj.0`).
|
||||||
|
pub struct ExprField {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) base: Box<Expr>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) member: Member,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A for loop: `for pat in expr { ... }`.
|
||||||
|
pub struct ExprForLoop {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Label>,
|
||||||
|
pub(crate) pat: Pat,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) body: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An expression contained within invisible delimiters.
|
||||||
|
///
|
||||||
|
/// This variant is important for faithfully representing the precedence
|
||||||
|
/// of expressions and is related to `None`-delimited spans in a
|
||||||
|
/// `TokenStream`.
|
||||||
|
pub struct ExprGroup {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An `if` expression with an optional `else` block: `if expr { ... }
|
||||||
|
/// else { ... }`.
|
||||||
|
///
|
||||||
|
/// The `else` branch expression may only be an `If` or `Block`
|
||||||
|
/// expression, not any of the other types of expression.
|
||||||
|
pub struct ExprIf {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) cond: Box<Expr>,
|
||||||
|
pub(crate) then_branch: Block,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) else_branch: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A square bracketed indexing expression: `vector[2]`.
|
||||||
|
pub struct ExprIndex {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) index: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `let` guard: `let Some(x) = opt`.
|
||||||
|
pub struct ExprLet {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) pat: Pat,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A literal in place of an expression: `1`, `"foo"`.
|
||||||
|
pub struct ExprLit {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) lit: Lit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Conditionless loop: `loop { ... }`.
|
||||||
|
pub struct ExprLoop {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Label>,
|
||||||
|
pub(crate) body: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation expression: `format!("{}", q)`.
|
||||||
|
pub struct ExprMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `match` expression: `match n { Some(n) => {}, None => {} }`.
|
||||||
|
pub struct ExprMatch {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) arms: Vec<Arm>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A method call expression: `x.foo::<T>(a, b)`.
|
||||||
|
pub struct ExprMethodCall {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) receiver: Box<Expr>,
|
||||||
|
pub(crate) method: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) turbofish: Option<MethodTurbofish>,
|
||||||
|
pub(crate) args: Punctuated<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A parenthesized expression: `(a + b)`.
|
||||||
|
pub struct ExprParen {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A path like `std::mem::replace` possibly containing generic
|
||||||
|
/// parameters and a qualified self-type.
|
||||||
|
///
|
||||||
|
/// A plain identifier like `x` is a path of length 1.
|
||||||
|
pub struct ExprPath {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) qself: Option<QSelf>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) path: Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A range expression: `1..2`, `1..`, `..2`, `1..=2`, `..=2`.
|
||||||
|
pub struct ExprRange {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) from: Option<Box<Expr>>,
|
||||||
|
pub(crate) limits: RangeLimits,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) to: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A referencing operation: `&a` or `&mut a`.
|
||||||
|
pub struct ExprReference {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
// #[serde(default, skip_serializing_if = "Reserved::is_default")]
|
||||||
|
// pub(crate) raw: Reserved,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An array literal constructed from one repeated element: `[0u8; N]`.
|
||||||
|
pub struct ExprRepeat {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) len: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `return`, with an optional value to be returned.
|
||||||
|
pub struct ExprReturn {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) expr: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A struct literal expression: `Point { x: 1, y: 1 }`.
|
||||||
|
///
|
||||||
|
/// The `rest` provides the value of the remaining fields as in `S { a:
|
||||||
|
/// 1, b: 1, ..rest }`.
|
||||||
|
pub struct ExprStruct {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) fields: Punctuated<FieldValue>,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) dot2_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) rest: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A try-expression: `expr?`.
|
||||||
|
pub struct ExprTry {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A try block: `try { ... }`.
|
||||||
|
pub struct ExprTryBlock {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A tuple expression: `(a, b, c, d)`.
|
||||||
|
pub struct ExprTuple {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) elems: Punctuated<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A type ascription expression: `foo: f64`.
|
||||||
|
pub struct ExprType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A unary operation: `!x`, `*x`.
|
||||||
|
pub struct ExprUnary {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) op: UnOp,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An unsafe block: `unsafe { ... }`.
|
||||||
|
pub struct ExprUnsafe {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A while loop: `while expr { ... }`.
|
||||||
|
pub struct ExprWhile {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) label: Option<Label>,
|
||||||
|
pub(crate) cond: Box<Expr>,
|
||||||
|
pub(crate) body: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A yield expression: `yield expr`.
|
||||||
|
pub struct ExprYield {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) expr: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A struct or tuple struct field accessed in a struct literal or field
|
||||||
|
/// expression.
|
||||||
|
pub enum Member {
|
||||||
|
/// A named field like `self.x`.
|
||||||
|
#[serde(rename = "ident")]
|
||||||
|
Named(Ident),
|
||||||
|
/// An unnamed field like `self.0`.
|
||||||
|
#[serde(rename = "index")]
|
||||||
|
Unnamed(Index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The index of an unnamed tuple struct field.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Index {
|
||||||
|
pub(crate) index: u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The `::<>` explicit type parameters passed to a method call:
|
||||||
|
/// `parse::<u64>()`.
|
||||||
|
pub struct MethodTurbofish {
|
||||||
|
pub(crate) args: Punctuated<GenericMethodArgument>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An individual generic argument to a method, like `T`.
|
||||||
|
pub enum GenericMethodArgument {
|
||||||
|
/// A type argument.
|
||||||
|
Type(Type),
|
||||||
|
/// A const expression. Must be inside of a block.
|
||||||
|
///
|
||||||
|
/// NOTE: Identity expressions are represented as Type arguments, as
|
||||||
|
/// they are indistinguishable syntactically.
|
||||||
|
Const(Expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A field-value pair in a struct literal.
|
||||||
|
pub struct FieldValue {
|
||||||
|
/// Attributes tagged on the field.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
|
||||||
|
/// Name or index of the field.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) member: Member,
|
||||||
|
|
||||||
|
/// The colon in `Struct { x: x }`. If written in shorthand like
|
||||||
|
/// `Struct { x }`, there is no colon.
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
|
||||||
|
/// Value of the field.
|
||||||
|
pub(crate) expr: Expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A lifetime labeling a `for`, `while`, or `loop`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Label {
|
||||||
|
pub(crate) name: Lifetime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// One arm of a `match` expression: `0...10 => { return true; }`.
|
||||||
|
pub struct Arm {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) pat: Pat,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) guard: Option<Box<Expr>>,
|
||||||
|
pub(crate) body: Box<Expr>,
|
||||||
|
// #[serde(default, skip_serializing_if = "not")]
|
||||||
|
// pub(crate) comma: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Limit types of a range, inclusive or exclusive.
|
||||||
|
pub enum RangeLimits {
|
||||||
|
/// Inclusive at the beginning, exclusive at the end.
|
||||||
|
#[serde(rename = "..")]
|
||||||
|
HalfOpen,
|
||||||
|
/// Inclusive at the beginning and end.
|
||||||
|
#[serde(rename = "..=")]
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn requires_terminator(expr: &Expr) -> bool {
|
||||||
|
// see https://github.com/rust-lang/rust/blob/eb8f2586e/src/libsyntax/parse/classify.rs#L17-L37
|
||||||
|
match expr {
|
||||||
|
Expr::Unsafe(..)
|
||||||
|
| Expr::Block(..)
|
||||||
|
| Expr::If(..)
|
||||||
|
| Expr::Match(..)
|
||||||
|
| Expr::While(..)
|
||||||
|
| Expr::Loop(..)
|
||||||
|
| Expr::ForLoop(..)
|
||||||
|
| Expr::Async(..)
|
||||||
|
| Expr::TryBlock(..) => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// ExprMatch
|
||||||
|
syn_trait_impl!(syn::ExprMatch);
|
||||||
|
fn from_syn_arms(other: &[syn::Arm]) -> Vec<Arm> {
|
||||||
|
let last = other.len().saturating_sub(1);
|
||||||
|
other
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, other)| {
|
||||||
|
let body = other.body.map_into();
|
||||||
|
if i < last && requires_terminator(&*body) {
|
||||||
|
assert!(other.comma.is_some(), "expected `,`");
|
||||||
|
}
|
||||||
|
|
||||||
|
Arm {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
pat: other.pat.ref_into(),
|
||||||
|
guard: other.guard.ref_map(|(_, x)| x.map_into()),
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
impl From<&syn::ExprMatch> for ExprMatch {
|
||||||
|
fn from(other: &syn::ExprMatch) -> Self {
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
expr: other.expr.map_into(),
|
||||||
|
arms: from_syn_arms(&other.arms),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&ExprMatch> for syn::ExprMatch {
|
||||||
|
fn from(other: &ExprMatch) -> Self {
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
match_token: default(),
|
||||||
|
expr: other.expr.map_into(),
|
||||||
|
brace_token: default(),
|
||||||
|
arms: other.arms.map_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arm
|
||||||
|
syn_trait_impl!(syn::Arm);
|
||||||
|
impl From<&syn::Arm> for Arm {
|
||||||
|
fn from(other: &syn::Arm) -> Self {
|
||||||
|
let body = other.body.map_into();
|
||||||
|
if requires_terminator(&*body) {
|
||||||
|
assert!(other.comma.is_some(), "expected `,`");
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
pat: other.pat.ref_into(),
|
||||||
|
guard: other.guard.ref_map(|(_, x)| x.map_into()),
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Arm> for syn::Arm {
|
||||||
|
fn from(other: &Arm) -> Self {
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
pat: other.pat.ref_into(),
|
||||||
|
guard: other.guard.ref_map(|x| (default(), x.map_into())),
|
||||||
|
fat_arrow_token: default(),
|
||||||
|
body: other.body.map_into(),
|
||||||
|
comma: default_or_none(requires_terminator(&*other.body)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
syn-serde/src/file.rs
Normal file
12
syn-serde/src/file.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A complete file of Rust source code.
|
||||||
|
pub struct File {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) shebang: Option<String>,
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) items: Vec<Item>,
|
||||||
|
}
|
||||||
|
}
|
3398
syn-serde/src/gen/mod.rs
Normal file
3398
syn-serde/src/gen/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
223
syn-serde/src/generics.rs
Normal file
223
syn-serde/src/generics.rs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Lifetimes and type parameters attached to a declaration of a function,
|
||||||
|
/// enum, trait, etc.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Generics {
|
||||||
|
// #[serde(default, skip_serializing_if = "not")]
|
||||||
|
// pub(crate) lt_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Punctuated::is_empty")]
|
||||||
|
pub(crate) params: Punctuated<GenericParam>,
|
||||||
|
// #[serde(default, skip_serializing_if = "not")]
|
||||||
|
// pub(crate) gt_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) where_clause: Option<WhereClause>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Generics {
|
||||||
|
pub(crate) fn is_none(&self) -> bool {
|
||||||
|
self.params.is_empty() && self.where_clause.is_none() // && !self.lt_token && !self.gt_token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A generic type parameter, lifetime, or const generic: `T: Into<String>`,
|
||||||
|
/// `'a: 'b`, `const LEN: usize`.
|
||||||
|
pub enum GenericParam {
|
||||||
|
/// A generic type parameter: `T: Into<String>`.
|
||||||
|
Type(TypeParam),
|
||||||
|
|
||||||
|
/// A lifetime definition: `'a: 'b + 'c + 'd`.
|
||||||
|
Lifetime(LifetimeDef),
|
||||||
|
|
||||||
|
/// A const generic parameter: `const LENGTH: usize`.
|
||||||
|
Const(ConstParam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A generic type parameter: `T: Into<String>`.
|
||||||
|
pub struct TypeParam {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Punctuated::is_empty")]
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) eq_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) default: Option<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A lifetime definition: `'a: 'b + 'c + 'd`.
|
||||||
|
pub struct LifetimeDef {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) lifetime: Lifetime,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Punctuated::is_empty")]
|
||||||
|
pub(crate) bounds: Punctuated<Lifetime>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A const generic parameter: `const LENGTH: usize`.
|
||||||
|
pub struct ConstParam {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) eq_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) default: Option<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A set of bound lifetimes: `for<'a, 'b, 'c>`.
|
||||||
|
#[derive(Default)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct BoundLifetimes {
|
||||||
|
pub(crate) lifetimes: Punctuated<LifetimeDef>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A trait or lifetime used as a bound on a type parameter.
|
||||||
|
pub enum TypeParamBound {
|
||||||
|
Trait(TraitBound),
|
||||||
|
Lifetime(Lifetime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A trait used as a bound on a type parameter.
|
||||||
|
pub struct TraitBound {
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) paren_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "TraitBoundModifier::is_none")]
|
||||||
|
pub(crate) modifier: TraitBoundModifier,
|
||||||
|
/// The `for<'a>` in `for<'a> Foo<&'a T>`
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) lifetimes: Option<BoundLifetimes>,
|
||||||
|
/// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`
|
||||||
|
pub(crate) path: Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A modifier on a trait bound, currently only used for the `?` in
|
||||||
|
/// `?Sized`.
|
||||||
|
pub enum TraitBoundModifier {
|
||||||
|
None,
|
||||||
|
Maybe,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitBoundModifier {
|
||||||
|
fn is_none(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TraitBoundModifier::None => true,
|
||||||
|
TraitBoundModifier::Maybe => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TraitBoundModifier {
|
||||||
|
fn default() -> Self {
|
||||||
|
TraitBoundModifier::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A `where` clause in a definition: `where T: Deserialize<'de>, D:
|
||||||
|
/// 'static`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct WhereClause {
|
||||||
|
pub(crate) predicates: Punctuated<WherePredicate>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A single predicate in a `where` clause: `T: Deserialize<'de>`.
|
||||||
|
pub enum WherePredicate {
|
||||||
|
/// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`.
|
||||||
|
Type(PredicateType),
|
||||||
|
|
||||||
|
/// A lifetime predicate in a `where` clause: `'a: 'b + 'c`.
|
||||||
|
Lifetime(PredicateLifetime),
|
||||||
|
|
||||||
|
/// An equality predicate in a `where` clause (unsupported).
|
||||||
|
Eq(PredicateEq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A type predicate in a `where` clause: `for<'c> Foo<'c>: Trait<'c>`.
|
||||||
|
pub struct PredicateType {
|
||||||
|
/// Any lifetimes from a `for` binding
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) lifetimes: Option<BoundLifetimes>,
|
||||||
|
/// The type being bounded
|
||||||
|
pub(crate) bounded_ty: Type,
|
||||||
|
/// Trait and lifetime bounds (`Clone+Send+'static`)
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A lifetime predicate in a `where` clause: `'a: 'b + 'c`.
|
||||||
|
pub struct PredicateLifetime {
|
||||||
|
pub(crate) lifetime: Lifetime,
|
||||||
|
pub(crate) bounds: Punctuated<Lifetime>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An equality predicate in a `where` clause (unsupported).
|
||||||
|
pub struct PredicateEq {
|
||||||
|
pub(crate) lhs_ty: Type,
|
||||||
|
pub(crate) rhs_ty: Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Generics
|
||||||
|
syn_trait_impl!(syn::Generics);
|
||||||
|
impl From<&syn::Generics> for Generics {
|
||||||
|
fn from(other: &syn::Generics) -> Self {
|
||||||
|
// `ident ..>` or `ident <..`
|
||||||
|
assert_eq!(other.lt_token.is_some(), other.gt_token.is_some());
|
||||||
|
// `ident T`
|
||||||
|
assert!(
|
||||||
|
other.params.is_empty() || other.lt_token.is_some(),
|
||||||
|
"expected `<`"
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
params: other.params.map_into(),
|
||||||
|
where_clause: other.where_clause.map_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Generics> for syn::Generics {
|
||||||
|
fn from(other: &Generics) -> Self {
|
||||||
|
Self {
|
||||||
|
lt_token: default_or_none(!other.params.is_empty()),
|
||||||
|
params: other.params.map_into(),
|
||||||
|
gt_token: default_or_none(!other.params.is_empty()),
|
||||||
|
where_clause: other.where_clause.map_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
770
syn-serde/src/item.rs
Normal file
770
syn-serde/src/item.rs
Normal file
|
@ -0,0 +1,770 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Things that can appear directly inside of a module or scope.
|
||||||
|
pub enum Item {
|
||||||
|
/// A constant item: `const MAX: u16 = 65535`.
|
||||||
|
Const(ItemConst),
|
||||||
|
|
||||||
|
/// An enum definition: `enum Foo<A, B> { A(A), B(B) }`.
|
||||||
|
Enum(ItemEnum),
|
||||||
|
|
||||||
|
/// An `extern crate` item: `extern crate serde`.
|
||||||
|
ExternCrate(ItemExternCrate),
|
||||||
|
|
||||||
|
/// A free-standing function: `fn process(n: usize) -> Result<()> { ...
|
||||||
|
/// }`.
|
||||||
|
Fn(ItemFn),
|
||||||
|
|
||||||
|
/// A block of foreign items: `extern "C" { ... }`.
|
||||||
|
ForeignMod(ItemForeignMod),
|
||||||
|
|
||||||
|
/// An impl block providing trait or associated items: `impl<A> Trait
|
||||||
|
/// for Data<A> { ... }`.
|
||||||
|
Impl(ItemImpl),
|
||||||
|
|
||||||
|
/// A macro invocation, which includes `macro_rules!` definitions.
|
||||||
|
Macro(ItemMacro),
|
||||||
|
|
||||||
|
/// A 2.0-style declarative macro introduced by the `macro` keyword.
|
||||||
|
Macro2(ItemMacro2),
|
||||||
|
|
||||||
|
/// A module or module declaration: `mod m` or `mod m { ... }`.
|
||||||
|
Mod(ItemMod),
|
||||||
|
|
||||||
|
/// A static item: `static BIKE: Shed = Shed(42)`.
|
||||||
|
Static(ItemStatic),
|
||||||
|
|
||||||
|
/// A struct definition: `struct Foo<A> { x: A }`.
|
||||||
|
Struct(ItemStruct),
|
||||||
|
|
||||||
|
/// A trait definition: `pub trait Iterator { ... }`.
|
||||||
|
Trait(ItemTrait),
|
||||||
|
|
||||||
|
/// A trait alias: `pub trait SharableIterator = Iterator + Sync`.
|
||||||
|
TraitAlias(ItemTraitAlias),
|
||||||
|
|
||||||
|
/// A type alias: `type Result<T> = std::result::Result<T, MyError>`.
|
||||||
|
Type(ItemType),
|
||||||
|
|
||||||
|
/// A union definition: `union Foo<A, B> { x: A, y: B }`.
|
||||||
|
Union(ItemUnion),
|
||||||
|
|
||||||
|
/// A use declaration: `use std::collections::HashMap`.
|
||||||
|
Use(ItemUse),
|
||||||
|
|
||||||
|
/// Tokens forming an item not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A constant item: `const MAX: u16 = 65535`.
|
||||||
|
pub struct ItemConst {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An enum definition: `enum Foo<A, B> { A(A), B(B) }`.
|
||||||
|
pub struct ItemEnum {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) variants: Punctuated<Variant>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An `extern crate` item: `extern crate serde`.
|
||||||
|
pub struct ItemExternCrate {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) rename: Option<Ident>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A free-standing function: `fn process(n: usize) -> Result<()> { ...
|
||||||
|
/// }`.
|
||||||
|
pub struct ItemFn {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) sig: Signature,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Box<Block>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A block of foreign items: `extern "C" { ... }`.
|
||||||
|
pub struct ItemForeignMod {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) abi: Abi,
|
||||||
|
pub(crate) items: Vec<ForeignItem>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An impl block providing trait or associated items: `impl<A> Trait
|
||||||
|
/// for Data<A> { ... }`.
|
||||||
|
pub struct ItemImpl {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "default")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) defaultness: bool,
|
||||||
|
#[serde(rename = "unsafe")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) unsafety: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
/// Trait this impl implements.
|
||||||
|
#[serde(rename = "trait")]
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) trait_: Option<(bool, Path)>,
|
||||||
|
/// The Self type of the impl.
|
||||||
|
pub(crate) self_ty: Box<Type>,
|
||||||
|
pub(crate) items: Vec<ImplItem>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation, which includes `macro_rules!` definitions.
|
||||||
|
pub struct ItemMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
/// The `example` in `macro_rules! example { ... }`.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) ident: Option<Ident>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A 2.0-style declarative macro introduced by the `macro` keyword.
|
||||||
|
pub struct ItemMacro2 {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) rules: TokenStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A module or module declaration: `mod m` or `mod m { ... }`.
|
||||||
|
pub struct ItemMod {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) content: Option<Vec<Item>>,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) semi: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A static item: `static BIKE: Shed = Shed(42)`.
|
||||||
|
pub struct ItemStatic {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A struct definition: `struct Foo<A> { x: A }`.
|
||||||
|
pub struct ItemStruct {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) fields: Fields,
|
||||||
|
// #[serde(default, skip_serializing_if = "not")]
|
||||||
|
// pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A trait definition: `pub trait Iterator { ... }`.
|
||||||
|
pub struct ItemTrait {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "unsafe")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) unsafety: bool,
|
||||||
|
#[serde(rename = "auto")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) auto_token: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Punctuated::is_empty")]
|
||||||
|
pub(crate) supertraits: Punctuated<TypeParamBound>,
|
||||||
|
pub(crate) items: Vec<TraitItem>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A trait alias: `pub trait SharableIterator = Iterator + Sync`.
|
||||||
|
pub struct ItemTraitAlias {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A type alias: `type Result<T> = std::result::Result<T, MyError>`.
|
||||||
|
pub struct ItemType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A union definition: `union Foo<A, B> { x: A, y: B }`.
|
||||||
|
pub struct ItemUnion {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) fields: FieldsNamed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A use declaration: `use std::collections::HashMap`.
|
||||||
|
pub struct ItemUse {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) leading_colon: bool,
|
||||||
|
pub(crate) tree: UseTree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A suffix of an import tree in a `use` item: `Type as Renamed` or `*`.
|
||||||
|
pub enum UseTree {
|
||||||
|
/// A path prefix of imports in a `use` item: `std::...`.
|
||||||
|
Path(UsePath),
|
||||||
|
|
||||||
|
/// An identifier imported by a `use` item: `HashMap`.
|
||||||
|
#[serde(rename = "ident")]
|
||||||
|
Name(UseName),
|
||||||
|
|
||||||
|
/// An renamed identifier imported by a `use` item: `HashMap as Map`.
|
||||||
|
Rename(UseRename),
|
||||||
|
|
||||||
|
/// A glob import in a `use` item: `*`.
|
||||||
|
#[serde(rename = "*")]
|
||||||
|
Glob,
|
||||||
|
|
||||||
|
/// A braced group of imports in a `use` item: `{A, B, C}`.
|
||||||
|
Group(UseGroup),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A path prefix of imports in a `use` item: `std::...`.
|
||||||
|
pub struct UsePath {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) tree: Box<UseTree>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An identifier imported by a `use` item: `HashMap`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct UseName {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An renamed identifier imported by a `use` item: `HashMap as Map`.
|
||||||
|
pub struct UseRename {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) rename: Ident,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A braced group of imports in a `use` item: `{A, B, C}`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct UseGroup {
|
||||||
|
pub(crate) items: Punctuated<UseTree>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An item within an `extern` block.
|
||||||
|
pub enum ForeignItem {
|
||||||
|
/// A foreign function in an `extern` block.
|
||||||
|
Fn(ForeignItemFn),
|
||||||
|
|
||||||
|
/// A foreign static item in an `extern` block: `static ext: u8`.
|
||||||
|
Static(ForeignItemStatic),
|
||||||
|
|
||||||
|
/// A foreign type in an `extern` block: `type void`.
|
||||||
|
Type(ForeignItemType),
|
||||||
|
|
||||||
|
/// A macro invocation within an extern block.
|
||||||
|
Macro(ForeignItemMacro),
|
||||||
|
|
||||||
|
/// Tokens in an `extern` block not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A foreign function in an `extern` block.
|
||||||
|
pub struct ForeignItemFn {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) sig: Signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A foreign static item in an `extern` block: `static ext: u8`.
|
||||||
|
pub struct ForeignItemStatic {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A foreign type in an `extern` block: `type void`.
|
||||||
|
pub struct ForeignItemType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation within an extern block.
|
||||||
|
pub struct ForeignItemMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An item declaration within the definition of a trait.
|
||||||
|
pub enum TraitItem {
|
||||||
|
/// An associated constant within the definition of a trait.
|
||||||
|
Const(TraitItemConst),
|
||||||
|
|
||||||
|
/// A trait method within the definition of a trait.
|
||||||
|
Method(TraitItemMethod),
|
||||||
|
|
||||||
|
/// An associated type within the definition of a trait.
|
||||||
|
Type(TraitItemType),
|
||||||
|
|
||||||
|
/// A macro invocation within the definition of a trait.
|
||||||
|
Macro(TraitItemMacro),
|
||||||
|
|
||||||
|
/// Tokens within the definition of a trait not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An associated constant within the definition of a trait.
|
||||||
|
pub struct TraitItemConst {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) default: Option<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A trait method within the definition of a trait.
|
||||||
|
pub struct TraitItemMethod {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) sig: Signature,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) default: Option<Block>,
|
||||||
|
// #[serde(default, skip_serializing_if = "not")]
|
||||||
|
// pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An associated type within the definition of a trait.
|
||||||
|
pub struct TraitItemType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Punctuated::is_empty")]
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) default: Option<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation within the definition of a trait.
|
||||||
|
pub struct TraitItemMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An item within an impl block.
|
||||||
|
pub enum ImplItem {
|
||||||
|
/// An associated constant within an impl block.
|
||||||
|
Const(ImplItemConst),
|
||||||
|
|
||||||
|
/// A method within an impl block.
|
||||||
|
Method(ImplItemMethod),
|
||||||
|
|
||||||
|
/// An associated type within an impl block.
|
||||||
|
Type(ImplItemType),
|
||||||
|
|
||||||
|
/// A macro invocation within an impl block.
|
||||||
|
Macro(ImplItemMacro),
|
||||||
|
|
||||||
|
/// Tokens within an impl block not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An associated constant within an impl block.
|
||||||
|
pub struct ImplItemConst {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "default")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) defaultness: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
pub(crate) expr: Expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A method within an impl block.
|
||||||
|
pub struct ImplItemMethod {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "default")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) defaultness: bool,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) sig: Signature,
|
||||||
|
#[serde(rename = "stmts")]
|
||||||
|
pub(crate) block: Block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An associated type within an impl block.
|
||||||
|
pub struct ImplItemType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Visibility::is_inherited")]
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
#[serde(rename = "default")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) defaultness: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation within an impl block.
|
||||||
|
pub struct ImplItemMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) semi_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A function signature in a trait or implementation: `unsafe fn
|
||||||
|
/// initialize(&self)`.
|
||||||
|
pub struct Signature {
|
||||||
|
#[serde(rename = "const")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) constness: bool,
|
||||||
|
#[serde(rename = "async")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) asyncness: bool,
|
||||||
|
#[serde(rename = "unsafe")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) unsafety: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) abi: Option<Abi>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Generics::is_none")]
|
||||||
|
pub(crate) generics: Generics,
|
||||||
|
pub(crate) inputs: Punctuated<FnArg>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) variadic: Option<Variadic>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) output: ReturnType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
|
||||||
|
pub enum FnArg {
|
||||||
|
/// The `self` argument of an associated method, whether taken by value
|
||||||
|
/// or by reference.
|
||||||
|
Receiver(Receiver),
|
||||||
|
|
||||||
|
/// A function argument accepted by pattern and type.
|
||||||
|
Typed(PatType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The `self` argument of an associated method, whether taken by value
|
||||||
|
/// or by reference.
|
||||||
|
pub struct Receiver {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "ref")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) reference: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) lifetime: Option<Lifetime>,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// ItemStruct
|
||||||
|
syn_trait_impl!(syn::ItemStruct);
|
||||||
|
impl From<&syn::ItemStruct> for ItemStruct {
|
||||||
|
fn from(other: &syn::ItemStruct) -> Self {
|
||||||
|
let fields: Fields = other.fields.ref_into();
|
||||||
|
assert_struct_semi(&fields, other.semi_token.is_some());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
vis: other.vis.ref_into(),
|
||||||
|
ident: other.ident.ref_into(),
|
||||||
|
generics: other.generics.ref_into(),
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&ItemStruct> for syn::ItemStruct {
|
||||||
|
fn from(other: &ItemStruct) -> Self {
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
vis: other.vis.ref_into(),
|
||||||
|
struct_token: default(),
|
||||||
|
ident: other.ident.ref_into(),
|
||||||
|
generics: other.generics.ref_into(),
|
||||||
|
fields: other.fields.ref_into(),
|
||||||
|
semi_token: default_or_none(!other.fields.is_named()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraitItemMethod
|
||||||
|
syn_trait_impl!(syn::TraitItemMethod);
|
||||||
|
impl From<&syn::TraitItemMethod> for TraitItemMethod {
|
||||||
|
fn from(other: &syn::TraitItemMethod) -> Self {
|
||||||
|
if other.default.is_some() {
|
||||||
|
// `fn foo() -> bool {};`
|
||||||
|
assert!(other.semi_token.is_none(), "unexpected token: `;`");
|
||||||
|
} else {
|
||||||
|
// `fn foo() -> bool`
|
||||||
|
assert!(other.semi_token.is_some(), "expected `;`");
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
sig: other.sig.ref_into(),
|
||||||
|
default: other.default.map_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&TraitItemMethod> for syn::TraitItemMethod {
|
||||||
|
fn from(other: &TraitItemMethod) -> Self {
|
||||||
|
Self {
|
||||||
|
attrs: other.attrs.map_into(),
|
||||||
|
sig: other.sig.ref_into(),
|
||||||
|
default: other.default.map_into(),
|
||||||
|
semi_token: default_or_none(other.default.is_none()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseTree
|
||||||
|
syn_trait_impl!(syn::UseTree);
|
||||||
|
impl From<&syn::UseTree> for UseTree {
|
||||||
|
fn from(other: &syn::UseTree) -> Self {
|
||||||
|
use super::UseTree::*;
|
||||||
|
use syn::UseTree;
|
||||||
|
match other {
|
||||||
|
UseTree::Path(x) => Path(x.into()),
|
||||||
|
UseTree::Name(x) => Name(x.into()),
|
||||||
|
UseTree::Rename(x) => Rename(x.into()),
|
||||||
|
UseTree::Glob(_) => Glob,
|
||||||
|
UseTree::Group(x) => Group(x.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&UseTree> for syn::UseTree {
|
||||||
|
fn from(other: &UseTree) -> Self {
|
||||||
|
use syn::UseTree::*;
|
||||||
|
match other {
|
||||||
|
UseTree::Path(x) => Path(x.into()),
|
||||||
|
UseTree::Name(x) => Name(x.into()),
|
||||||
|
UseTree::Rename(x) => Rename(x.into()),
|
||||||
|
UseTree::Glob => Glob(syn::UseGlob {
|
||||||
|
star_token: default(),
|
||||||
|
}),
|
||||||
|
UseTree::Group(x) => Group(x.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receiver
|
||||||
|
syn_trait_impl!(syn::Receiver);
|
||||||
|
impl From<&syn::Receiver> for Receiver {
|
||||||
|
fn from(node: &syn::Receiver) -> Self {
|
||||||
|
Receiver {
|
||||||
|
attrs: node.attrs.map_into(),
|
||||||
|
reference: node.reference.is_some(),
|
||||||
|
lifetime: node.reference.as_ref().and_then(|(_0, _1)| _1.map_into()),
|
||||||
|
mutability: node.mutability.is_some(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Receiver> for syn::Receiver {
|
||||||
|
fn from(node: &Receiver) -> Self {
|
||||||
|
syn::Receiver {
|
||||||
|
attrs: node.attrs.map_into(),
|
||||||
|
reference: if node.reference {
|
||||||
|
Some((default(), node.lifetime.map_into()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
mutability: default_or_none(node.mutability),
|
||||||
|
self_token: default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
210
syn-serde/src/json.rs
Normal file
210
syn-serde/src/json.rs
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
//! A module to provide functions for JSON <-> Rust serialize and deserialize.
|
||||||
|
//!
|
||||||
|
//! *This module is available if syn-serde is built with the `"json"` feature.*
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use serde_json::Result;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
// Serialize [`Syn`] type into JSON data.
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as JSON into the IO stream.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::io;
|
||||||
|
/// # fn to_writer<W>(writer: W, syn_file: &syn::File) -> serde_json::Result<()>
|
||||||
|
/// # where
|
||||||
|
/// # W: io::Write,
|
||||||
|
/// # {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_writer(writer, &serializable_file)
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_writer<S, W>(writer: W, syn: &S) -> Result<()>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
W: io::Write,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_writer(writer, &adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as pretty-printed JSON into the IO
|
||||||
|
/// stream.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::io;
|
||||||
|
/// # fn to_writer_pretty<W: io::Write>(writer: W, syn_file: &syn::File) -> serde_json::Result<()> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_writer_pretty(writer, &serializable_file)
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_writer_pretty<S, W>(writer: W, syn: &S) -> Result<()>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
W: io::Write,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_writer_pretty(writer, &adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as a JSON byte vector.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn to_vec(syn_file: &syn::File) -> Vec<u8> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_vec(&serializable_file).unwrap()
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_vec<S>(syn: &S) -> Vec<u8>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_vec(&adapter).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as a pretty-printed JSON byte vector.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn to_vec_pretty(syn_file: &syn::File) -> Vec<u8> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_vec_pretty(&serializable_file).unwrap()
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_vec_pretty<S>(syn: &S) -> Vec<u8>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_vec_pretty(&adapter).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as a String of JSON.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn to_string(syn_file: &syn::File) -> String {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_string(&serializable_file).unwrap()
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_string<S>(syn: &S) -> String
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_string(&adapter).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the given [`Syn`] type as a pretty-printed String of JSON.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn to_string_pretty(syn_file: &syn::File) -> String {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// serde_json::to_string_pretty(&serializable_file).unwrap()
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_string_pretty<S>(syn: &S) -> String
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter = syn.to_adapter();
|
||||||
|
serde_json::to_string_pretty(&adapter).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize JSON data to [`Syn`] type.
|
||||||
|
|
||||||
|
/// Deserialize an instance of [`Syn`] type from an IO stream of JSON.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::io;
|
||||||
|
/// # fn from_reader<R: io::Read>(rdr: R) -> serde_json::Result<syn::File> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file: <syn::File as Syn>::Adapter = serde_json::from_reader(rdr)?;
|
||||||
|
/// let syn_file = syn::File::from_adapter(&serializable_file);
|
||||||
|
/// Ok(syn_file)
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_reader<S, R>(rdr: R) -> Result<S>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
R: io::Read,
|
||||||
|
{
|
||||||
|
let adapter: S::Adapter = serde_json::from_reader(rdr)?;
|
||||||
|
Ok(S::from_adapter(&adapter))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize an instance of [`Syn`] type from bytes of JSON text.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn from_reader(v: &[u8]) -> serde_json::Result<syn::File> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file: <syn::File as Syn>::Adapter = serde_json::from_slice(v)?;
|
||||||
|
/// let syn_file = syn::File::from_adapter(&serializable_file);
|
||||||
|
/// Ok(syn_file)
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_slice<S>(v: &[u8]) -> Result<S>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter: S::Adapter = serde_json::from_slice(v)?;
|
||||||
|
Ok(S::from_adapter(&adapter))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize an instance of [`Syn`] type from a string of JSON text.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn from_str(s: &str) -> serde_json::Result<syn::File> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let serializable_file: <syn::File as Syn>::Adapter = serde_json::from_str(s)?;
|
||||||
|
/// let syn_file = syn::File::from_adapter(&serializable_file);
|
||||||
|
/// Ok(syn_file)
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_str<S>(s: &str) -> Result<S>
|
||||||
|
where
|
||||||
|
S: Syn,
|
||||||
|
{
|
||||||
|
let adapter: S::Adapter = serde_json::from_str(s)?;
|
||||||
|
Ok(S::from_adapter(&adapter))
|
||||||
|
}
|
350
syn-serde/src/lib.rs
Normal file
350
syn-serde/src/lib.rs
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
//! Library to serialize and deserialize [Syn] syntax trees.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [dependencies]
|
||||||
|
//! syn-serde = { version = "0.2", features = ["json"] }
|
||||||
|
//! syn = { version = "1", features = ["full"] }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[cfg(feature = "json")]
|
||||||
|
//! # fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
//! use syn_serde::json;
|
||||||
|
//!
|
||||||
|
//! let syn_file: syn::File = syn::parse_quote! {
|
||||||
|
//! fn main() {
|
||||||
|
//! println!("Hello, world!");
|
||||||
|
//! }
|
||||||
|
//! };
|
||||||
|
//! println!("{}", json::to_string_pretty(&syn_file));
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! This prints the following JSON:
|
||||||
|
//!
|
||||||
|
//! ```json
|
||||||
|
//! {
|
||||||
|
//! "items": [
|
||||||
|
//! {
|
||||||
|
//! "fn": {
|
||||||
|
//! "ident": "main",
|
||||||
|
//! "inputs": [],
|
||||||
|
//! "output": null,
|
||||||
|
//! "stmts": [
|
||||||
|
//! {
|
||||||
|
//! "semi": {
|
||||||
|
//! "macro": {
|
||||||
|
//! "path": {
|
||||||
|
//! "segments": [
|
||||||
|
//! {
|
||||||
|
//! "ident": "println"
|
||||||
|
//! }
|
||||||
|
//! ]
|
||||||
|
//! },
|
||||||
|
//! "delimiter": "paren",
|
||||||
|
//! "tokens": [
|
||||||
|
//! {
|
||||||
|
//! "lit": "\"Hello, world!\""
|
||||||
|
//! }
|
||||||
|
//! ]
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ]
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ]
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Rust source file -> JSON representation of the syntax tree
|
||||||
|
//!
|
||||||
|
//! The [`rust2json`] example parse a Rust source file into a `syn_serde::File`
|
||||||
|
//! and print out a JSON representation of the syntax tree.
|
||||||
|
//!
|
||||||
|
//! ### JSON file -> Rust syntax tree
|
||||||
|
//!
|
||||||
|
//! The [`json2rust`] example parse a JSON file into a `syn_serde::File` and
|
||||||
|
//! print out a Rust syntax tree.
|
||||||
|
//!
|
||||||
|
//! ## Optional features
|
||||||
|
//!
|
||||||
|
//! - **`json`** — Provides functions for JSON <-> Rust serializing and
|
||||||
|
//! deserializing.
|
||||||
|
//!
|
||||||
|
//! [Syn]: https://github.com/dtolnay/syn
|
||||||
|
//! [`rust2json`]: https://github.com/taiki-e/syn-serde/tree/master/examples/rust2json
|
||||||
|
//! [`json2rust`]: https://github.com/taiki-e/syn-serde/tree/master/examples/json2rust
|
||||||
|
|
||||||
|
#![doc(html_root_url = "https://docs.rs/syn-serde/0.2.0")]
|
||||||
|
#![doc(test(
|
||||||
|
no_crate_inject,
|
||||||
|
attr(
|
||||||
|
deny(warnings, rust_2018_idioms, single_use_lifetimes),
|
||||||
|
allow(dead_code)
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms, unreachable_pub)]
|
||||||
|
// It cannot be included in the published code because these lints have false positives in the minimum required version.
|
||||||
|
#![cfg_attr(test, warn(single_use_lifetimes))]
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
// mem::take requires Rust 1.40
|
||||||
|
#![allow(clippy::mem_replace_with_default)]
|
||||||
|
#![allow(clippy::large_enum_variant, clippy::needless_doctest_main)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
mod gen;
|
||||||
|
|
||||||
|
mod attr;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::attr::*;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::data::*;
|
||||||
|
|
||||||
|
mod expr;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::expr::*;
|
||||||
|
|
||||||
|
mod generics;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::generics::*;
|
||||||
|
|
||||||
|
mod item;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::item::*;
|
||||||
|
|
||||||
|
mod file;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::file::File;
|
||||||
|
|
||||||
|
mod lifetime;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::lifetime::Lifetime;
|
||||||
|
|
||||||
|
mod lit;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::lit::*;
|
||||||
|
|
||||||
|
mod mac;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::mac::{Macro, MacroDelimiter};
|
||||||
|
|
||||||
|
mod op;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::op::{BinOp, UnOp};
|
||||||
|
|
||||||
|
mod ty;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::ty::*;
|
||||||
|
|
||||||
|
mod pat;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::pat::*;
|
||||||
|
|
||||||
|
mod path;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::path::*;
|
||||||
|
|
||||||
|
mod stmt;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::stmt::{Block, Local, Stmt};
|
||||||
|
|
||||||
|
mod token_stream;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use crate::token_stream::{
|
||||||
|
Delimiter, Group, Ident, Literal, Punct, Spacing, TokenStream, TokenTree,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
pub mod json;
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Syn trait
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for the data structures of [Syn] and [proc-macro2].
|
||||||
|
///
|
||||||
|
/// [Syn]: https://github.com/dtolnay/syn
|
||||||
|
/// [proc-macro2]: https://github.com/alexcrichton/proc-macro2
|
||||||
|
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
|
||||||
|
pub trait Syn: Sized + private::Sealed {
|
||||||
|
type Adapter: Serialize + for<'de> Deserialize<'de>;
|
||||||
|
|
||||||
|
/// Converts a `Syn` type into an adapter.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "json")]
|
||||||
|
/// # fn dox() {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// let syn_file: syn::File = syn::parse_quote! {
|
||||||
|
/// fn main() {
|
||||||
|
/// println!("Hello, world!");
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let serializable_file = syn_file.to_adapter();
|
||||||
|
/// println!("{}", serde_json::to_string_pretty(&serializable_file).unwrap());
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
fn to_adapter(&self) -> Self::Adapter;
|
||||||
|
|
||||||
|
/// Converts an adapter into a `Syn` type.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "json")]
|
||||||
|
/// # fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// use syn_serde::Syn;
|
||||||
|
///
|
||||||
|
/// // `struct Unit;`
|
||||||
|
/// let json = r#"{
|
||||||
|
/// "struct": {
|
||||||
|
/// "ident": "Unit",
|
||||||
|
/// "fields": "unit"
|
||||||
|
/// }
|
||||||
|
/// }"#;
|
||||||
|
///
|
||||||
|
/// let serializable_file: <syn::File as Syn>::Adapter = serde_json::from_str(json)?;
|
||||||
|
/// let _syn_file = syn::File::from_adapter(&serializable_file);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
fn from_adapter(adapter: &Self::Adapter) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
use proc_macro2::Span;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
type Punctuated<T> = Vec<T>;
|
||||||
|
|
||||||
|
fn default<T>() -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
T::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_or_none<T>(x: bool) -> Option<T>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
if x {
|
||||||
|
Some(T::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not<T>(x: T) -> T::Output
|
||||||
|
where
|
||||||
|
T: std::ops::Not,
|
||||||
|
{
|
||||||
|
!x
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/rust/issues/51443
|
||||||
|
trait RefInto<U>: Sized {
|
||||||
|
fn ref_into<'a>(&'a self) -> U
|
||||||
|
where
|
||||||
|
&'a Self: Into<U>,
|
||||||
|
{
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> RefInto<U> for T {}
|
||||||
|
|
||||||
|
trait MapInto<U, M> {
|
||||||
|
type T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, f: F) -> M
|
||||||
|
where
|
||||||
|
Self::T: 'a,
|
||||||
|
F: FnMut(&'a Self::T) -> U;
|
||||||
|
|
||||||
|
fn map_into<'a>(&'a self) -> M
|
||||||
|
where
|
||||||
|
Self::T: 'a,
|
||||||
|
&'a Self::T: Into<U>,
|
||||||
|
{
|
||||||
|
self.ref_map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> MapInto<U, Vec<U>> for Vec<T> {
|
||||||
|
type T = T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, f: F) -> Vec<U>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a Self::T) -> U,
|
||||||
|
{
|
||||||
|
self.iter().map(f).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U, P> MapInto<U, syn::punctuated::Punctuated<U, P>> for Vec<T>
|
||||||
|
where
|
||||||
|
P: Default,
|
||||||
|
{
|
||||||
|
type T = T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, f: F) -> syn::punctuated::Punctuated<U, P>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a Self::T) -> U,
|
||||||
|
{
|
||||||
|
self.iter().map(f).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U, P> MapInto<U, Vec<U>> for syn::punctuated::Punctuated<T, P>
|
||||||
|
where
|
||||||
|
P: Default,
|
||||||
|
{
|
||||||
|
type T = T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, f: F) -> Vec<U>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a Self::T) -> U,
|
||||||
|
{
|
||||||
|
self.iter().map(f).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> MapInto<U, Option<U>> for Option<T> {
|
||||||
|
type T = T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, f: F) -> Option<U>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a Self::T) -> U,
|
||||||
|
{
|
||||||
|
self.as_ref().map(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> MapInto<U, Box<U>> for Box<T> {
|
||||||
|
type T = T;
|
||||||
|
|
||||||
|
fn ref_map<'a, F>(&'a self, mut f: F) -> Box<U>
|
||||||
|
where
|
||||||
|
F: FnMut(&'a Self::T) -> U,
|
||||||
|
{
|
||||||
|
Box::new(f(&**self))
|
||||||
|
}
|
||||||
|
}
|
19
syn-serde/src/lifetime.rs
Normal file
19
syn-serde/src/lifetime.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A Rust lifetime: `'a`.
|
||||||
|
///
|
||||||
|
/// Lifetime names must conform to the following rules:
|
||||||
|
///
|
||||||
|
/// - Must start with an apostrophe.
|
||||||
|
/// - Must not consist of just an apostrophe: `'`.
|
||||||
|
/// - Character after the apostrophe must be `_` or a Unicode code point with
|
||||||
|
/// the XID_Start property.
|
||||||
|
/// - All following characters must be Unicode code points with the XID_Continue
|
||||||
|
/// property.
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Lifetime {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
}
|
||||||
|
}
|
525
syn-serde/src/lit.rs
Normal file
525
syn-serde/src/lit.rs
Normal file
|
@ -0,0 +1,525 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A Rust literal such as a string or integer or boolean.
|
||||||
|
pub enum Lit {
|
||||||
|
/// A UTF-8 string literal: `"foo"`.
|
||||||
|
Str(LitStr),
|
||||||
|
|
||||||
|
/// A byte string literal: `b"foo"`.
|
||||||
|
ByteStr(LitByteStr),
|
||||||
|
|
||||||
|
/// A byte literal: `b'f'`.
|
||||||
|
Byte(LitByte),
|
||||||
|
|
||||||
|
/// A character literal: `'a'`.
|
||||||
|
Char(LitChar),
|
||||||
|
|
||||||
|
/// An integer literal: `1` or `1u16`.
|
||||||
|
Int(LitInt),
|
||||||
|
|
||||||
|
/// A floating point literal: `1f64` or `1.0e10f64`.
|
||||||
|
///
|
||||||
|
/// Must be finite. May not be infinte or NaN.
|
||||||
|
Float(LitFloat),
|
||||||
|
|
||||||
|
/// A boolean literal: `true` or `false`.
|
||||||
|
Bool(LitBool),
|
||||||
|
|
||||||
|
/// A raw token literal not interpreted by Syn.
|
||||||
|
Verbatim(Literal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A UTF-8 string literal: `"foo"`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitStr {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A byte string literal: `b"foo"`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitByteStr {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A byte literal: `b'f'`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitByte {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A character literal: `'a'`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitChar {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An integer literal: `1` or `1u16`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitInt {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A floating point literal: `1f64` or `1.0e10f64`.
|
||||||
|
///
|
||||||
|
/// Must be finite. May not be infinte or NaN.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitFloat {
|
||||||
|
token: Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A boolean literal: `true` or `false`.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct LitBool {
|
||||||
|
pub(crate) value: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// The style of a string literal, either plain quoted or a raw string like
|
||||||
|
/// `r##"data"##`.
|
||||||
|
pub enum StrStyle {
|
||||||
|
/// An ordinary string like `"data"`.
|
||||||
|
Cooked,
|
||||||
|
/// A raw string like `r##"data"##`.
|
||||||
|
///
|
||||||
|
/// The unsigned integer is the number of `#` symbols used.
|
||||||
|
Raw(usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod value {
|
||||||
|
use super::*;
|
||||||
|
use proc_macro2::{TokenStream, TokenTree};
|
||||||
|
use std::{
|
||||||
|
char,
|
||||||
|
ops::{Index, RangeFrom},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get the byte at offset idx, or a default of `b'\0'` if we're looking
|
||||||
|
/// past the end of the input buffer.
|
||||||
|
pub(crate) fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
|
||||||
|
let s = s.as_ref();
|
||||||
|
if idx < s.len() {
|
||||||
|
s[idx]
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_chr(s: &str) -> char {
|
||||||
|
s.chars().next().unwrap_or('\0')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns (content, suffix).
|
||||||
|
pub(crate) fn parse_lit_str(s: &str) -> (Box<str>, Box<str>) {
|
||||||
|
match byte(s, 0) {
|
||||||
|
b'"' => parse_lit_str_cooked(s),
|
||||||
|
b'r' => parse_lit_str_raw(s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_lit_str_cooked(mut s: &str) -> (Box<str>, Box<str>) {
|
||||||
|
assert_eq!(byte(s, 0), b'"');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut content = String::new();
|
||||||
|
'outer: loop {
|
||||||
|
let ch = match byte(s, 0) {
|
||||||
|
b'"' => break,
|
||||||
|
b'\\' => {
|
||||||
|
let b = byte(s, 1);
|
||||||
|
s = &s[2..];
|
||||||
|
match b {
|
||||||
|
b'x' => {
|
||||||
|
let (byte, rest) = backslash_x(s);
|
||||||
|
s = rest;
|
||||||
|
assert!(byte <= 0x80, "Invalid \\x byte in string literal");
|
||||||
|
char::from_u32(u32::from(byte)).unwrap()
|
||||||
|
}
|
||||||
|
b'u' => {
|
||||||
|
let (chr, rest) = backslash_u(s);
|
||||||
|
s = rest;
|
||||||
|
chr
|
||||||
|
}
|
||||||
|
b'n' => '\n',
|
||||||
|
b'r' => '\r',
|
||||||
|
b't' => '\t',
|
||||||
|
b'\\' => '\\',
|
||||||
|
b'0' => '\0',
|
||||||
|
b'\'' => '\'',
|
||||||
|
b'"' => '"',
|
||||||
|
b'\r' | b'\n' => loop {
|
||||||
|
let ch = next_chr(s);
|
||||||
|
if ch.is_whitespace() {
|
||||||
|
s = &s[ch.len_utf8()..];
|
||||||
|
} else {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'\r' => {
|
||||||
|
assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
|
||||||
|
s = &s[2..];
|
||||||
|
'\n'
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let ch = next_chr(s);
|
||||||
|
s = &s[ch.len_utf8()..];
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
};
|
||||||
|
content.push(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(s.starts_with('"'));
|
||||||
|
let content = content.into_boxed_str();
|
||||||
|
let suffix = s[1..].to_owned().into_boxed_str();
|
||||||
|
(content, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_lit_str_raw(mut s: &str) -> (Box<str>, Box<str>) {
|
||||||
|
assert_eq!(byte(s, 0), b'r');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut pounds = 0;
|
||||||
|
while byte(s, pounds) == b'#' {
|
||||||
|
pounds += 1;
|
||||||
|
}
|
||||||
|
assert_eq!(byte(s, pounds), b'"');
|
||||||
|
assert_eq!(byte(s, s.len() - pounds - 1), b'"');
|
||||||
|
for end in s[s.len() - pounds..].bytes() {
|
||||||
|
assert_eq!(end, b'#');
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = s[pounds + 1..s.len() - pounds - 1]
|
||||||
|
.to_owned()
|
||||||
|
.into_boxed_str();
|
||||||
|
let suffix = Box::<str>::default(); // todo
|
||||||
|
(content, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_lit_byte_str(s: &str) -> Vec<u8> {
|
||||||
|
assert_eq!(byte(s, 0), b'b');
|
||||||
|
match byte(s, 1) {
|
||||||
|
b'"' => parse_lit_byte_str_cooked(s),
|
||||||
|
b'r' => parse_lit_byte_str_raw(s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_lit_byte_str_cooked(mut s: &str) -> Vec<u8> {
|
||||||
|
assert_eq!(byte(s, 0), b'b');
|
||||||
|
assert_eq!(byte(s, 1), b'"');
|
||||||
|
s = &s[2..];
|
||||||
|
|
||||||
|
// We're going to want to have slices which don't respect codepoint boundaries.
|
||||||
|
let mut s = s.as_bytes();
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
'outer: loop {
|
||||||
|
let byte = match byte(s, 0) {
|
||||||
|
b'"' => break,
|
||||||
|
b'\\' => {
|
||||||
|
let b = byte(s, 1);
|
||||||
|
s = &s[2..];
|
||||||
|
match b {
|
||||||
|
b'x' => {
|
||||||
|
let (b, rest) = backslash_x(s);
|
||||||
|
s = rest;
|
||||||
|
b
|
||||||
|
}
|
||||||
|
b'n' => b'\n',
|
||||||
|
b'r' => b'\r',
|
||||||
|
b't' => b'\t',
|
||||||
|
b'\\' => b'\\',
|
||||||
|
b'0' => b'\0',
|
||||||
|
b'\'' => b'\'',
|
||||||
|
b'"' => b'"',
|
||||||
|
b'\r' | b'\n' => loop {
|
||||||
|
let byte = byte(s, 0);
|
||||||
|
let ch = char::from_u32(u32::from(byte)).unwrap();
|
||||||
|
if ch.is_whitespace() {
|
||||||
|
s = &s[1..];
|
||||||
|
} else {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'\r' => {
|
||||||
|
assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string");
|
||||||
|
s = &s[2..];
|
||||||
|
b'\n'
|
||||||
|
}
|
||||||
|
b => {
|
||||||
|
s = &s[1..];
|
||||||
|
b
|
||||||
|
}
|
||||||
|
};
|
||||||
|
out.push(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(s, b"\"");
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_lit_byte_str_raw(s: &str) -> Vec<u8> {
|
||||||
|
assert_eq!(byte(s, 0), b'b');
|
||||||
|
String::from(parse_lit_str_raw(&s[1..]).0).into_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_lit_byte(s: &str) -> u8 {
|
||||||
|
assert_eq!(byte(s, 0), b'b');
|
||||||
|
assert_eq!(byte(s, 1), b'\'');
|
||||||
|
|
||||||
|
// We're going to want to have slices which don't respect codepoint boundaries.
|
||||||
|
let mut s = s[2..].as_bytes();
|
||||||
|
|
||||||
|
let b = match byte(s, 0) {
|
||||||
|
b'\\' => {
|
||||||
|
let b = byte(s, 1);
|
||||||
|
s = &s[2..];
|
||||||
|
match b {
|
||||||
|
b'x' => {
|
||||||
|
let (b, rest) = backslash_x(s);
|
||||||
|
s = rest;
|
||||||
|
b
|
||||||
|
}
|
||||||
|
b'n' => b'\n',
|
||||||
|
b'r' => b'\r',
|
||||||
|
b't' => b'\t',
|
||||||
|
b'\\' => b'\\',
|
||||||
|
b'0' => b'\0',
|
||||||
|
b'\'' => b'\'',
|
||||||
|
b'"' => b'"',
|
||||||
|
b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b => {
|
||||||
|
s = &s[1..];
|
||||||
|
b
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(byte(s, 0), b'\'');
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_lit_char(mut s: &str) -> char {
|
||||||
|
assert_eq!(byte(s, 0), b'\'');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let ch = if byte(s, 0) == b'\\' {
|
||||||
|
let b = byte(s, 1);
|
||||||
|
s = &s[2..];
|
||||||
|
match b {
|
||||||
|
b'x' => {
|
||||||
|
let (byte, rest) = backslash_x(s);
|
||||||
|
s = rest;
|
||||||
|
assert!(byte <= 0x80, "Invalid \\x byte in string literal");
|
||||||
|
char::from_u32(u32::from(byte)).unwrap()
|
||||||
|
}
|
||||||
|
b'u' => {
|
||||||
|
let (chr, rest) = backslash_u(s);
|
||||||
|
s = rest;
|
||||||
|
chr
|
||||||
|
}
|
||||||
|
b'n' => '\n',
|
||||||
|
b'r' => '\r',
|
||||||
|
b't' => '\t',
|
||||||
|
b'\\' => '\\',
|
||||||
|
b'0' => '\0',
|
||||||
|
b'\'' => '\'',
|
||||||
|
b'"' => '"',
|
||||||
|
b => panic!("unexpected byte {:?} after \\ character in byte literal", b),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ch = next_chr(s);
|
||||||
|
s = &s[ch.len_utf8()..];
|
||||||
|
ch
|
||||||
|
};
|
||||||
|
assert_eq!(s, "\'", "Expected end of char literal");
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backslash_x<S>(s: &S) -> (u8, &S)
|
||||||
|
where
|
||||||
|
S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
|
||||||
|
{
|
||||||
|
let mut ch = 0;
|
||||||
|
let b0 = byte(s, 0);
|
||||||
|
let b1 = byte(s, 1);
|
||||||
|
ch += 0x10
|
||||||
|
* match b0 {
|
||||||
|
b'0'..=b'9' => b0 - b'0',
|
||||||
|
b'a'..=b'f' => 10 + (b0 - b'a'),
|
||||||
|
b'A'..=b'F' => 10 + (b0 - b'A'),
|
||||||
|
_ => panic!("unexpected non-hex character after \\x"),
|
||||||
|
};
|
||||||
|
ch += match b1 {
|
||||||
|
b'0'..=b'9' => b1 - b'0',
|
||||||
|
b'a'..=b'f' => 10 + (b1 - b'a'),
|
||||||
|
b'A'..=b'F' => 10 + (b1 - b'A'),
|
||||||
|
_ => panic!("unexpected non-hex character after \\x"),
|
||||||
|
};
|
||||||
|
(ch, &s[2..])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backslash_u(mut s: &str) -> (char, &str) {
|
||||||
|
if byte(s, 0) != b'{' {
|
||||||
|
panic!("expected {{ after \\u");
|
||||||
|
}
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
let mut ch = 0;
|
||||||
|
for _ in 0..6 {
|
||||||
|
let b = byte(s, 0);
|
||||||
|
match b {
|
||||||
|
b'0'..=b'9' => {
|
||||||
|
ch *= 0x10;
|
||||||
|
ch += u32::from(b - b'0');
|
||||||
|
s = &s[1..];
|
||||||
|
}
|
||||||
|
b'a'..=b'f' => {
|
||||||
|
ch *= 0x10;
|
||||||
|
ch += u32::from(10 + b - b'a');
|
||||||
|
s = &s[1..];
|
||||||
|
}
|
||||||
|
b'A'..=b'F' => {
|
||||||
|
ch *= 0x10;
|
||||||
|
ch += u32::from(10 + b - b'A');
|
||||||
|
s = &s[1..];
|
||||||
|
}
|
||||||
|
b'}' => break,
|
||||||
|
_ => panic!("unexpected non-hex character after \\u"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(byte(s, 0) == b'}');
|
||||||
|
s = &s[1..];
|
||||||
|
|
||||||
|
if let Some(ch) = char::from_u32(ch) {
|
||||||
|
(ch, s)
|
||||||
|
} else {
|
||||||
|
panic!("character code {:x} is not a valid unicode character", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_literal(s: &str) -> Literal {
|
||||||
|
let stream = s.parse::<TokenStream>().unwrap();
|
||||||
|
match stream.into_iter().next().unwrap() {
|
||||||
|
TokenTree::Literal(l) => l.ref_into(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// LitStr
|
||||||
|
impl From<&syn::LitStr> for LitStr {
|
||||||
|
fn from(other: &syn::LitStr) -> Self {
|
||||||
|
Self {
|
||||||
|
token: Literal::string(&other.value()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitStr> for syn::LitStr {
|
||||||
|
fn from(other: &LitStr) -> Self {
|
||||||
|
let (value, _) = value::parse_lit_str(&other.token.text);
|
||||||
|
Self::new(&value, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LitByteStr
|
||||||
|
impl From<&syn::LitByteStr> for LitByteStr {
|
||||||
|
fn from(other: &syn::LitByteStr) -> Self {
|
||||||
|
Self {
|
||||||
|
token: Literal::byte_string(&other.value()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitByteStr> for syn::LitByteStr {
|
||||||
|
fn from(other: &LitByteStr) -> Self {
|
||||||
|
let value = value::parse_lit_byte_str(&other.token.text);
|
||||||
|
Self::new(&value, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LitByte
|
||||||
|
impl From<&syn::LitByte> for LitByte {
|
||||||
|
fn from(other: &syn::LitByte) -> Self {
|
||||||
|
Self {
|
||||||
|
token: Literal::u8_suffixed(other.value()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitByte> for syn::LitByte {
|
||||||
|
fn from(other: &LitByte) -> Self {
|
||||||
|
let value = value::parse_lit_byte(&other.token.text);
|
||||||
|
Self::new(value, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LitChar
|
||||||
|
impl From<&syn::LitChar> for LitChar {
|
||||||
|
fn from(other: &syn::LitChar) -> Self {
|
||||||
|
Self {
|
||||||
|
token: Literal::character(other.value()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitChar> for syn::LitChar {
|
||||||
|
fn from(other: &LitChar) -> Self {
|
||||||
|
let value = value::parse_lit_char(&other.token.text);
|
||||||
|
Self::new(value, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LitInt
|
||||||
|
impl From<&syn::LitInt> for LitInt {
|
||||||
|
fn from(other: &syn::LitInt) -> Self {
|
||||||
|
Self {
|
||||||
|
token: value::to_literal(&other.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitInt> for syn::LitInt {
|
||||||
|
fn from(other: &LitInt) -> Self {
|
||||||
|
Self::new(&other.token.text, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LitFloat
|
||||||
|
impl From<&syn::LitFloat> for LitFloat {
|
||||||
|
fn from(other: &syn::LitFloat) -> Self {
|
||||||
|
Self {
|
||||||
|
token: value::to_literal(&other.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&LitFloat> for syn::LitFloat {
|
||||||
|
fn from(other: &LitFloat) -> Self {
|
||||||
|
Self::new(&other.token.text, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
syn-serde/src/mac.rs
Normal file
19
syn-serde/src/mac.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro invocation: `println!("{}", mac)`.
|
||||||
|
pub struct Macro {
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) delimiter: MacroDelimiter,
|
||||||
|
pub(crate) tokens: TokenStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`.
|
||||||
|
pub enum MacroDelimiter {
|
||||||
|
Paren,
|
||||||
|
Brace,
|
||||||
|
Bracket,
|
||||||
|
}
|
||||||
|
}
|
57
syn-serde/src/macros.rs
Normal file
57
syn-serde/src/macros.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
macro_rules! ast_struct {
|
||||||
|
(
|
||||||
|
[$($attrs_pub:tt)*]
|
||||||
|
struct $name:ident $($rest:tt)*
|
||||||
|
) => {
|
||||||
|
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||||
|
$($attrs_pub)* struct $name $($rest)*
|
||||||
|
};
|
||||||
|
|
||||||
|
($($t:tt)*) => {
|
||||||
|
strip_attrs_pub!(ast_struct!($($t)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ast_enum {
|
||||||
|
(
|
||||||
|
[$($attrs_pub:tt)*]
|
||||||
|
enum $name:ident $($rest:tt)*
|
||||||
|
) => (
|
||||||
|
#[derive(Debug, crate::Serialize, crate::Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
$($attrs_pub)* enum $name $($rest)*
|
||||||
|
);
|
||||||
|
|
||||||
|
($($t:tt)*) => {
|
||||||
|
strip_attrs_pub!(ast_enum!($($t)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! strip_attrs_pub {
|
||||||
|
($mac:ident!($(#[$m:meta])* $pub:ident $($t:tt)*)) => {
|
||||||
|
check_keyword_matches!(pub $pub);
|
||||||
|
|
||||||
|
$mac!([$(#[$m])* $pub] $($t)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! check_keyword_matches {
|
||||||
|
(struct struct) => {};
|
||||||
|
(enum enum) => {};
|
||||||
|
(pub pub) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! syn_trait_impl {
|
||||||
|
($path:ident :: $ty:ident) => {
|
||||||
|
impl crate::private::Sealed for $path::$ty {}
|
||||||
|
impl crate::Syn for $path::$ty {
|
||||||
|
type Adapter = $ty;
|
||||||
|
fn to_adapter(&self) -> Self::Adapter {
|
||||||
|
Self::Adapter::from(self)
|
||||||
|
}
|
||||||
|
fn from_adapter(adapter: &Self::Adapter) -> Self {
|
||||||
|
Self::from(adapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
104
syn-serde/src/op.rs
Normal file
104
syn-serde/src/op.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
ast_enum! {
|
||||||
|
/// A binary operator: `+`, `+=`, `&`.
|
||||||
|
pub enum BinOp {
|
||||||
|
/// The `+` operator (addition)
|
||||||
|
#[serde(rename = "+")]
|
||||||
|
Add,
|
||||||
|
/// The `-` operator (subtraction)
|
||||||
|
#[serde(rename = "-")]
|
||||||
|
Sub,
|
||||||
|
/// The `*` operator (multiplication)
|
||||||
|
#[serde(rename = "*")]
|
||||||
|
Mul,
|
||||||
|
/// The `/` operator (division)
|
||||||
|
#[serde(rename = "/")]
|
||||||
|
Div,
|
||||||
|
/// The `%` operator (modulus)
|
||||||
|
#[serde(rename = "%")]
|
||||||
|
Rem,
|
||||||
|
/// The `&&` operator (logical and)
|
||||||
|
#[serde(rename = "&&")]
|
||||||
|
And,
|
||||||
|
/// The `||` operator (logical or)
|
||||||
|
#[serde(rename = "||")]
|
||||||
|
Or,
|
||||||
|
/// The `^` operator (bitwise xor)
|
||||||
|
#[serde(rename = "^")]
|
||||||
|
BitXor,
|
||||||
|
/// The `&` operator (bitwise and)
|
||||||
|
#[serde(rename = "&")]
|
||||||
|
BitAnd,
|
||||||
|
/// The `|` operator (bitwise or)
|
||||||
|
#[serde(rename = "|")]
|
||||||
|
BitOr,
|
||||||
|
/// The `<<` operator (shift left)
|
||||||
|
#[serde(rename = "<<")]
|
||||||
|
Shl,
|
||||||
|
/// The `>>` operator (shift right)
|
||||||
|
#[serde(rename = ">>")]
|
||||||
|
Shr,
|
||||||
|
/// The `==` operator (equality)
|
||||||
|
#[serde(rename = "==")]
|
||||||
|
Eq,
|
||||||
|
/// The `<` operator (less than)
|
||||||
|
#[serde(rename = "<")]
|
||||||
|
Lt,
|
||||||
|
/// The `<=` operator (less than or equal to)
|
||||||
|
#[serde(rename = "<=")]
|
||||||
|
Le,
|
||||||
|
/// The `!=` operator (not equal to)
|
||||||
|
#[serde(rename = "!=")]
|
||||||
|
Ne,
|
||||||
|
/// The `>=` operator (greater than or equal to)
|
||||||
|
#[serde(rename = ">=")]
|
||||||
|
Ge,
|
||||||
|
/// The `>` operator (greater than)
|
||||||
|
#[serde(rename = ">")]
|
||||||
|
Gt,
|
||||||
|
/// The `+=` operator
|
||||||
|
#[serde(rename = "+=")]
|
||||||
|
AddEq,
|
||||||
|
/// The `-=` operator
|
||||||
|
#[serde(rename = "-=")]
|
||||||
|
SubEq,
|
||||||
|
/// The `*=` operator
|
||||||
|
#[serde(rename = "*=")]
|
||||||
|
MulEq,
|
||||||
|
/// The `/=` operator
|
||||||
|
#[serde(rename = "/=")]
|
||||||
|
DivEq,
|
||||||
|
/// The `%=` operator
|
||||||
|
#[serde(rename = "%=")]
|
||||||
|
RemEq,
|
||||||
|
/// The `^=` operator
|
||||||
|
#[serde(rename = "^=")]
|
||||||
|
BitXorEq,
|
||||||
|
/// The `&=` operator
|
||||||
|
#[serde(rename = "&=")]
|
||||||
|
BitAndEq,
|
||||||
|
/// The `|=` operator
|
||||||
|
#[serde(rename = "|=")]
|
||||||
|
BitOrEq,
|
||||||
|
/// The `<<=` operator
|
||||||
|
#[serde(rename = "<<=")]
|
||||||
|
ShlEq,
|
||||||
|
/// The `>>=` operator
|
||||||
|
#[serde(rename = ">>=")]
|
||||||
|
ShrEq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A unary operator: `*`, `!`, `-`.
|
||||||
|
pub enum UnOp {
|
||||||
|
/// The `*` operator for dereferencing
|
||||||
|
#[serde(rename = "*")]
|
||||||
|
Deref,
|
||||||
|
/// The `!` operator for logical inversion
|
||||||
|
#[serde(rename = "!")]
|
||||||
|
Not,
|
||||||
|
/// The `-` operator for negation
|
||||||
|
#[serde(rename = "-")]
|
||||||
|
Neg,
|
||||||
|
}
|
||||||
|
}
|
250
syn-serde/src/pat.rs
Normal file
250
syn-serde/src/pat.rs
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A pattern in a local binding, function signature, match expression, or
|
||||||
|
/// various other places.
|
||||||
|
pub enum Pat {
|
||||||
|
/// A box pattern: `box v`.
|
||||||
|
Box(PatBox),
|
||||||
|
|
||||||
|
/// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`.
|
||||||
|
Ident(PatIdent),
|
||||||
|
|
||||||
|
/// A literal pattern: `0`.
|
||||||
|
///
|
||||||
|
/// This holds an `Expr` rather than a `Lit` because negative numbers
|
||||||
|
/// are represented as an `Expr::Unary`.
|
||||||
|
Lit(PatLit),
|
||||||
|
|
||||||
|
/// A macro in pattern position.
|
||||||
|
Macro(PatMacro),
|
||||||
|
|
||||||
|
/// A pattern that matches any one of a set of cases.
|
||||||
|
Or(PatOr),
|
||||||
|
|
||||||
|
/// A path pattern like `Color::Red`, optionally qualified with a
|
||||||
|
/// self-type.
|
||||||
|
///
|
||||||
|
/// Unqualified path patterns can legally refer to variants, structs,
|
||||||
|
/// constants or associated constants. Qualified path patterns like
|
||||||
|
/// `<A>::B::C` and `<A as Trait>::B::C` can only legally refer to
|
||||||
|
/// associated constants.
|
||||||
|
Path(PatPath),
|
||||||
|
|
||||||
|
/// A range pattern: `1..=2`.
|
||||||
|
Range(PatRange),
|
||||||
|
|
||||||
|
/// A reference pattern: `&mut var`.
|
||||||
|
Reference(PatReference),
|
||||||
|
|
||||||
|
/// The dots in a tuple or slice pattern: `[0, 1, ..]`
|
||||||
|
Rest(PatRest),
|
||||||
|
|
||||||
|
/// A dynamically sized slice pattern: `[a, b, ref i @ .., y, z]`.
|
||||||
|
Slice(PatSlice),
|
||||||
|
|
||||||
|
/// A struct or struct variant pattern: `Variant { x, y, .. }`.
|
||||||
|
Struct(PatStruct),
|
||||||
|
|
||||||
|
/// A tuple pattern: `(a, b)`.
|
||||||
|
Tuple(PatTuple),
|
||||||
|
|
||||||
|
/// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`.
|
||||||
|
TupleStruct(PatTupleStruct),
|
||||||
|
|
||||||
|
/// A type ascription pattern: `foo: f64`.
|
||||||
|
Type(PatType),
|
||||||
|
|
||||||
|
/// Tokens in pattern position not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
/// A pattern that matches any value: `_`.
|
||||||
|
#[serde(rename = "_")]
|
||||||
|
Wild(PatWild),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A box pattern: `box v`.
|
||||||
|
pub struct PatBox {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) pat: Box<Pat>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`.
|
||||||
|
pub struct PatIdent {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "ref")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) by_ref: bool,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) subpat: Option<Box<Pat>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A literal pattern: `0`.
|
||||||
|
///
|
||||||
|
/// This holds an `Expr` rather than a `Lit` because negative numbers
|
||||||
|
/// are represented as an `Expr::Unary`.
|
||||||
|
pub struct PatLit {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro in pattern position.
|
||||||
|
pub struct PatMacro {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A pattern that matches any one of a set of cases.
|
||||||
|
pub struct PatOr {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) leading_vert: bool,
|
||||||
|
pub(crate) cases: Punctuated<Pat>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A path pattern like `Color::Red`, optionally qualified with a
|
||||||
|
/// self-type.
|
||||||
|
///
|
||||||
|
/// Unqualified path patterns can legally refer to variants, structs,
|
||||||
|
/// constants or associated constants. Qualified path patterns like
|
||||||
|
/// `<A>::B::C` and `<A as Trait>::B::C` can only legally refer to
|
||||||
|
/// associated constants.
|
||||||
|
pub struct PatPath {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) qself: Option<QSelf>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) path: Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A range pattern: `1..=2`.
|
||||||
|
pub struct PatRange {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) lo: Box<Expr>,
|
||||||
|
pub(crate) limits: RangeLimits,
|
||||||
|
pub(crate) hi: Box<Expr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A reference pattern: `&mut var`.
|
||||||
|
pub struct PatReference {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) pat: Box<Pat>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The dots in a tuple or slice pattern: `[0, 1, ..]`
|
||||||
|
pub struct PatRest {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A dynamically sized slice pattern: `[a, b, ref i @ .., y, z]`.
|
||||||
|
pub struct PatSlice {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) elems: Punctuated<Pat>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A struct or struct variant pattern: `Variant { x, y, .. }`.
|
||||||
|
pub struct PatStruct {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) fields: Punctuated<FieldPat>,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) dot2_token: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A tuple pattern: `(a, b)`.
|
||||||
|
pub struct PatTuple {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) elems: Punctuated<Pat>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`.
|
||||||
|
pub struct PatTupleStruct {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) path: Path,
|
||||||
|
pub(crate) pat: PatTuple,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A type ascription pattern: `foo: f64`.
|
||||||
|
pub struct PatType {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) pat: Box<Pat>,
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A pattern that matches any value: `_`.
|
||||||
|
pub struct PatWild {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A single field in a struct pattern.
|
||||||
|
///
|
||||||
|
/// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated
|
||||||
|
/// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token.
|
||||||
|
pub struct FieldPat {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) member: Member,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon_token: bool,
|
||||||
|
pub(crate) pat: Box<Pat>,
|
||||||
|
}
|
||||||
|
}
|
136
syn-serde/src/path.rs
Normal file
136
syn-serde/src/path.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A path at which a named item is exported: `std::collections::HashMap`.
|
||||||
|
pub struct Path {
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) leading_colon: bool,
|
||||||
|
pub(crate) segments: Punctuated<PathSegment>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A segment of a path together with any path arguments on that segment.
|
||||||
|
pub struct PathSegment {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
#[serde(default, skip_serializing_if = "PathArguments::is_none")]
|
||||||
|
pub(crate) arguments: PathArguments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Angle bracketed or parenthesized arguments of a path segment.
|
||||||
|
///
|
||||||
|
/// ## Angle bracketed
|
||||||
|
///
|
||||||
|
/// The `<'a, T>` in `std::slice::iter<'a, T>`.
|
||||||
|
///
|
||||||
|
/// ## Parenthesized
|
||||||
|
///
|
||||||
|
/// The `(A, B) -> C` in `Fn(A, B) -> C`.
|
||||||
|
pub enum PathArguments {
|
||||||
|
None,
|
||||||
|
/// The `<'a, T>` in `std::slice::iter<'a, T>`.
|
||||||
|
AngleBracketed(AngleBracketedGenericArguments),
|
||||||
|
/// The `(A, B) -> C` in `Fn(A, B) -> C`.
|
||||||
|
Parenthesized(ParenthesizedGenericArguments),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PathArguments {
|
||||||
|
fn default() -> Self {
|
||||||
|
PathArguments::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathArguments {
|
||||||
|
fn is_none(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
PathArguments::None => true,
|
||||||
|
PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// An individual generic argument, like `'a`, `T`, or `Item = T`.
|
||||||
|
pub enum GenericArgument {
|
||||||
|
/// A lifetime argument.
|
||||||
|
Lifetime(Lifetime),
|
||||||
|
/// A type argument.
|
||||||
|
Type(Type),
|
||||||
|
/// A binding (equality constraint) on an associated type: the `Item =
|
||||||
|
/// u8` in `Iterator<Item = u8>`.
|
||||||
|
Binding(Binding),
|
||||||
|
/// An associated type bound: `Iterator<Item: Display>`.
|
||||||
|
Constraint(Constraint),
|
||||||
|
/// A const expression. Must be inside of a block.
|
||||||
|
///
|
||||||
|
/// NOTE: Identity expressions are represented as Type arguments, as
|
||||||
|
/// they are indistinguishable syntactically.
|
||||||
|
Const(Expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
|
||||||
|
/// V>`.
|
||||||
|
pub struct AngleBracketedGenericArguments {
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) colon2_token: bool,
|
||||||
|
pub(crate) args: Punctuated<GenericArgument>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A binding (equality constraint) on an associated type: `Item = u8`.
|
||||||
|
pub struct Binding {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An associated type bound: `Iterator<Item: Display>`.
|
||||||
|
pub struct Constraint {
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
|
||||||
|
/// C`.
|
||||||
|
pub struct ParenthesizedGenericArguments {
|
||||||
|
/// `(A, B)`
|
||||||
|
pub(crate) inputs: Punctuated<Type>,
|
||||||
|
/// `C`
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) output: ReturnType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The explicit Self type in a qualified path: the `T` in `<T as
|
||||||
|
/// Display>::fmt`.
|
||||||
|
///
|
||||||
|
/// The actual path, including the trait and the associated item, is stored
|
||||||
|
/// separately. The `position` field represents the index of the associated
|
||||||
|
/// item qualified with this Self type.
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// <Vec<T> as a::b::Trait>::AssociatedItem
|
||||||
|
/// ^~~~~~ ~~~~~~~~~~~~~~^
|
||||||
|
/// ty position = 3
|
||||||
|
///
|
||||||
|
/// <Vec<T>>::AssociatedItem
|
||||||
|
/// ^~~~~~ ^
|
||||||
|
/// ty position = 0
|
||||||
|
/// ```
|
||||||
|
pub struct QSelf {
|
||||||
|
pub(crate) ty: Box<Type>,
|
||||||
|
pub(crate) position: usize,
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) as_token: bool,
|
||||||
|
}
|
||||||
|
}
|
39
syn-serde/src/stmt.rs
Normal file
39
syn-serde/src/stmt.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A braced block containing Rust statements.
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Block {
|
||||||
|
/// Statements in a block
|
||||||
|
pub(crate) stmts: Vec<Stmt>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A statement, usually ending in a semicolon.
|
||||||
|
pub enum Stmt {
|
||||||
|
/// A local (let) binding.
|
||||||
|
#[serde(rename = "let")]
|
||||||
|
Local(Local),
|
||||||
|
|
||||||
|
/// An item definition.
|
||||||
|
Item(Item),
|
||||||
|
|
||||||
|
/// Expr without trailing semicolon.
|
||||||
|
Expr(Expr),
|
||||||
|
|
||||||
|
/// Expression with trailing semicolon.
|
||||||
|
Semi(Expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A local `let` binding: `let x: u64 = s.parse()?`.
|
||||||
|
pub struct Local {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) pat: Pat,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) init: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
}
|
345
syn-serde/src/token_stream.rs
Normal file
345
syn-serde/src/token_stream.rs
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
use super::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An abstract stream of tokens, or more concretely a sequence of token trees.
|
||||||
|
///
|
||||||
|
/// This type provides interfaces for iterating over token trees and for
|
||||||
|
/// collecting token trees into one stream.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct TokenStream {
|
||||||
|
inner: Vec<TokenTree>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenStream {
|
||||||
|
fn _new(inner: Vec<TokenTree>) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
|
self.inner.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum TokenTree {
|
||||||
|
/// A token stream surrounded by bracket delimiters.
|
||||||
|
Group(Group),
|
||||||
|
/// An identifier.
|
||||||
|
Ident(Ident),
|
||||||
|
/// A single punctuation character (`+`, `,`, `$`, etc.).
|
||||||
|
Punct(Punct),
|
||||||
|
/// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc.
|
||||||
|
#[serde(rename = "lit")]
|
||||||
|
Literal(Literal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A delimited token stream.
|
||||||
|
///
|
||||||
|
/// A `Group` internally contains a `TokenStream` which is surrounded by
|
||||||
|
/// `Delimiter`s.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Group {
|
||||||
|
delimiter: Delimiter,
|
||||||
|
stream: TokenStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Describes how a sequence of token trees is delimited.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Delimiter {
|
||||||
|
/// `( ... )`
|
||||||
|
Parenthesis,
|
||||||
|
/// `{ ... }`
|
||||||
|
Brace,
|
||||||
|
/// `[ ... ]`
|
||||||
|
Bracket,
|
||||||
|
/// `Ø ... Ø`
|
||||||
|
///
|
||||||
|
/// An implicit delimiter, that may, for example, appear around tokens
|
||||||
|
/// coming from a "macro variable" `$var`. It is important to preserve
|
||||||
|
/// operator priorities in cases like `$var * 3` where `$var` is `1 + 2`.
|
||||||
|
/// Implicit delimiters may not survive roundtrip of a token stream through
|
||||||
|
/// a string.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An `Punct` is an single punctuation character like `+`, `-` or `#`.
|
||||||
|
///
|
||||||
|
/// Multicharacter operators like `+=` are represented as two instances of
|
||||||
|
/// `Punct` with different forms of `Spacing` returned.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Punct {
|
||||||
|
op: char,
|
||||||
|
spacing: Spacing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// Whether an `Punct` is followed immediately by another `Punct` or followed by
|
||||||
|
/// another token or whitespace.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Spacing {
|
||||||
|
/// E.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
|
||||||
|
Alone,
|
||||||
|
/// E.g. `+` is `Joint` in `+=` or `'#`.
|
||||||
|
///
|
||||||
|
/// Additionally, single quote `'` can join with identifiers to form
|
||||||
|
/// lifetimes `'ident`.
|
||||||
|
Joint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A word of Rust code, which may be a keyword or legal variable name.
|
||||||
|
///
|
||||||
|
/// An identifier consists of at least one Unicode code point, the first of
|
||||||
|
/// which has the XID_Start property and the rest of which have the XID_Continue
|
||||||
|
/// property.
|
||||||
|
///
|
||||||
|
/// - The empty string is not an identifier. Use `Option<Ident>`.
|
||||||
|
/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Ident {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A literal string (`"hello"`), byte string (`b"hello"`), character (`'a'`),
|
||||||
|
/// byte character (`b'a'`), an integer or floating point number with or without
|
||||||
|
/// a suffix (`1`, `1u8`, `2.3`, `2.3f32`).
|
||||||
|
///
|
||||||
|
/// Boolean literals like `true` and `false` do not belong here, they are
|
||||||
|
/// `Ident`s.
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Literal {
|
||||||
|
pub(crate) text: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
fn _new(text: String) -> Self {
|
||||||
|
Self { text }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn u8_suffixed(n: u8) -> Self {
|
||||||
|
Self::_new(format!(concat!("{}", stringify!(u8)), n))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn string(t: &str) -> Self {
|
||||||
|
let mut s = t.chars().flat_map(char::escape_default).collect::<String>();
|
||||||
|
s.push('"');
|
||||||
|
s.insert(0, '"');
|
||||||
|
Self::_new(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn character(t: char) -> Self {
|
||||||
|
Self::_new(format!("'{}'", t.escape_default().collect::<String>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::match_overlapping_arm)]
|
||||||
|
pub(crate) fn byte_string(bytes: &[u8]) -> Self {
|
||||||
|
let mut escaped = "b\"".to_string();
|
||||||
|
for b in bytes {
|
||||||
|
match *b {
|
||||||
|
b'\0' => escaped.push_str(r"\0"),
|
||||||
|
b'\t' => escaped.push_str(r"\t"),
|
||||||
|
b'\n' => escaped.push_str(r"\n"),
|
||||||
|
b'\r' => escaped.push_str(r"\r"),
|
||||||
|
b'"' => escaped.push_str("\\\""),
|
||||||
|
b'\\' => escaped.push_str("\\\\"),
|
||||||
|
b'\x20'..=b'\x7E' => escaped.push(*b as char),
|
||||||
|
_ => escaped.push_str(&format!("\\x{:02X}", b)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
escaped.push('"');
|
||||||
|
Self::_new(escaped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: when release the next minor version, remove this.
|
||||||
|
impl fmt::Display for Literal {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.text.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// TokenStream
|
||||||
|
syn_trait_impl!(proc_macro2::TokenStream);
|
||||||
|
impl From<&proc_macro2::TokenStream> for TokenStream {
|
||||||
|
fn from(other: &proc_macro2::TokenStream) -> Self {
|
||||||
|
Self::_new(
|
||||||
|
other
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map::<TokenTree, _>(|x| x.ref_into())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&TokenStream> for proc_macro2::TokenStream {
|
||||||
|
fn from(other: &TokenStream) -> Self {
|
||||||
|
other
|
||||||
|
.inner
|
||||||
|
.iter()
|
||||||
|
.map::<proc_macro2::TokenTree, _>(Into::into)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenTree
|
||||||
|
syn_trait_impl!(proc_macro2::TokenTree);
|
||||||
|
impl From<&proc_macro2::TokenTree> for TokenTree {
|
||||||
|
fn from(other: &proc_macro2::TokenTree) -> Self {
|
||||||
|
use super::TokenTree::*;
|
||||||
|
use proc_macro2::TokenTree;
|
||||||
|
match other {
|
||||||
|
TokenTree::Group(t) => Group(t.into()),
|
||||||
|
TokenTree::Ident(t) => Ident(t.into()),
|
||||||
|
TokenTree::Punct(t) => Punct(t.into()),
|
||||||
|
TokenTree::Literal(t) => Literal(t.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&TokenTree> for proc_macro2::TokenTree {
|
||||||
|
fn from(other: &TokenTree) -> Self {
|
||||||
|
use proc_macro2::TokenTree::*;
|
||||||
|
match other {
|
||||||
|
TokenTree::Group(t) => Group(t.into()),
|
||||||
|
TokenTree::Ident(t) => Ident(t.into()),
|
||||||
|
TokenTree::Punct(t) => Punct(t.into()),
|
||||||
|
TokenTree::Literal(t) => Literal(t.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group
|
||||||
|
syn_trait_impl!(proc_macro2::Group);
|
||||||
|
impl From<&proc_macro2::Group> for Group {
|
||||||
|
fn from(other: &proc_macro2::Group) -> Self {
|
||||||
|
Self {
|
||||||
|
delimiter: other.delimiter().ref_into(),
|
||||||
|
stream: other.stream().ref_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Group> for proc_macro2::Group {
|
||||||
|
fn from(other: &Group) -> Self {
|
||||||
|
Self::new(other.delimiter.ref_into(), other.stream.ref_into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delimiter
|
||||||
|
syn_trait_impl!(proc_macro2::Delimiter);
|
||||||
|
impl From<&proc_macro2::Delimiter> for Delimiter {
|
||||||
|
fn from(other: &proc_macro2::Delimiter) -> Self {
|
||||||
|
use super::Delimiter::*;
|
||||||
|
use proc_macro2::Delimiter;
|
||||||
|
match other {
|
||||||
|
Delimiter::Parenthesis => Parenthesis,
|
||||||
|
Delimiter::Brace => Brace,
|
||||||
|
Delimiter::Bracket => Bracket,
|
||||||
|
Delimiter::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Delimiter> for proc_macro2::Delimiter {
|
||||||
|
fn from(other: &Delimiter) -> Self {
|
||||||
|
use proc_macro2::Delimiter::*;
|
||||||
|
match other {
|
||||||
|
Delimiter::Parenthesis => Parenthesis,
|
||||||
|
Delimiter::Brace => Brace,
|
||||||
|
Delimiter::Bracket => Bracket,
|
||||||
|
Delimiter::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ident
|
||||||
|
syn_trait_impl!(proc_macro2::Ident);
|
||||||
|
impl From<&proc_macro2::Ident> for Ident {
|
||||||
|
fn from(other: &proc_macro2::Ident) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: other.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Ident> for proc_macro2::Ident {
|
||||||
|
fn from(other: &Ident) -> Self {
|
||||||
|
Self::new(&other.inner, Span::call_site())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Punct
|
||||||
|
syn_trait_impl!(proc_macro2::Punct);
|
||||||
|
impl From<&proc_macro2::Punct> for Punct {
|
||||||
|
fn from(other: &proc_macro2::Punct) -> Self {
|
||||||
|
Self {
|
||||||
|
op: other.as_char(),
|
||||||
|
spacing: other.spacing().ref_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Punct> for proc_macro2::Punct {
|
||||||
|
fn from(other: &Punct) -> Self {
|
||||||
|
Self::new(other.op, other.spacing.ref_into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacing
|
||||||
|
syn_trait_impl!(proc_macro2::Spacing);
|
||||||
|
impl From<&proc_macro2::Spacing> for Spacing {
|
||||||
|
fn from(other: &proc_macro2::Spacing) -> Self {
|
||||||
|
use super::Spacing::*;
|
||||||
|
use proc_macro2::Spacing;
|
||||||
|
match other {
|
||||||
|
Spacing::Alone => Alone,
|
||||||
|
Spacing::Joint => Joint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Spacing> for proc_macro2::Spacing {
|
||||||
|
fn from(other: &Spacing) -> Self {
|
||||||
|
use proc_macro2::Spacing::*;
|
||||||
|
match other {
|
||||||
|
Spacing::Alone => Alone,
|
||||||
|
Spacing::Joint => Joint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
syn_trait_impl!(proc_macro2::Literal);
|
||||||
|
impl From<&proc_macro2::Literal> for Literal {
|
||||||
|
fn from(other: &proc_macro2::Literal) -> Self {
|
||||||
|
Self {
|
||||||
|
text: other.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Literal> for proc_macro2::Literal {
|
||||||
|
fn from(other: &Literal) -> Self {
|
||||||
|
use proc_macro2::*;
|
||||||
|
let stream = other.text.parse::<TokenStream>().unwrap();
|
||||||
|
match stream.into_iter().next().unwrap() {
|
||||||
|
TokenTree::Literal(l) => l,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
291
syn-serde/src/ty.rs
Normal file
291
syn-serde/src/ty.rs
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
ast_enum! {
|
||||||
|
/// The possible types that a Rust value could have.
|
||||||
|
pub enum Type {
|
||||||
|
/// A fixed size array type: `[T; n]`.
|
||||||
|
Array(TypeArray),
|
||||||
|
|
||||||
|
/// A bare function type: `fn(usize) -> bool`.
|
||||||
|
BareFn(TypeBareFn),
|
||||||
|
|
||||||
|
/// A type contained within invisible delimiters.
|
||||||
|
Group(TypeGroup),
|
||||||
|
|
||||||
|
/// An `impl Bound1 + Bound2 + Bound3` type where `Bound` is a trait or
|
||||||
|
/// a lifetime.
|
||||||
|
ImplTrait(TypeImplTrait),
|
||||||
|
|
||||||
|
/// Indication that a type should be inferred by the compiler: `_`.
|
||||||
|
#[serde(rename = "_")]
|
||||||
|
Infer,
|
||||||
|
|
||||||
|
/// A macro in the type position.
|
||||||
|
Macro(TypeMacro),
|
||||||
|
|
||||||
|
/// The never type: `!`.
|
||||||
|
#[serde(rename = "!")]
|
||||||
|
Never,
|
||||||
|
|
||||||
|
/// A parenthesized type equivalent to the inner type.
|
||||||
|
Paren(TypeParen),
|
||||||
|
|
||||||
|
/// A path like `std::slice::Iter`, optionally qualified with a
|
||||||
|
/// self-type as in `<Vec<T> as SomeTrait>::Associated`.
|
||||||
|
Path(TypePath),
|
||||||
|
|
||||||
|
/// A raw pointer type: `*const T` or `*mut T`.
|
||||||
|
Ptr(TypePtr),
|
||||||
|
|
||||||
|
/// A reference type: `&'a T` or `&'a mut T`.
|
||||||
|
Reference(TypeReference),
|
||||||
|
|
||||||
|
/// A dynamically sized slice type: `[T]`.
|
||||||
|
Slice(TypeSlice),
|
||||||
|
|
||||||
|
/// A trait object type `Bound1 + Bound2 + Bound3` where `Bound` is a
|
||||||
|
/// trait or a lifetime.
|
||||||
|
TraitObject(TypeTraitObject),
|
||||||
|
|
||||||
|
/// A tuple type: `(A, B, C, String)`.
|
||||||
|
Tuple(TypeTuple),
|
||||||
|
|
||||||
|
/// Tokens in type position not interpreted by Syn.
|
||||||
|
Verbatim(TokenStream),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Nonexhaustive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A fixed size array type: `[T; n]`.
|
||||||
|
pub struct TypeArray {
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
pub(crate) len: Expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A bare function type: `fn(usize) -> bool`.
|
||||||
|
pub struct TypeBareFn {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) lifetimes: Option<BoundLifetimes>,
|
||||||
|
#[serde(rename = "unsafe")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) unsafety: bool,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) abi: Option<Abi>,
|
||||||
|
pub(crate) inputs: Punctuated<BareFnArg>,
|
||||||
|
pub(crate) variadic: Option<Variadic>,
|
||||||
|
pub(crate) output: ReturnType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A type contained within invisible delimiters.
|
||||||
|
pub struct TypeGroup {
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An `impl Bound1 + Bound2 + Bound3` type where `Bound` is a trait or
|
||||||
|
/// a lifetime.
|
||||||
|
pub struct TypeImplTrait {
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A macro in the type position.
|
||||||
|
pub struct TypeMacro {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) mac: Macro,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A parenthesized type equivalent to the inner type.
|
||||||
|
pub struct TypeParen {
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A path like `std::slice::Iter`, optionally qualified with a
|
||||||
|
/// self-type as in `<Vec<T> as SomeTrait>::Associated`.
|
||||||
|
pub struct TypePath {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) qself: Option<QSelf>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) path: Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A raw pointer type: `*const T` or `*mut T`.
|
||||||
|
pub struct TypePtr {
|
||||||
|
#[serde(rename = "const")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) const_token: bool,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A reference type: `&'a T` or `&'a mut T`.
|
||||||
|
pub struct TypeReference {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) lifetime: Option<Lifetime>,
|
||||||
|
#[serde(rename = "mut")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) mutability: bool,
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A dynamically sized slice type: `[T]`.
|
||||||
|
pub struct TypeSlice {
|
||||||
|
pub(crate) elem: Box<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A trait object type `Bound1 + Bound2 + Bound3` where `Bound` is a
|
||||||
|
/// trait or a lifetime.
|
||||||
|
pub struct TypeTraitObject {
|
||||||
|
#[serde(rename = "dyn")]
|
||||||
|
#[serde(default, skip_serializing_if = "not")]
|
||||||
|
pub(crate) dyn_token: bool,
|
||||||
|
pub(crate) bounds: Punctuated<TypeParamBound>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// A tuple type: `(A, B, C, String)`.
|
||||||
|
pub struct TypeTuple {
|
||||||
|
pub(crate) elems: Punctuated<Type>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The binary interface of a function: `extern "C"`.
|
||||||
|
pub struct Abi {
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) name: Option<LitStr>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// An argument in a function type: the `usize` in `fn(usize) -> bool`.
|
||||||
|
pub struct BareFnArg {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub(crate) name: Option<Ident>,
|
||||||
|
pub(crate) ty: Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// The variadic argument of a foreign function.
|
||||||
|
pub struct Variadic {
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_struct! {
|
||||||
|
/// Return type of a function signature.
|
||||||
|
#[derive(Default)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct ReturnType {
|
||||||
|
ty: Option<Box<Type>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod convert {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Type
|
||||||
|
syn_trait_impl!(syn::Type);
|
||||||
|
impl From<&syn::Type> for Type {
|
||||||
|
fn from(other: &syn::Type) -> Self {
|
||||||
|
use super::Type::*;
|
||||||
|
use syn::Type;
|
||||||
|
match other {
|
||||||
|
Type::Slice(x) => Slice(x.into()),
|
||||||
|
Type::Array(x) => Array(x.into()),
|
||||||
|
Type::Ptr(x) => Ptr(x.into()),
|
||||||
|
Type::Reference(x) => Reference(x.into()),
|
||||||
|
Type::BareFn(x) => BareFn(x.into()),
|
||||||
|
Type::Never(_) => Never,
|
||||||
|
Type::Tuple(x) => Tuple(x.into()),
|
||||||
|
Type::Path(x) => Path(x.into()),
|
||||||
|
Type::TraitObject(x) => TraitObject(x.into()),
|
||||||
|
Type::ImplTrait(x) => ImplTrait(x.into()),
|
||||||
|
Type::Paren(x) => Paren(x.into()),
|
||||||
|
Type::Group(x) => Group(x.into()),
|
||||||
|
Type::Infer(_) => Infer,
|
||||||
|
Type::Macro(x) => Macro(x.into()),
|
||||||
|
Type::Verbatim(x) => Verbatim(x.into()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&Type> for syn::Type {
|
||||||
|
fn from(other: &Type) -> Self {
|
||||||
|
use syn::Type::*;
|
||||||
|
match other {
|
||||||
|
Type::Slice(x) => Slice(x.into()),
|
||||||
|
Type::Array(x) => Array(x.into()),
|
||||||
|
Type::Ptr(x) => Ptr(x.into()),
|
||||||
|
Type::Reference(x) => Reference(x.into()),
|
||||||
|
Type::BareFn(x) => BareFn(x.into()),
|
||||||
|
Type::Never => Never(syn::TypeNever {
|
||||||
|
bang_token: default(),
|
||||||
|
}),
|
||||||
|
Type::Tuple(x) => Tuple(x.into()),
|
||||||
|
Type::Path(x) => Path(x.into()),
|
||||||
|
Type::TraitObject(x) => TraitObject(x.into()),
|
||||||
|
Type::ImplTrait(x) => ImplTrait(x.into()),
|
||||||
|
Type::Paren(x) => Paren(x.into()),
|
||||||
|
Type::Group(x) => Group(x.into()),
|
||||||
|
Type::Infer => Infer(syn::TypeInfer {
|
||||||
|
underscore_token: default(),
|
||||||
|
}),
|
||||||
|
Type::Macro(x) => Macro(x.into()),
|
||||||
|
Type::Verbatim(x) => Verbatim(x.into()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnType
|
||||||
|
syn_trait_impl!(syn::ReturnType);
|
||||||
|
impl From<&syn::ReturnType> for ReturnType {
|
||||||
|
fn from(other: &syn::ReturnType) -> Self {
|
||||||
|
use syn::ReturnType;
|
||||||
|
match other {
|
||||||
|
ReturnType::Default => Self { ty: None },
|
||||||
|
ReturnType::Type(_, x) => Self {
|
||||||
|
ty: Some(x.map_into()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&ReturnType> for syn::ReturnType {
|
||||||
|
fn from(other: &ReturnType) -> Self {
|
||||||
|
use syn::ReturnType;
|
||||||
|
match &other.ty {
|
||||||
|
None => ReturnType::Default,
|
||||||
|
Some(x) => ReturnType::Type(default(), x.map_into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5616
syn-serde/syn.json
Normal file
5616
syn-serde/syn.json
Normal file
File diff suppressed because it is too large
Load diff
740
syn-serde/tests/test_item.rs
Normal file
740
syn-serde/tests/test_item.rs
Normal file
|
@ -0,0 +1,740 @@
|
||||||
|
use syn::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unit() {
|
||||||
|
let raw = "struct Unit;";
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"ident": "Unit",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let ser: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let ser = Item::from(&ser);
|
||||||
|
assert_eq!(ser, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_struct() {
|
||||||
|
let raw = "
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Item {
|
||||||
|
pub ident: Ident,
|
||||||
|
pub attrs: Vec<Attribute>
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "derive"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "parenthesis",
|
||||||
|
"stream": [
|
||||||
|
{
|
||||||
|
"ident": "Debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": ",",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Clone"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vis": "pub",
|
||||||
|
"ident": "Item",
|
||||||
|
"fields": {
|
||||||
|
"named": [
|
||||||
|
{
|
||||||
|
"vis": "pub",
|
||||||
|
"ident": "ident",
|
||||||
|
"colon_token": true,
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Ident"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vis": "pub",
|
||||||
|
"ident": "attrs",
|
||||||
|
"colon_token": true,
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Vec",
|
||||||
|
"arguments": {
|
||||||
|
"angle_bracketed": {
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "Attribute"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_union() {
|
||||||
|
let raw = "
|
||||||
|
union MaybeUninit<T> {
|
||||||
|
uninit: (),
|
||||||
|
value: T
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"union": {
|
||||||
|
"ident": "MaybeUninit",
|
||||||
|
"generics": {
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"ident": "uninit",
|
||||||
|
"colon_token": true,
|
||||||
|
"ty": {
|
||||||
|
"tuple": {
|
||||||
|
"elems": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "value",
|
||||||
|
"colon_token": true,
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum() {
|
||||||
|
let raw = r#"
|
||||||
|
/// See the std::result module documentation for details.
|
||||||
|
#[must_use]
|
||||||
|
pub enum Result<T, E> {
|
||||||
|
Ok(T),
|
||||||
|
Err(E),
|
||||||
|
Surprise = 0isize,
|
||||||
|
|
||||||
|
// Smuggling data into a proc_macro_derive,
|
||||||
|
// in the style of https://github.com/dtolnay/proc-macro-hack
|
||||||
|
ProcMacroHack = (0, "data").0
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"enum": {
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "doc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": "=",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lit": "\" See the std::result module documentation for details.\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "must_use"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vis": "pub",
|
||||||
|
"ident": "Result",
|
||||||
|
"generics": {
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"ident": "E"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"variants": [
|
||||||
|
{
|
||||||
|
"ident": "Ok",
|
||||||
|
"fields": {
|
||||||
|
"unnamed": [
|
||||||
|
{
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "T"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Err",
|
||||||
|
"fields": {
|
||||||
|
"unnamed": [
|
||||||
|
{
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "E"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "Surprise",
|
||||||
|
"fields": "unit",
|
||||||
|
"discriminant": {
|
||||||
|
"lit": {
|
||||||
|
"int": "0isize"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "ProcMacroHack",
|
||||||
|
"fields": "unit",
|
||||||
|
"discriminant": {
|
||||||
|
"field": {
|
||||||
|
"base": {
|
||||||
|
"tuple": {
|
||||||
|
"elems": [
|
||||||
|
{
|
||||||
|
"lit": {
|
||||||
|
"int": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lit": {
|
||||||
|
"str": "\"data\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_attr_with_path() {
|
||||||
|
let raw = r#"
|
||||||
|
#[::attr_args::identity
|
||||||
|
fn main() { assert_eq!(foo(), "Hello, world!"); }]
|
||||||
|
struct Dummy;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"leading_colon": true,
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "attr_args"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "identity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"ident": "fn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "parenthesis",
|
||||||
|
"stream": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "brace",
|
||||||
|
"stream": [
|
||||||
|
{
|
||||||
|
"ident": "assert_eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": "!",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "parenthesis",
|
||||||
|
"stream": [
|
||||||
|
{
|
||||||
|
"ident": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": {
|
||||||
|
"delimiter": "parenthesis",
|
||||||
|
"stream": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": ",",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lit": "\"Hello, world!\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": ";",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ident": "Dummy",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_attr_with_non_mod_style_path() {
|
||||||
|
let raw = r#"
|
||||||
|
#[inert <T>]
|
||||||
|
struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "inert"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": "<",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "T"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"punct": {
|
||||||
|
"op": ">",
|
||||||
|
"spacing": "alone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_attr_with_mod_style_path_with_self() {
|
||||||
|
let raw = r#"
|
||||||
|
#[foo::self]
|
||||||
|
struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"style": "outer",
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "self"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_restricted() {
|
||||||
|
// Taken from tests/rust/src/test/ui/resolve/auxiliary/privacy-struct-ctor.rs
|
||||||
|
let raw = r#"
|
||||||
|
pub(in m) struct Z(pub(in m::n) u8);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"vis": {
|
||||||
|
"restricted": {
|
||||||
|
"in_token": true,
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "m"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ident": "Z",
|
||||||
|
"fields": {
|
||||||
|
"unnamed": [
|
||||||
|
{
|
||||||
|
"vis": {
|
||||||
|
"restricted": {
|
||||||
|
"in_token": true,
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "u8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vis_crate() {
|
||||||
|
let raw = r#"
|
||||||
|
crate struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"vis": "crate",
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_restricted_crate() {
|
||||||
|
let raw = r#"
|
||||||
|
pub(crate) struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"vis": {
|
||||||
|
"restricted": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "crate"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_restricted_super() {
|
||||||
|
let raw = r#"
|
||||||
|
pub(super) struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"vis": {
|
||||||
|
"restricted": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "super"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_restricted_in_super() {
|
||||||
|
let raw = r#"
|
||||||
|
pub(in super) struct S;
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"vis": {
|
||||||
|
"restricted": {
|
||||||
|
"in_token": true,
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "super"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ident": "S",
|
||||||
|
"fields": "unit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ambiguous_crate() {
|
||||||
|
// The field type is `(crate::X)` not `crate (::X)`.
|
||||||
|
let raw = "struct S(crate::X);";
|
||||||
|
|
||||||
|
let json = r#"
|
||||||
|
{
|
||||||
|
"struct": {
|
||||||
|
"ident": "S",
|
||||||
|
"fields": {
|
||||||
|
"unnamed": [
|
||||||
|
{
|
||||||
|
"ty": {
|
||||||
|
"path": {
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"ident": "crate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ident": "X"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let actual = syn::parse_str(raw).unwrap();
|
||||||
|
let json: syn_serde::Item = serde_json::from_str(json).unwrap();
|
||||||
|
let json = Item::from(&json);
|
||||||
|
assert_eq!(json, actual);
|
||||||
|
}
|
Loading…
Reference in a new issue