├── .github └── workflows │ ├── buffer.yml │ ├── derive.yml │ ├── dynamic.yml │ ├── flatten.yml │ ├── fmt.yml │ ├── json.yml │ ├── nested.yml │ ├── ref.yml │ ├── serde.yml │ ├── sval.yml │ └── test.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── buffer ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── value.rs └── src │ ├── error.rs │ ├── fragments.rs │ ├── lib.rs │ └── value.rs ├── derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── test │ ├── Cargo.toml │ ├── compile_fail │ ├── enum_dynamic_unindexed_variants.rs │ ├── enum_dynamic_unindexed_variants.stderr │ ├── enum_dynamic_unlabeled_variants.rs │ ├── enum_dynamic_unlabeled_variants.stderr │ ├── enum_unindexed_fields.rs │ ├── enum_unindexed_fields.stderr │ ├── enum_unlabeled_fields.rs │ ├── enum_unlabeled_fields.stderr │ ├── newtype_data_tag.rs │ ├── newtype_data_tag.stderr │ ├── newtype_index.rs │ ├── newtype_index.stderr │ ├── newtype_label.rs │ ├── newtype_label.stderr │ ├── newtype_tag.rs │ ├── newtype_tag.stderr │ ├── record_as_seq_incomplete_indexes.rs │ ├── record_as_seq_incomplete_indexes.stderr │ ├── record_as_seq_incomplete_labels.rs │ ├── record_as_seq_incomplete_labels.stderr │ ├── record_as_tuple_incomplete_labels.rs │ ├── record_as_tuple_incomplete_labels.stderr │ ├── tuple_incomplete_labels.rs │ ├── tuple_incomplete_labels.stderr │ ├── tuple_transparent.rs │ ├── tuple_transparent.stderr │ ├── union.rs │ └── union.stderr │ └── lib.rs ├── derive_macros ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── attr.rs │ ├── bound.rs │ ├── derive.rs │ ├── derive │ ├── derive_enum.rs │ ├── derive_newtype.rs │ ├── derive_struct.rs │ ├── derive_unit_struct.rs │ └── derive_void.rs │ ├── index.rs │ ├── label.rs │ ├── lib.rs │ ├── stream.rs │ ├── stream │ ├── stream_newtype.rs │ ├── stream_record_tuple.rs │ └── stream_tag.rs │ └── tag.rs ├── dynamic ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── lib.rs │ ├── stream.rs │ └── value.rs ├── examples ├── stream │ ├── mod.rs │ └── simple.rs ├── stream_simple.rs ├── tags.rs └── value_derive.rs ├── experiments ├── Cargo.toml └── src │ ├── json.rs │ ├── json │ └── from_slice.rs │ └── lib.rs ├── flatten ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bench │ ├── Cargo.toml │ └── lib.rs └── src │ ├── flattener.rs │ ├── index.rs │ ├── label.rs │ ├── lib.rs │ ├── map.rs │ ├── record.rs │ ├── record_tuple.rs │ ├── seq.rs │ └── tuple.rs ├── fmt ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ ├── lib.rs │ ├── tags.rs │ ├── to_fmt.rs │ ├── to_string.rs │ ├── to_value.rs │ ├── to_write.rs │ ├── token_write.rs │ └── writer.rs └── test │ ├── Cargo.toml │ └── lib.rs ├── json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bench │ ├── Cargo.toml │ ├── lib.rs │ └── twitter.json ├── src │ ├── error.rs │ ├── lib.rs │ ├── tags.rs │ ├── to_fmt.rs │ ├── to_io.rs │ ├── to_string.rs │ ├── to_value.rs │ ├── to_vec.rs │ └── value.rs └── test │ ├── Cargo.toml │ └── lib.rs ├── nested ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples │ └── stream_nested_simple.rs └── src │ ├── error.rs │ ├── flat.rs │ ├── flat_enum.rs │ └── lib.rs ├── ref ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── lib.rs │ └── seq.rs ├── serde ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ ├── lib.rs │ ├── to_serialize.rs │ └── to_value.rs └── test │ ├── Cargo.toml │ └── lib.rs ├── src ├── data.rs ├── data │ ├── binary.rs │ ├── map.rs │ ├── number.rs │ ├── option.rs │ ├── seq.rs │ ├── tags.rs │ └── text.rs ├── lib.rs ├── result.rs ├── stream.rs └── value.rs └── test ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src └── lib.rs /.github/workflows/buffer.yml: -------------------------------------------------------------------------------- 1 | name: buffer 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./buffer 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./buffer 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | bench: 31 | name: Bench 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 36 | 37 | - name: Install Rust toolchain 38 | run: rustup default nightly 39 | 40 | - name: Install cargo-hack 41 | run: cargo install cargo-hack 42 | 43 | - name: Powerset 44 | working-directory: ./buffer 45 | run: cargo hack bench --feature-powerset --no-run 46 | 47 | msrv: 48 | name: Build (MSRV) 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Checkout sources 52 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 53 | 54 | - name: Install Rust toolchain 55 | run: | 56 | rustup toolchain add 1.61.0 --profile minimal 57 | rustup default 1.61.0 58 | 59 | - name: Check 60 | working-directory: ./buffer 61 | run: cargo check --all-features 62 | 63 | embedded: 64 | name: Build (embedded) 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: Checkout sources 68 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 69 | 70 | - name: Install Rust toolchain 71 | run: | 72 | rustup default nightly 73 | rustup target add thumbv6m-none-eabi 74 | 75 | - name: Install cargo-hack 76 | run: cargo install cargo-hack 77 | 78 | - name: Powerset 79 | working-directory: ./buffer 80 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 81 | 82 | miri: 83 | name: Test (Miri) 84 | runs-on: ubuntu-latest 85 | steps: 86 | - name: Checkout sources 87 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 88 | 89 | - name: Install Miri 90 | run: | 91 | rustup toolchain install nightly --component miri 92 | cargo +nightly miri setup 93 | 94 | - name: Default features 95 | working-directory: ./buffer 96 | run: cargo +nightly miri test --lib 97 | 98 | - name: No features 99 | working-directory: ./buffer 100 | run: cargo +nightly miri test --lib --no-default-features 101 | -------------------------------------------------------------------------------- /.github/workflows/derive.yml: -------------------------------------------------------------------------------- 1 | name: derive 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./derive 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./derive 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | - name: Powerset (Tests) 31 | working-directory: ./derive/test 32 | run: cargo hack test --feature-powerset 33 | -------------------------------------------------------------------------------- /.github/workflows/dynamic.yml: -------------------------------------------------------------------------------- 1 | name: dynamic 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./dynamic 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./dynamic 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | msrv: 31 | name: Build (MSRV) 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 36 | 37 | - name: Install Rust toolchain 38 | run: | 39 | rustup toolchain add 1.61.0 --profile minimal 40 | rustup default 1.61.0 41 | 42 | - name: Check 43 | working-directory: ./dynamic 44 | run: cargo check --all-features 45 | 46 | embedded: 47 | name: Build (embedded) 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Checkout sources 51 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 52 | 53 | - name: Install Rust toolchain 54 | run: | 55 | rustup default nightly 56 | rustup target add thumbv6m-none-eabi 57 | 58 | - name: Install cargo-hack 59 | run: cargo install cargo-hack 60 | 61 | - name: Powerset 62 | working-directory: ./fmt 63 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 64 | -------------------------------------------------------------------------------- /.github/workflows/flatten.yml: -------------------------------------------------------------------------------- 1 | name: flatten 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./flatten 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Powerset (Benches) 27 | working-directory: ./flatten/bench 28 | run: cargo hack test --feature-powerset 29 | 30 | - name: Minimal Versions 31 | working-directory: ./flatten 32 | run: cargo hack test --feature-powerset -Z minimal-versions 33 | 34 | msrv: 35 | name: Build (MSRV) 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 40 | 41 | - name: Install Rust toolchain 42 | run: | 43 | rustup toolchain add 1.61.0 --profile minimal 44 | rustup default 1.61.0 45 | 46 | - name: Check 47 | working-directory: ./flatten 48 | run: cargo check --all-features 49 | 50 | embedded: 51 | name: Build (embedded) 52 | runs-on: ubuntu-latest 53 | steps: 54 | - name: Checkout sources 55 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 56 | 57 | - name: Install Rust toolchain 58 | run: | 59 | rustup default nightly 60 | rustup target add thumbv6m-none-eabi 61 | 62 | - name: Install cargo-hack 63 | run: cargo install cargo-hack 64 | 65 | - name: Powerset 66 | working-directory: ./flatten 67 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 68 | -------------------------------------------------------------------------------- /.github/workflows/fmt.yml: -------------------------------------------------------------------------------- 1 | name: fmt 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./fmt 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./fmt 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | - name: Powerset (Tests) 31 | working-directory: ./fmt/test 32 | run: cargo hack test --feature-powerset 33 | 34 | embedded: 35 | name: Build (embedded) 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 40 | 41 | - name: Install Rust toolchain 42 | run: | 43 | rustup default nightly 44 | rustup target add thumbv6m-none-eabi 45 | 46 | - name: Install cargo-hack 47 | run: cargo install cargo-hack 48 | 49 | - name: Powerset 50 | working-directory: ./fmt 51 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 52 | -------------------------------------------------------------------------------- /.github/workflows/json.yml: -------------------------------------------------------------------------------- 1 | name: json 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./json 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./json 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | - name: Powerset (Tests) 31 | working-directory: ./json/test 32 | run: cargo hack test --feature-powerset 33 | 34 | - name: Powerset (Benches) 35 | working-directory: ./json/bench 36 | run: cargo hack test --feature-powerset 37 | 38 | embedded: 39 | name: Build (embedded) 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: Checkout sources 43 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 44 | 45 | - name: Install Rust toolchain 46 | run: | 47 | rustup default nightly 48 | rustup target add thumbv6m-none-eabi 49 | 50 | - name: Install cargo-hack 51 | run: cargo install cargo-hack 52 | 53 | - name: Powerset 54 | working-directory: ./json 55 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 56 | -------------------------------------------------------------------------------- /.github/workflows/nested.yml: -------------------------------------------------------------------------------- 1 | name: nested 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./nested 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./nested 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | bench: 31 | name: Bench 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 36 | 37 | - name: Install Rust toolchain 38 | run: rustup default nightly 39 | 40 | - name: Install cargo-hack 41 | run: cargo install cargo-hack 42 | 43 | - name: Powerset 44 | working-directory: ./nested 45 | run: cargo hack bench --feature-powerset --no-run 46 | 47 | msrv: 48 | name: Build (MSRV) 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Checkout sources 52 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 53 | 54 | - name: Install Rust toolchain 55 | run: | 56 | rustup toolchain add 1.61.0 --profile minimal 57 | rustup default 1.61.0 58 | 59 | - name: Check 60 | working-directory: ./nested 61 | run: cargo check --all-features 62 | 63 | embedded: 64 | name: Build (embedded) 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: Checkout sources 68 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 69 | 70 | - name: Install Rust toolchain 71 | run: | 72 | rustup default nightly 73 | rustup target add thumbv6m-none-eabi 74 | 75 | - name: Install cargo-hack 76 | run: cargo install cargo-hack 77 | 78 | - name: Powerset 79 | working-directory: ./nested 80 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 81 | -------------------------------------------------------------------------------- /.github/workflows/ref.yml: -------------------------------------------------------------------------------- 1 | name: ref 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./ref 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./ref 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | msrv: 31 | name: Build (MSRV) 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 36 | 37 | - name: Install Rust toolchain 38 | run: | 39 | rustup toolchain add 1.61.0 --profile minimal 40 | rustup default 1.61.0 41 | 42 | - name: Check 43 | working-directory: ./ref 44 | run: cargo check --all-features 45 | 46 | embedded: 47 | name: Build (embedded) 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Checkout sources 51 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 52 | 53 | - name: Install Rust toolchain 54 | run: | 55 | rustup default nightly 56 | rustup target add thumbv6m-none-eabi 57 | 58 | - name: Install cargo-hack 59 | run: cargo install cargo-hack 60 | 61 | - name: Powerset 62 | working-directory: ./ref 63 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 64 | -------------------------------------------------------------------------------- /.github/workflows/serde.yml: -------------------------------------------------------------------------------- 1 | name: serde 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./serde 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./serde 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | - name: Powerset (Tests) 31 | working-directory: ./serde/test 32 | run: cargo hack test --feature-powerset 33 | 34 | msrv: 35 | name: Build (MSRV) 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 40 | 41 | - name: Install Rust toolchain 42 | run: | 43 | rustup toolchain add 1.61.0 --profile minimal 44 | rustup default 1.61.0 45 | 46 | - name: Check 47 | working-directory: ./serde 48 | run: cargo check --all-features 49 | 50 | embedded: 51 | name: Build (embedded) 52 | runs-on: ubuntu-latest 53 | steps: 54 | - name: Checkout sources 55 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 56 | 57 | - name: Install Rust toolchain 58 | run: | 59 | rustup default nightly 60 | rustup target add thumbv6m-none-eabi 61 | 62 | - name: Install cargo-hack 63 | run: cargo install cargo-hack 64 | 65 | - name: Powerset 66 | working-directory: ./serde 67 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 68 | -------------------------------------------------------------------------------- /.github/workflows/sval.yml: -------------------------------------------------------------------------------- 1 | name: sval 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - macos-latest 18 | - windows-latest 19 | steps: 20 | - name: Checkout sources 21 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 22 | 23 | - name: Install Rust toolchain 24 | run: rustup default nightly 25 | 26 | - name: Install cargo-hack 27 | run: cargo install cargo-hack 28 | 29 | - name: Powerset 30 | run: cargo hack test --feature-powerset 31 | 32 | - name: Minimal Versions 33 | run: cargo hack test --feature-powerset -Z minimal-versions 34 | 35 | - name: All 36 | run: cargo test --all 37 | 38 | embedded: 39 | name: Build (embedded) 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: Checkout sources 43 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 44 | 45 | - name: Install Rust toolchain 46 | run: | 47 | rustup default nightly 48 | rustup target add thumbv6m-none-eabi 49 | 50 | - name: Install cargo-hack 51 | run: cargo install cargo-hack 52 | 53 | - name: Powerset 54 | run: cargo hack check --each-feature --exclude-features std,alloc -Z avoid-dev-deps --target thumbv6m-none-eabi 55 | 56 | msrv: 57 | name: Build (MSRV) 58 | runs-on: ubuntu-latest 59 | steps: 60 | - name: Checkout sources 61 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 62 | 63 | - name: Install Rust toolchain 64 | run: | 65 | rustup toolchain add 1.61.0 --profile minimal 66 | rustup default 1.61.0 67 | 68 | - name: Check 69 | run: cargo check --all-features 70 | 71 | miri: 72 | name: Test (Miri) 73 | runs-on: ubuntu-latest 74 | steps: 75 | - name: Checkout sources 76 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 77 | 78 | - name: Install Miri 79 | run: | 80 | rustup toolchain install nightly --component miri 81 | cargo +nightly miri setup 82 | 83 | - name: Default features 84 | run: cargo +nightly miri test --lib 85 | 86 | - name: No features 87 | run: cargo +nightly miri test --lib --no-default-features 88 | 89 | - name: All features 90 | run: cargo +nightly miri test --all-features --lib 91 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 15 | 16 | - name: Install Rust toolchain 17 | run: rustup default nightly 18 | 19 | - name: Install cargo-hack 20 | run: cargo install cargo-hack 21 | 22 | - name: Powerset 23 | working-directory: ./test 24 | run: cargo hack test --feature-powerset 25 | 26 | - name: Minimal Versions 27 | working-directory: ./test 28 | run: cargo hack test --feature-powerset -Z minimal-versions 29 | 30 | msrv: 31 | name: Build (MSRV) 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 36 | 37 | - name: Install Rust toolchain 38 | run: | 39 | rustup toolchain add 1.61.0 --profile minimal 40 | rustup default 1.61.0 41 | 42 | - name: Check 43 | working-directory: ./test 44 | run: cargo check --all-features 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sval" 3 | version = "2.14.1" 4 | authors = ["Ashley Mannix "] 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | documentation = "https://docs.rs/sval" 8 | description = "Streaming, structured value serialization" 9 | repository = "https://github.com/sval-rs/sval" 10 | readme = "README.md" 11 | keywords = ["serialization", "no_std"] 12 | categories = ["encoding", "no-std"] 13 | 14 | [package.metadata.docs.rs] 15 | features = ["std"] 16 | 17 | [workspace] 18 | members = [ 19 | "derive_macros", 20 | "derive", 21 | "derive/test", 22 | "dynamic", 23 | "ref", 24 | "buffer", 25 | "fmt", 26 | "fmt/test", 27 | "nested", 28 | "serde", 29 | "serde/test", 30 | "json", 31 | "json/test", 32 | "json/bench", 33 | "flatten", 34 | "flatten/bench", 35 | "test", 36 | "experiments", 37 | ] 38 | 39 | [features] 40 | std = ["alloc"] 41 | alloc = [] 42 | 43 | derive = ["dep:sval_derive_macros"] 44 | 45 | [dependencies.sval_derive_macros] 46 | version = "2.14.1" 47 | path = "derive_macros" 48 | optional = true 49 | 50 | [dev-dependencies.sval_derive_macros] 51 | path = "derive_macros" 52 | 53 | [dev-dependencies.humantime] 54 | version = "2" 55 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 sval-rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `sval`: Streaming, structured values 2 | 3 | [![Rust](https://github.com/sval-rs/sval/workflows/sval/badge.svg)](https://github.com/sval-rs/sval/actions) 4 | [![Latest version](https://img.shields.io/crates/v/sval.svg)](https://crates.io/crates/sval) 5 | [![Documentation Latest](https://docs.rs/sval/badge.svg)](https://docs.rs/sval) 6 | 7 | `sval` is a lightweight serialization-only framework that treats values like a flat stream of tokens. 8 | It's well suited to self-describing text formats like JSON. 9 | 10 | ## How is this different from `serde`? 11 | 12 | `serde` is the de-facto serialization framework for Rust and is well suited to the majority of 13 | use cases. `sval` is like a light blend of `serde::ser` and `serde::de` that is smaller in scope. 14 | It makes a few key different design decisions than `serde` that make it effective for working with 15 | self-describing formats: 16 | 17 | 1. The API is flat rather than using recursion to stream nested datastructures. 18 | 2. All values with dynamic sizes, including text strings, can be streamed in multiple calls. 19 | 3. Borrowing is an optional optimization. 20 | 4. The core data model is small, with tags for extensibility. 21 | 22 | # Data-model 23 | 24 | - Values: 25 | - `null`: the absence of any other meaningful value. 26 | - Booleans: `true` and `false`. 27 | - Text strings: stream of UTF8-encoded bytes. 28 | - Binary strings: stream of arbtirary bytes. 29 | - Numbers: 30 | - Integers: `u8`-`u128`, `i8`-`i128`. 31 | - Binary floating point: `f32`-`f64`. 32 | - Maps: heterogeneous collection of key-value pairs. 33 | - Sequences: heterogeneous collection of values. 34 | - Tags: out-of-band type hints. 35 | - Tagged values: a tag associated with a value. 36 | - Records: tagged maps where keys are well-known labels. 37 | - Tuples: tagged sequences. 38 | - Enums: tagged variants, where variants are enums, tags, tagged values, records, or tuples. 39 | 40 | `sval` includes built-in tags that extend its data-model with some common datatypes: 41 | 42 | - Rust primitives: 43 | - `()`. 44 | - `Option`. 45 | - Arbitrary-precision decimal floating point numbers. 46 | 47 | Other built-in tags may be added in the future. Libraries may also define their own tags. 48 | 49 | ## Current status 50 | 51 | This project has a complete and stable API, but isn't well documented yet. 52 | -------------------------------------------------------------------------------- /buffer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sval_buffer" 3 | version = "2.14.1" 4 | authors = ["Ashley Mannix "] 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | documentation = "https://docs.rs/sval_buffer" 8 | description = "Value buffering for sval" 9 | repository = "https://github.com/sval-rs/sval" 10 | readme = "README.md" 11 | keywords = ["serialization", "no_std"] 12 | categories = ["encoding", "no-std"] 13 | 14 | [package.metadata.docs.rs] 15 | features = ["std"] 16 | 17 | [features] 18 | default = ["alloc"] 19 | std = ["alloc", "sval/std"] 20 | alloc = ["sval/alloc"] 21 | no_debug_assertions = [] 22 | 23 | [dependencies.sval] 24 | version = "2.14.1" 25 | path = "../" 26 | 27 | [dependencies.sval_ref] 28 | version = "2.14.1" 29 | path = "../ref" 30 | 31 | [dev-dependencies.sval_derive_macros] 32 | version = "2.14.1" 33 | path = "../derive_macros" 34 | 35 | [dev-dependencies.sval_test] 36 | version = "2.14.1" 37 | path = "../test" 38 | -------------------------------------------------------------------------------- /buffer/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /buffer/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /buffer/README.md: -------------------------------------------------------------------------------- 1 | # `sval_buffer` 2 | 3 | [![Rust](https://github.com/sval-rs/sval/workflows/buffer/badge.svg)](https://github.com/sval-rs/sval/actions) 4 | [![Latest version](https://img.shields.io/crates/v/sval.svg)](https://crates.io/crates/sval_buffer) 5 | [![Documentation Latest](https://docs.rs/sval_buffer/badge.svg)](https://docs.rs/sval_buffer) 6 | 7 | Support for buffering implementations of `sval::Value`. 8 | -------------------------------------------------------------------------------- /buffer/benches/value.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | #[macro_use] 6 | extern crate sval_derive_macros; 7 | 8 | #[cfg(feature = "alloc")] 9 | #[derive(Value)] 10 | struct OwnedData { 11 | id: i32, 12 | title: String, 13 | attributes: Vec, 14 | } 15 | 16 | #[cfg(feature = "alloc")] 17 | fn owned_data() -> OwnedData { 18 | OwnedData { 19 | id: 42, 20 | title: "A very important document".to_owned(), 21 | attributes: vec!["#1".to_owned(), "#2".to_owned(), "#3".to_owned()], 22 | } 23 | } 24 | 25 | #[cfg(feature = "alloc")] 26 | #[derive(Value)] 27 | struct BorrowedData<'a> { 28 | id: i32, 29 | title: &'a str, 30 | attributes: &'a [&'a str], 31 | } 32 | 33 | #[cfg(feature = "alloc")] 34 | fn borrowed_data() -> BorrowedData<'static> { 35 | BorrowedData { 36 | id: 42, 37 | title: "A very important document", 38 | attributes: &["#1", "#2", "#3"], 39 | } 40 | } 41 | 42 | #[cfg(feature = "alloc")] 43 | #[bench] 44 | fn borrowed(b: &mut test::Bencher) { 45 | b.iter(|| borrowed_data()) 46 | } 47 | 48 | #[cfg(feature = "alloc")] 49 | #[bench] 50 | fn borrowed_collect_ref(b: &mut test::Bencher) { 51 | b.iter(|| { 52 | let data = borrowed_data(); 53 | test::black_box(sval_buffer::ValueBuf::collect(&data).unwrap()); 54 | }) 55 | } 56 | 57 | #[cfg(feature = "alloc")] 58 | #[bench] 59 | fn borrowed_collect(b: &mut test::Bencher) { 60 | b.iter(|| { 61 | let data = borrowed_data(); 62 | test::black_box(sval_buffer::ValueBuf::collect_owned(data).unwrap()); 63 | }) 64 | } 65 | 66 | #[cfg(feature = "alloc")] 67 | #[bench] 68 | fn borrowed_collect_ref_to_owned(b: &mut test::Bencher) { 69 | b.iter(|| { 70 | let data = borrowed_data(); 71 | sval_buffer::ValueBuf::collect(&data) 72 | .unwrap() 73 | .into_owned() 74 | .unwrap() 75 | }) 76 | } 77 | 78 | #[cfg(feature = "alloc")] 79 | #[bench] 80 | fn borrowed_collect_to_owned(b: &mut test::Bencher) { 81 | b.iter(|| { 82 | let data = borrowed_data(); 83 | sval_buffer::ValueBuf::collect_owned(&data) 84 | .unwrap() 85 | .into_owned() 86 | .unwrap() 87 | }) 88 | } 89 | 90 | #[cfg(feature = "alloc")] 91 | #[bench] 92 | fn owned(b: &mut test::Bencher) { 93 | b.iter(|| owned_data()) 94 | } 95 | 96 | #[cfg(feature = "alloc")] 97 | #[bench] 98 | fn owned_collect_ref(b: &mut test::Bencher) { 99 | b.iter(|| { 100 | let data = owned_data(); 101 | test::black_box(sval_buffer::ValueBuf::collect(&data).unwrap()); 102 | }) 103 | } 104 | 105 | #[cfg(feature = "alloc")] 106 | #[bench] 107 | fn owned_collect(b: &mut test::Bencher) { 108 | b.iter(|| { 109 | let data = owned_data(); 110 | test::black_box(sval_buffer::ValueBuf::collect_owned(data).unwrap()); 111 | }) 112 | } 113 | 114 | #[cfg(feature = "alloc")] 115 | #[bench] 116 | fn owned_collect_ref_to_owned(b: &mut test::Bencher) { 117 | b.iter(|| { 118 | let data = owned_data(); 119 | sval_buffer::ValueBuf::collect(&data) 120 | .unwrap() 121 | .into_owned() 122 | .unwrap() 123 | }) 124 | } 125 | 126 | #[cfg(feature = "alloc")] 127 | #[bench] 128 | fn owned_collect_to_owned(b: &mut test::Bencher) { 129 | b.iter(|| { 130 | let data = owned_data(); 131 | sval_buffer::ValueBuf::collect_owned(&data) 132 | .unwrap() 133 | .into_owned() 134 | .unwrap() 135 | }) 136 | } 137 | -------------------------------------------------------------------------------- /buffer/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::std::fmt; 2 | 3 | /** 4 | An error encountered buffering data. 5 | */ 6 | #[derive(Debug)] 7 | pub struct Error(ErrorKind); 8 | 9 | #[derive(Debug)] 10 | enum ErrorKind { 11 | Unsupported { 12 | actual: &'static str, 13 | expected: &'static str, 14 | }, 15 | InvalidValue { 16 | reason: &'static str, 17 | }, 18 | OutsideContainer { 19 | method: &'static str, 20 | }, 21 | #[allow(dead_code)] // debug builds never reach this variant 22 | NoAlloc { 23 | method: &'static str, 24 | }, 25 | } 26 | 27 | impl fmt::Display for Error { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | match self.0 { 30 | ErrorKind::Unsupported { actual, expected } => { 31 | write!(f, "unexpected {}, expected {}", actual, expected) 32 | } 33 | ErrorKind::InvalidValue { reason } => { 34 | write!(f, "the value is invalid: {}", reason) 35 | } 36 | ErrorKind::OutsideContainer { method } => { 37 | write!(f, "expected a fragment while buffering {}", method) 38 | } 39 | ErrorKind::NoAlloc { method } => write!(f, "cannot allocate for {}", method), 40 | } 41 | } 42 | } 43 | 44 | impl Error { 45 | pub(crate) fn unsupported(expected: &'static str, actual: &'static str) -> Self { 46 | Error(ErrorKind::Unsupported { actual, expected }) 47 | } 48 | 49 | pub(crate) fn outside_container(method: &'static str) -> Self { 50 | Error(ErrorKind::OutsideContainer { method }) 51 | } 52 | 53 | /** 54 | The given value is invalid. 55 | */ 56 | pub(crate) fn invalid_value(reason: &'static str) -> Self { 57 | Error(ErrorKind::InvalidValue { reason }) 58 | } 59 | 60 | #[track_caller] 61 | pub(crate) fn no_alloc(method: &'static str) -> Self { 62 | /* 63 | Users may not know they aren't depending on an allocator when using `sval_buffer` 64 | and have buffering fail unexpectedly. In debug builds we provide a more developer-centric 65 | panic message if this happens so they can decide whether failure to buffer is acceptable 66 | or not, and enable features accordingly. 67 | 68 | If you're not depending on `sval_buffer` directly, but through another library like 69 | `sval_serde`, then you can enable their `alloc` or `std` features instead. 70 | */ 71 | 72 | #[cfg(all(debug_assertions, not(feature = "no_debug_assertions"), not(test)))] 73 | { 74 | panic!("attempt to allocate for {} would fail; add the `alloc` feature of `sval_buffer` or the depdendent `sval_*` library to support allocation. This call will error instead of panicking in release builds. Add the `feature = no_debug_assertions` feature of `sval_buffer` if this error is expected.", method); 75 | } 76 | #[cfg(not(all(debug_assertions, not(feature = "no_debug_assertions"), not(test))))] 77 | { 78 | Error(ErrorKind::NoAlloc { method }) 79 | } 80 | } 81 | } 82 | 83 | #[cfg(feature = "std")] 84 | mod std_support { 85 | use super::*; 86 | 87 | use crate::std::error; 88 | 89 | impl error::Error for Error {} 90 | } 91 | -------------------------------------------------------------------------------- /buffer/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Buffering support for `sval`. 3 | 4 | This crate provides the [`ValueBuf`] type, which can buffer a flat 5 | stream of data into a tree of borrowed values. 6 | 7 | Some functionality requires the `alloc` Cargo feature to be enabled. 8 | Rather than conditionally compile these methods, this library stubs 9 | out functionality when an allocator isn't available. 10 | */ 11 | 12 | #![no_std] 13 | #![deny(missing_docs)] 14 | 15 | mod error; 16 | 17 | #[cfg(feature = "std")] 18 | #[macro_use] 19 | #[allow(unused_imports)] 20 | extern crate std as libstd; 21 | 22 | #[cfg(not(feature = "alloc"))] 23 | extern crate core as std; 24 | 25 | #[cfg(any(test, feature = "alloc"))] 26 | #[macro_use] 27 | #[allow(unused_imports)] 28 | extern crate alloc; 29 | #[cfg(feature = "alloc")] 30 | extern crate core; 31 | 32 | #[cfg(feature = "alloc")] 33 | mod std { 34 | #[allow(unused_imports)] 35 | pub use crate::{ 36 | alloc::{borrow, boxed, collections, string, vec}, 37 | core::{convert, fmt, hash, marker, mem, ops, result, str}, 38 | }; 39 | 40 | #[cfg(feature = "std")] 41 | pub use libstd::error; 42 | } 43 | 44 | mod fragments; 45 | mod value; 46 | 47 | #[cfg(feature = "alloc")] 48 | fn assert_static(_: &mut T) {} 49 | 50 | pub use self::{error::*, fragments::*, value::*}; 51 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sval_derive" 3 | version = "2.14.1" 4 | authors = ["Ashley Mannix "] 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | documentation = "https://docs.rs/sval_derive" 8 | description = "Derive support for sval" 9 | repository = "https://github.com/sval-rs/sval" 10 | readme = "README.md" 11 | keywords = ["serialization", "no_std"] 12 | categories = ["encoding", "no-std"] 13 | 14 | [features] 15 | alloc = ["sval_flatten?/alloc"] 16 | std = ["sval_flatten?/std"] 17 | flatten = ["dep:sval_flatten", "sval_derive_macros/flatten"] 18 | 19 | [dependencies.sval_derive_macros] 20 | version = "2.14.1" 21 | path = "../derive_macros" 22 | 23 | [dependencies.sval_flatten] 24 | version = "2.14.1" 25 | path = "../flatten" 26 | optional = true 27 | -------------------------------------------------------------------------------- /derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /derive/README.md: -------------------------------------------------------------------------------- 1 | # `sval_derive` 2 | 3 | [![Rust](https://github.com/sval-rs/sval/workflows/derive/badge.svg)](https://github.com/sval-rs/sval/actions) 4 | [![Latest version](https://img.shields.io/crates/v/sval.svg)](https://crates.io/crates/sval_derive) 5 | [![Documentation Latest](https://docs.rs/sval_derive/badge.svg)](https://docs.rs/sval_derive) 6 | 7 | Automatically derive `sval::Value`. 8 | -------------------------------------------------------------------------------- /derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | `#![derive(Value)]`. 3 | 4 | This library hosts a custom-derive to simplify implementing `sval::Value`. 5 | 6 | # Structs 7 | 8 | Container attributes: 9 | 10 | - `#[sval(tag = "path::to::TAG")]`: Set a tag on the struct. No tag is used by default. 11 | - `#[sval(label = "text")]`: Set a label on the struct. The identifier of the struct is used by default. 12 | - `#[sval(index = 1)]`: Set an index on the struct. No index is used by default. 13 | - `#[sval(unlabeled_fields)]`: Specify that all fields should be unlabeled. This will stream the struct as a tuple. 14 | If `#[sval(unindexed_fields)]` is also specified then it will stream the struct as a sequence. 15 | - `#[sval(unindexed_fields]`: Specify that all fields should be unindexed. This will stream the struct as a record. 16 | If `#[sval(unlabeled_fields)]` is also specified then it will stream the struct as a sequence. 17 | 18 | Field attributes: 19 | 20 | - `#[sval(tag = "path::to::TAG")]`: Set a tag on the struct field itself. No tag is used by default. 21 | If you want to use a tag to signal that the field's value has a particular property then use `#[sval(data_tag)]`. 22 | - `#[sval(data_tag = "path::to::TAG")]`: Set a tag on the struct field's value. No tag is used by default. 23 | - `#[sval(label = "text")]`: Set a label on the struct field. The identifier of the field is used by default. 24 | - `#[sval(index = 1)]`: Set an index on the struct field. The zero-based offset of the field is used by default. 25 | - `#[sval(skip)]`: Skip a field. 26 | - `#[sval(flatten)]`: Flatten the field onto the struct. This attribute requires the `flatten` Cargo feature. 27 | 28 | # Newtypes 29 | 30 | Container attributes: 31 | 32 | - `#[sval(tag = "path::to::TAG")]`: Set a tag on the newtype. No tag is used by default. 33 | - `#[sval(label = "text")]`: Set a label on the newtype. The identifier of the newtype is used by default. 34 | - `#[sval(index = 1)]`: Set an index on the newtype. No index is used by default. 35 | - `#[sval(transparent)]`: Stream the newtype as its underlying field without wrapping it. 36 | 37 | # Enums 38 | 39 | Container attributes: 40 | 41 | - `#[sval(tag = "path::to::TAG")]`: Set a tag on the enum. No tag is used by default. 42 | - `#[sval(label = "text")]`: Set a label on the enum. The identifier of the enum is used by default. 43 | - `#[sval(index = 1)]`: Set an index on the enum. No index is used by default. 44 | - `#[sval(dynamic)]`: Stream the variant without wrapping it in an enum. 45 | 46 | Variant attributes: 47 | 48 | - `#[sval(tag = "path::to::TAG")]`: Set a tag on the enum variant itself. No tag is used by default. 49 | - `#[sval(label = "text")]`: Set a label on the enum variant. The identifier of the variant is used by default. 50 | - `#[sval(index = 1)]`: Set an index on the enum variant. The zero-based offset of the variant is used by default. 51 | */ 52 | 53 | #[doc(inline)] 54 | pub use sval_derive_macros::*; 55 | 56 | pub mod extensions { 57 | #[cfg(feature = "flatten")] 58 | pub use sval_flatten as flatten; 59 | } 60 | -------------------------------------------------------------------------------- /derive/test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sval_derive_test" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [lib] 8 | path = "lib.rs" 9 | 10 | [dependencies.sval_derive] 11 | path = "../../derive" 12 | features = ["flatten"] 13 | 14 | [dependencies.sval] 15 | path = "../../" 16 | 17 | [dependencies.sval_test] 18 | path = "../../test" 19 | 20 | [dependencies.trybuild] 21 | version = "1" 22 | -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_dynamic_unindexed_variants.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(dynamic, unindexed_variants)] 5 | pub enum Enum { 6 | A, 7 | B, 8 | C, 9 | } 10 | 11 | fn main() { 12 | 13 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_dynamic_unindexed_variants.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/enum_dynamic_unindexed_variants.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: dynamic enums don't have variants 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_dynamic_unlabeled_variants.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(dynamic, unlabeled_variants)] 5 | pub enum Enum { 6 | A, 7 | B, 8 | C, 9 | } 10 | 11 | fn main() { 12 | 13 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_dynamic_unlabeled_variants.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/enum_dynamic_unlabeled_variants.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: dynamic enums don't have variants 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_unindexed_fields.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(unindexed_fields)] 5 | pub enum Enum { 6 | A, 7 | B, 8 | C, 9 | } 10 | 11 | fn main() { 12 | 13 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_unindexed_fields.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/enum_unindexed_fields.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `unindexed_fields` on enum 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_unlabeled_fields.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(unlabeled_fields)] 5 | pub enum Enum { 6 | A, 7 | B, 8 | C, 9 | } 10 | 11 | fn main() { 12 | 13 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/enum_unlabeled_fields.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/enum_unlabeled_fields.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `unlabeled_fields` on enum 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_data_tag.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub struct Newtype(#[sval(data_tag = "sval::tags::NUMBER")] i32); 5 | 6 | fn main() { 7 | 8 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_data_tag.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/newtype_data_tag.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `data_tag` on newtype field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_index.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub struct Newtype(#[sval(index = 1)] i32); 5 | 6 | fn main() { 7 | 8 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_index.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/newtype_index.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `index` on newtype field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_label.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub struct Newtype(#[sval(label = "value")] i32); 5 | 6 | fn main() { 7 | 8 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_label.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/newtype_label.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `label` on newtype field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_tag.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub struct Newtype(#[sval(tag = "sval::tags::NUMBER")] i32); 5 | 6 | fn main() { 7 | 8 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/newtype_tag.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/newtype_tag.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `tag` on newtype field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_seq_incomplete_indexes.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(unindexed_fields, unlabeled_fields)] 5 | pub struct Record { 6 | #[sval(index = 1)] 7 | a: i32, 8 | b: i32, 9 | c: i32, 10 | } 11 | 12 | fn main() { 13 | 14 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_seq_incomplete_indexes.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/record_as_seq_incomplete_indexes.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `index` on struct field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_seq_incomplete_labels.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(unindexed_fields, unlabeled_fields)] 5 | pub struct Record { 6 | #[sval(label = "a")] 7 | a: i32, 8 | b: i32, 9 | c: i32, 10 | } 11 | 12 | fn main() { 13 | 14 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_seq_incomplete_labels.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/record_as_seq_incomplete_labels.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `label` on struct field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_tuple_incomplete_labels.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(unlabeled_fields)] 5 | pub struct Record { 6 | #[sval(label = "a")] 7 | a: i32, 8 | b: i32, 9 | c: i32, 10 | } 11 | 12 | fn main() { 13 | 14 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/record_as_tuple_incomplete_labels.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/record_as_tuple_incomplete_labels.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `label` on struct field 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/tuple_incomplete_labels.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub struct Tuple(#[sval(label = "a")] i32, i32, i32); 5 | 6 | fn main() { 7 | 8 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/tuple_incomplete_labels.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/tuple_incomplete_labels.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: if any fields have a label then all fields need one 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/tuple_transparent.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | #[sval(transparent)] 5 | pub struct Tuple(i32, i32, i32); 6 | 7 | fn main() { 8 | 9 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/tuple_transparent.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/tuple_transparent.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported attribute `transparent` on struct 8 | -------------------------------------------------------------------------------- /derive/test/compile_fail/union.rs: -------------------------------------------------------------------------------- 1 | use sval_derive::*; 2 | 3 | #[derive(Value)] 4 | pub union Union { 5 | a: i32, 6 | b: u32, 7 | } 8 | 9 | fn main() { 10 | 11 | } -------------------------------------------------------------------------------- /derive/test/compile_fail/union.stderr: -------------------------------------------------------------------------------- 1 | error: proc-macro derive panicked 2 | --> compile_fail/union.rs:3:10 3 | | 4 | 3 | #[derive(Value)] 5 | | ^^^^^ 6 | | 7 | = help: message: unsupported container type 8 | -------------------------------------------------------------------------------- /derive_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sval_derive_macros" 3 | version = "2.14.1" 4 | authors = ["Ashley Mannix "] 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | documentation = "https://docs.rs/sval_derive" 8 | description = "Minimal derive support for `sval`" 9 | repository = "https://github.com/sval-rs/sval" 10 | readme = "README.md" 11 | keywords = ["serialization", "no_std"] 12 | categories = ["encoding", "no-std"] 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [features] 18 | flatten = [] 19 | 20 | [dependencies.syn] 21 | version = "2" 22 | features = ["derive", "parsing", "extra-traits"] 23 | 24 | [dependencies.quote] 25 | version = "1" 26 | 27 | [dependencies.proc-macro2] 28 | version = "1" 29 | -------------------------------------------------------------------------------- /derive_macros/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 sval-rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /derive_macros/README.md: -------------------------------------------------------------------------------- 1 | # `sval_derive_macros` 2 | 3 | [![Rust](https://github.com/sval-rs/sval/workflows/derive/badge.svg)](https://github.com/sval-rs/sval/actions) 4 | [![Latest version](https://img.shields.io/crates/v/sval.svg)](https://crates.io/crates/sval_derive) 5 | [![Documentation Latest](https://docs.rs/sval_derive/badge.svg)](https://docs.rs/sval_derive) 6 | 7 | Automatically derive `sval::Value`. This library is an internal implementation detail of `sval` 8 | and shouldn't be depended on directly. 9 | -------------------------------------------------------------------------------- /derive_macros/src/bound.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use syn::{Generics, WhereClause, WherePredicate}; 3 | 4 | pub(crate) fn where_clause_with_bound(generics: &Generics, bound: TokenStream) -> WhereClause { 5 | let new_predicates = generics.type_params().map::(|param| { 6 | let param = ¶m.ident; 7 | parse_quote!(#param : #bound) 8 | }); 9 | 10 | let mut generics = generics.clone(); 11 | generics 12 | .make_where_clause() 13 | .predicates 14 | .extend(new_predicates); 15 | generics.where_clause.unwrap() 16 | } 17 | -------------------------------------------------------------------------------- /derive_macros/src/derive.rs: -------------------------------------------------------------------------------- 1 | mod derive_enum; 2 | mod derive_newtype; 3 | mod derive_struct; 4 | mod derive_unit_struct; 5 | mod derive_void; 6 | 7 | use syn::{Data, DataEnum, DataStruct, DeriveInput, Fields}; 8 | 9 | use self::{ 10 | derive_enum::*, derive_newtype::*, derive_struct::*, derive_unit_struct::*, derive_void::*, 11 | }; 12 | 13 | pub(crate) fn derive(input: DeriveInput) -> proc_macro2::TokenStream { 14 | match &input.data { 15 | Data::Struct(DataStruct { 16 | fields: Fields::Unit, 17 | .. 18 | }) => { 19 | let attrs = UnitStructAttrs::from_attrs(&input.attrs); 20 | 21 | derive_unit_struct(&input.ident, &input.generics, &attrs) 22 | } 23 | Data::Struct(DataStruct { 24 | fields: Fields::Unnamed(ref fields), 25 | .. 26 | }) if fields.unnamed.len() == 1 => { 27 | let attrs = NewtypeAttrs::from_attrs(&input.attrs); 28 | 29 | derive_newtype(&input.ident, &input.generics, &fields.unnamed[0], &attrs) 30 | } 31 | Data::Struct(DataStruct { ref fields, .. }) => { 32 | let attrs = StructAttrs::from_attrs(&input.attrs); 33 | 34 | derive_struct(&input.ident, &input.generics, fields, &attrs) 35 | } 36 | Data::Enum(DataEnum { ref variants, .. }) if variants.len() == 0 => { 37 | let attrs = VoidAttrs::from_attrs(&input.attrs); 38 | 39 | derive_void(&input.ident, &input.generics, &attrs) 40 | } 41 | Data::Enum(DataEnum { variants, .. }) => { 42 | let attrs = EnumAttrs::from_attrs(&input.attrs); 43 | 44 | derive_enum(&input.ident, &input.generics, variants.iter(), &attrs) 45 | } 46 | _ => panic!("unsupported container type"), 47 | } 48 | } 49 | 50 | fn impl_tokens( 51 | impl_generics: syn::ImplGenerics, 52 | ident: &syn::Ident, 53 | ty_generics: syn::TypeGenerics, 54 | bounded_where_clause: &syn::WhereClause, 55 | stream_body: proc_macro2::TokenStream, 56 | tag_body: Option, 57 | ) -> proc_macro2::TokenStream { 58 | let stream_fn = quote!( 59 | fn stream<'sval, __SvalStream: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut __SvalStream) -> sval::Result { 60 | #stream_body 61 | } 62 | ); 63 | 64 | let tag_fn = if let Some(tag_body) = tag_body { 65 | quote!( 66 | fn tag(&self) -> Option { 67 | #tag_body 68 | } 69 | ) 70 | } else { 71 | quote!() 72 | }; 73 | 74 | quote! { 75 | const _: () = { 76 | extern crate sval; 77 | 78 | impl #impl_generics sval::Value for #ident #ty_generics #bounded_where_clause { 79 | #stream_fn 80 | 81 | #tag_fn 82 | } 83 | }; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /derive_macros/src/derive/derive_newtype.rs: -------------------------------------------------------------------------------- 1 | use syn::{Attribute, Field, Generics, Ident, Path}; 2 | 3 | use crate::{ 4 | attr, bound, 5 | derive::impl_tokens, 6 | index::{Index, IndexAllocator, IndexValue}, 7 | label::{label_or_ident, LabelValue}, 8 | stream::stream_newtype, 9 | tag::quote_optional_tag_owned, 10 | }; 11 | 12 | pub(crate) struct NewtypeAttrs { 13 | tag: Option, 14 | label: Option, 15 | index: Option, 16 | transparent: bool, 17 | } 18 | 19 | impl NewtypeAttrs { 20 | pub(crate) fn from_attrs(attrs: &[Attribute]) -> Self { 21 | attr::check( 22 | "newtype", 23 | &[ 24 | &attr::TagAttr, 25 | &attr::LabelAttr, 26 | &attr::IndexAttr, 27 | &attr::TransparentAttr, 28 | ], 29 | attrs, 30 | ); 31 | 32 | let tag = attr::get_unchecked("newtype", attr::TagAttr, attrs); 33 | let label = attr::get_unchecked("newtype", attr::LabelAttr, attrs); 34 | let index = attr::get_unchecked("newtype", attr::IndexAttr, attrs); 35 | let transparent = 36 | attr::get_unchecked("newtype", attr::TransparentAttr, attrs).unwrap_or(false); 37 | 38 | if transparent { 39 | assert!(tag.is_none(), "transparent values cannot have tags"); 40 | assert!(label.is_none(), "transparent values cannot have labels"); 41 | assert!(index.is_none(), "transparent values cannot have indexes"); 42 | } 43 | 44 | NewtypeAttrs { 45 | tag, 46 | label, 47 | index, 48 | transparent, 49 | } 50 | } 51 | 52 | pub(crate) fn tag(&self) -> Option<&Path> { 53 | self.tag.as_ref() 54 | } 55 | 56 | pub(crate) fn label(&self) -> Option { 57 | self.label.clone() 58 | } 59 | 60 | pub(crate) fn index(&self) -> Option { 61 | self.index.clone().map(IndexAllocator::const_index_of) 62 | } 63 | 64 | pub(crate) fn transparent(&self) -> bool { 65 | self.transparent 66 | } 67 | } 68 | 69 | pub(crate) fn derive_newtype<'a>( 70 | ident: &Ident, 71 | generics: &Generics, 72 | field: &Field, 73 | attrs: &NewtypeAttrs, 74 | ) -> proc_macro2::TokenStream { 75 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); 76 | 77 | let bound = parse_quote!(sval::Value); 78 | let bounded_where_clause = bound::where_clause_with_bound(&generics, bound); 79 | 80 | let match_arm = stream_newtype( 81 | quote!(#ident), 82 | field, 83 | attrs.tag(), 84 | Some(label_or_ident(attrs.label(), ident)), 85 | attrs.index(), 86 | attrs.transparent(), 87 | ); 88 | 89 | let tag = quote_optional_tag_owned(attrs.tag()); 90 | 91 | impl_tokens( 92 | impl_generics, 93 | ident, 94 | ty_generics, 95 | &bounded_where_clause, 96 | quote!({ 97 | match self { 98 | #match_arm 99 | } 100 | 101 | Ok(()) 102 | }), 103 | Some(tag), 104 | ) 105 | } 106 | -------------------------------------------------------------------------------- /derive_macros/src/derive/derive_struct.rs: -------------------------------------------------------------------------------- 1 | use syn::{Attribute, Fields, Generics, Ident, Path}; 2 | 3 | use crate::{ 4 | attr, bound, 5 | derive::impl_tokens, 6 | index::{Index, IndexAllocator, IndexValue}, 7 | label::{label_or_ident, LabelValue}, 8 | stream::{stream_record_tuple, RecordTupleTarget}, 9 | tag::quote_optional_tag_owned, 10 | }; 11 | 12 | pub(crate) struct StructAttrs { 13 | tag: Option, 14 | label: Option, 15 | index: Option, 16 | unlabeled_fields: bool, 17 | unindexed_fields: bool, 18 | } 19 | 20 | impl StructAttrs { 21 | pub(crate) fn from_attrs(attrs: &[Attribute]) -> Self { 22 | attr::check( 23 | "struct", 24 | &[ 25 | &attr::TagAttr, 26 | &attr::LabelAttr, 27 | &attr::IndexAttr, 28 | &attr::UnlabeledFieldsAttr, 29 | &attr::UnindexedFieldsAttr, 30 | ], 31 | attrs, 32 | ); 33 | 34 | let tag = attr::get_unchecked("struct", attr::TagAttr, attrs); 35 | let label = attr::get_unchecked("struct", attr::LabelAttr, attrs); 36 | let index = attr::get_unchecked("struct", attr::IndexAttr, attrs); 37 | 38 | let unlabeled_fields = 39 | attr::get_unchecked("struct", attr::UnlabeledFieldsAttr, attrs).unwrap_or(false); 40 | let unindexed_fields = 41 | attr::get_unchecked("struct", attr::UnindexedFieldsAttr, attrs).unwrap_or(false); 42 | 43 | StructAttrs { 44 | tag, 45 | label, 46 | index, 47 | unlabeled_fields, 48 | unindexed_fields, 49 | } 50 | } 51 | 52 | pub(crate) fn tag(&self) -> Option<&Path> { 53 | self.tag.as_ref() 54 | } 55 | 56 | pub(crate) fn label(&self) -> Option { 57 | self.label.clone() 58 | } 59 | 60 | pub(crate) fn index(&self) -> Option { 61 | self.index.clone().map(IndexAllocator::const_index_of) 62 | } 63 | 64 | pub(crate) fn unlabeled_fields(&self) -> bool { 65 | self.unlabeled_fields 66 | } 67 | 68 | pub(crate) fn unindexed_fields(&self) -> bool { 69 | self.unindexed_fields 70 | } 71 | } 72 | 73 | pub(crate) fn derive_struct<'a>( 74 | ident: &Ident, 75 | generics: &Generics, 76 | fields: &Fields, 77 | attrs: &StructAttrs, 78 | ) -> proc_macro2::TokenStream { 79 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); 80 | 81 | let bound = parse_quote!(sval::Value); 82 | let bounded_where_clause = bound::where_clause_with_bound(&generics, bound); 83 | 84 | let (fields, target) = match fields { 85 | Fields::Named(ref fields) => (&fields.named, RecordTupleTarget::named_fields()), 86 | Fields::Unnamed(ref fields) => (&fields.unnamed, RecordTupleTarget::unnamed_fields()), 87 | _ => unreachable!(), 88 | }; 89 | 90 | let match_arm = stream_record_tuple( 91 | quote!(#ident), 92 | fields.iter(), 93 | target, 94 | attrs.tag(), 95 | Some(label_or_ident(attrs.label(), ident)), 96 | attrs.index(), 97 | attrs.unlabeled_fields(), 98 | attrs.unindexed_fields(), 99 | ); 100 | 101 | let tag = quote_optional_tag_owned(attrs.tag()); 102 | 103 | impl_tokens( 104 | impl_generics, 105 | ident, 106 | ty_generics, 107 | &bounded_where_clause, 108 | quote!({ 109 | match self { 110 | #match_arm 111 | } 112 | 113 | Ok(()) 114 | }), 115 | Some(tag), 116 | ) 117 | } 118 | -------------------------------------------------------------------------------- /derive_macros/src/derive/derive_unit_struct.rs: -------------------------------------------------------------------------------- 1 | use syn::{Attribute, Generics, Ident, Path}; 2 | 3 | use crate::{ 4 | attr, bound, 5 | derive::impl_tokens, 6 | index::{Index, IndexAllocator, IndexValue}, 7 | label::{label_or_ident, LabelValue}, 8 | stream::stream_tag, 9 | tag::quote_optional_tag_owned, 10 | }; 11 | 12 | pub(crate) struct UnitStructAttrs { 13 | tag: Option, 14 | label: Option, 15 | index: Option, 16 | } 17 | 18 | impl UnitStructAttrs { 19 | pub(crate) fn from_attrs(attrs: &[Attribute]) -> Self { 20 | attr::check( 21 | "unit struct", 22 | &[&attr::TagAttr, &attr::LabelAttr, &attr::IndexAttr], 23 | attrs, 24 | ); 25 | 26 | let tag = attr::get_unchecked("unit struct", attr::TagAttr, attrs); 27 | let label = attr::get_unchecked("unit struct", attr::LabelAttr, attrs); 28 | let index = attr::get_unchecked("unit struct", attr::IndexAttr, attrs); 29 | 30 | UnitStructAttrs { tag, label, index } 31 | } 32 | 33 | pub(crate) fn tag(&self) -> Option<&Path> { 34 | self.tag.as_ref() 35 | } 36 | 37 | pub(crate) fn label(&self) -> Option { 38 | self.label.clone() 39 | } 40 | 41 | pub(crate) fn index(&self) -> Option { 42 | self.index.clone().map(IndexAllocator::const_index_of) 43 | } 44 | } 45 | 46 | pub(crate) fn derive_unit_struct<'a>( 47 | ident: &Ident, 48 | generics: &Generics, 49 | attrs: &UnitStructAttrs, 50 | ) -> proc_macro2::TokenStream { 51 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); 52 | 53 | let bound = parse_quote!(sval::Value); 54 | let bounded_where_clause = bound::where_clause_with_bound(&generics, bound); 55 | 56 | let match_arm = stream_tag( 57 | quote!(_), 58 | attrs.tag(), 59 | Some(label_or_ident(attrs.label(), ident)), 60 | attrs.index(), 61 | ); 62 | 63 | let tag = quote_optional_tag_owned(attrs.tag()); 64 | 65 | impl_tokens( 66 | impl_generics, 67 | ident, 68 | ty_generics, 69 | &bounded_where_clause, 70 | quote!({ 71 | match self { 72 | #match_arm 73 | } 74 | 75 | Ok(()) 76 | }), 77 | Some(tag), 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /derive_macros/src/derive/derive_void.rs: -------------------------------------------------------------------------------- 1 | use syn::{Attribute, Generics, Ident}; 2 | 3 | use crate::{attr, bound, derive::impl_tokens}; 4 | 5 | pub(crate) struct VoidAttrs {} 6 | 7 | impl VoidAttrs { 8 | pub(crate) fn from_attrs(attrs: &[Attribute]) -> Self { 9 | attr::ensure_empty("void enum", attrs); 10 | 11 | VoidAttrs {} 12 | } 13 | } 14 | 15 | pub(crate) fn derive_void<'a>( 16 | ident: &Ident, 17 | generics: &Generics, 18 | attrs: &VoidAttrs, 19 | ) -> proc_macro2::TokenStream { 20 | let _ = attrs; 21 | 22 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); 23 | 24 | let bound = parse_quote!(sval::Value); 25 | let bounded_where_clause = bound::where_clause_with_bound(&generics, bound); 26 | 27 | impl_tokens( 28 | impl_generics, 29 | ident, 30 | ty_generics, 31 | &bounded_where_clause, 32 | quote!({ match *self {} }), 33 | None, 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /derive_macros/src/index.rs: -------------------------------------------------------------------------------- 1 | pub(crate) struct IndexAllocator { 2 | next_const_index: isize, 3 | explicit: bool, 4 | } 5 | 6 | impl IndexAllocator { 7 | pub(crate) fn new() -> Self { 8 | IndexAllocator { 9 | next_const_index: 0, 10 | explicit: false, 11 | } 12 | } 13 | 14 | pub(crate) fn const_index_of(explicit: IndexValue) -> Index { 15 | match explicit { 16 | IndexValue::Const(explicit) => Index::Explicit(quote!(#explicit)), 17 | IndexValue::Ident(explicit) => Index::Explicit(explicit), 18 | } 19 | } 20 | 21 | pub(crate) fn next_const_index(&mut self, explicit: Option) -> Index { 22 | if let Some(explicit) = explicit { 23 | self.explicit = true; 24 | 25 | let index = match explicit { 26 | IndexValue::Const(explicit) => explicit, 27 | // If we can't compute an index from the value then 28 | // just increment the one we've got 29 | _ => self.next_const_index, 30 | }; 31 | 32 | self.next_const_index = index + 1; 33 | 34 | Self::const_index_of(explicit) 35 | } else { 36 | let index = self.next_const_index; 37 | self.next_const_index += 1; 38 | 39 | if self.explicit { 40 | Index::Explicit(quote!(#index)) 41 | } else { 42 | Index::Implicit(quote!(#index)) 43 | } 44 | } 45 | } 46 | 47 | pub(crate) fn next_computed_index( 48 | &mut self, 49 | ident: &syn::Ident, 50 | explicit: Option, 51 | ) -> Index { 52 | match self.next_const_index(explicit) { 53 | Index::Implicit(_) => Index::Implicit(quote!({ 54 | let index = #ident; 55 | #ident += 1; 56 | index 57 | })), 58 | Index::Explicit(index) => Index::Explicit(index), 59 | } 60 | } 61 | } 62 | 63 | #[derive(Debug, Clone)] 64 | pub(crate) enum IndexValue { 65 | Const(isize), 66 | Ident(proc_macro2::TokenStream), 67 | } 68 | 69 | #[derive(Debug, Clone)] 70 | pub(crate) enum Index { 71 | Implicit(proc_macro2::TokenStream), 72 | Explicit(proc_macro2::TokenStream), 73 | } 74 | 75 | pub(crate) fn quote_index(index: Index) -> proc_macro2::TokenStream { 76 | match index { 77 | Index::Explicit(index) => quote!(&sval::Index::from(#index)), 78 | Index::Implicit(index) => { 79 | quote!(&sval::Index::from(#index).with_tag(&sval::tags::VALUE_OFFSET)) 80 | } 81 | } 82 | } 83 | 84 | pub(crate) fn quote_optional_index(index: Option) -> proc_macro2::TokenStream { 85 | match index { 86 | Some(index) => { 87 | let index = quote_index(index); 88 | quote!(Some(#index)) 89 | } 90 | None => quote!(None), 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /derive_macros/src/label.rs: -------------------------------------------------------------------------------- 1 | use syn::{ext::IdentExt, Ident}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub(crate) enum LabelValue { 5 | Const(String), 6 | Ident(proc_macro2::TokenStream), 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | pub(crate) enum Label { 11 | Implicit(proc_macro2::TokenStream), 12 | Const(proc_macro2::TokenStream), 13 | Ident(proc_macro2::TokenStream), 14 | } 15 | 16 | fn explicit_label(explicit: LabelValue) -> Label { 17 | match explicit { 18 | LabelValue::Const(explicit) => Label::Const(quote!(#explicit)), 19 | LabelValue::Ident(explicit) => Label::Ident(explicit), 20 | } 21 | } 22 | 23 | fn ident_label(ident: &Ident) -> Label { 24 | Label::Implicit({ 25 | let ident = ident.unraw().to_string(); 26 | quote!(#ident) 27 | }) 28 | } 29 | 30 | pub(crate) fn label_or_ident<'a>(explicit: Option, ident: &Ident) -> Label { 31 | explicit 32 | .map(explicit_label) 33 | .unwrap_or_else(|| ident_label(ident)) 34 | } 35 | 36 | pub(crate) fn optional_label_or_ident<'a>( 37 | explicit: Option, 38 | ident: Option<&Ident>, 39 | ) -> Option