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