├── .cargo └── config.toml ├── .github ├── dependabot.yml └── workflows │ ├── benchmarking.yml │ ├── checking.yml │ ├── coverage.yaml │ ├── formatting.yml │ └── testing.yaml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── Cargo.toml ├── LICENSE.md ├── README.md ├── benches └── benches.rs ├── cSpell.json ├── examples └── parse_torrent.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── from_bytes.rs ├── src ├── de.rs ├── error.rs ├── lib.rs ├── ser.rs ├── ser │ └── string.rs └── value.rs └── tests ├── fixtures └── torrents │ ├── with-one-node.torrent │ └── with-two-nodes.torrent └── tests.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | cov = "llvm-cov" 3 | cov-lcov = "llvm-cov --lcov --output-path=./.coverage/lcov.info" 4 | cov-html = "llvm-cov --html" 5 | time = "build --timings --all-targets" 6 | 7 | [build] 8 | rustflags = [ 9 | "-D", 10 | "warnings", 11 | "-D", 12 | "future-incompatible", 13 | "-D", 14 | "let-underscore", 15 | "-D", 16 | "nonstandard-style", 17 | "-D", 18 | "rust-2018-compatibility", 19 | "-D", 20 | "rust-2018-idioms", 21 | "-D", 22 | "rust-2021-compatibility", 23 | "-D", 24 | "unused", 25 | ] 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | target-branch: "main" 8 | 9 | - package-ecosystem: cargo 10 | directory: / 11 | schedule: 12 | interval: daily 13 | target-branch: "main" 14 | -------------------------------------------------------------------------------- /.github/workflows/benchmarking.yml: -------------------------------------------------------------------------------- 1 | name: Benchmarking 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | check: 12 | name: Benchmarking 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | toolchain: [nightly] 18 | 19 | steps: 20 | - id: checkout 21 | name: Checkout Repository 22 | uses: actions/checkout@v4 23 | 24 | - id: setup 25 | name: Setup Toolchain 26 | uses: dtolnay/rust-toolchain@stable 27 | with: 28 | toolchain: ${{ matrix.toolchain }} 29 | components: clippy 30 | 31 | - id: cache 32 | name: Enable Workflow Cache 33 | uses: Swatinem/rust-cache@v2 34 | 35 | - id: Benchmarking 36 | name: Run Benchmarking 37 | run: cargo bench 38 | -------------------------------------------------------------------------------- /.github/workflows/checking.yml: -------------------------------------------------------------------------------- 1 | name: Checking 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | check: 12 | name: Checking 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | toolchain: [stable, nightly] 18 | 19 | steps: 20 | - id: checkout 21 | name: Checkout Repository 22 | uses: actions/checkout@v4 23 | 24 | - id: setup 25 | name: Setup Toolchain 26 | uses: dtolnay/rust-toolchain@stable 27 | with: 28 | toolchain: ${{ matrix.toolchain }} 29 | components: clippy 30 | 31 | - id: cache 32 | name: Enable Workflow Cache 33 | uses: Swatinem/rust-cache@v2 34 | 35 | - id: check 36 | name: Run Build Checks 37 | run: cargo check --tests --examples --workspace --all-features 38 | 39 | - id: lint 40 | name: Run Lint Checks 41 | run: cargo clippy --tests --examples --workspace --all-features -- -D clippy::correctness -D clippy::suspicious -D clippy::complexity -D clippy::perf -D clippy::style -D clippy::pedantic 42 | 43 | - id: doc 44 | name: Run Documentation Checks 45 | run: cargo test --doc 46 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request_target: 8 | branches: 9 | - main 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | report: 16 | name: Report 17 | environment: coverage 18 | runs-on: ubuntu-latest 19 | env: 20 | CARGO_INCREMENTAL: "0" 21 | RUSTFLAGS: "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests" 22 | RUSTDOCFLAGS: "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests" 23 | 24 | steps: 25 | - id: checkout_push 26 | if: github.event_name == 'push' 27 | name: Checkout Repository (Push) 28 | uses: actions/checkout@v4 29 | 30 | - id: checkout_pull_request_target 31 | if: github.event_name == 'pull_request_target' 32 | name: Checkout Repository (Pull Request Target) 33 | uses: actions/checkout@v4 34 | with: 35 | ref: "refs/pull/${{ github.event.pull_request.number }}/head" 36 | 37 | - id: setup 38 | name: Setup Toolchain 39 | uses: dtolnay/rust-toolchain@stable 40 | with: 41 | toolchain: nightly 42 | components: llvm-tools-preview 43 | 44 | - id: cache 45 | name: Enable Workflow Cache 46 | uses: Swatinem/rust-cache@v2 47 | 48 | - id: tools 49 | name: Install tools 50 | uses: taiki-e/install-action@v2 51 | with: 52 | tool: grcov,cargo-llvm-cov,nextest 53 | 54 | - id: print 55 | name: Print Coverage Report 56 | run: cargo llvm-cov nextest 57 | 58 | - id: report 59 | name: Generate Coverage Report 60 | uses: alekitto/grcov@v0.2 61 | 62 | - id: upload 63 | name: Upload Coverage Report 64 | uses: codecov/codecov-action@v3 65 | with: 66 | token: ${{ secrets.CODECOV_TOKEN }} 67 | files: ${{ steps.coverage.outputs.report }} 68 | verbose: true 69 | fail_ci_if_error: true 70 | -------------------------------------------------------------------------------- /.github/workflows/formatting.yml: -------------------------------------------------------------------------------- 1 | name: Formatting 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | format: 12 | name: Formatting 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - id: checkout 17 | name: Checkout Repository 18 | uses: actions/checkout@v4 19 | 20 | - id: setup 21 | name: Setup Toolchain 22 | uses: dtolnay/rust-toolchain@stable 23 | with: 24 | toolchain: nightly 25 | components: rustfmt 26 | 27 | - id: cache 28 | name: Enable Workflow Cache 29 | uses: Swatinem/rust-cache@v2 30 | 31 | - id: format 32 | name: Run Formatting-Checks 33 | run: cargo fmt --check 34 | -------------------------------------------------------------------------------- /.github/workflows/testing.yaml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | test: 12 | name: Testing 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | toolchain: [stable, nightly] 18 | 19 | steps: 20 | - id: checkout 21 | name: Checkout Repository 22 | uses: actions/checkout@v4 23 | 24 | - id: setup 25 | name: Setup Toolchain 26 | uses: dtolnay/rust-toolchain@stable 27 | with: 28 | toolchain: ${{ matrix.toolchain }} 29 | components: llvm-tools-preview 30 | 31 | - id: cache 32 | name: Enable Job Cache 33 | uses: Swatinem/rust-cache@v2 34 | 35 | - id: test 36 | name: Run Unit Tests 37 | run: cargo test --tests --examples --workspace --all-features 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coverage 2 | *.rustfmt 3 | Cargo.lock 4 | target 5 | 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "streetsidesoftware.code-spell-checker", 4 | "rust-lang.rust-analyzer" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[rust]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "rust-analyzer.checkOnSave": true, 6 | "rust-analyzer.check.command": "clippy", 7 | "rust-analyzer.check.allTargets": true, 8 | "rust-analyzer.check.extraArgs": [ 9 | "--", 10 | "-D", 11 | "clippy::correctness", 12 | "-D", 13 | "clippy::suspicious", 14 | "-W", 15 | "clippy::complexity", 16 | "-W", 17 | "clippy::perf", 18 | "-W", 19 | "clippy::style", 20 | "-W", 21 | "clippy::pedantic", 22 | ], 23 | "rust-analyzer.linkedProjects": [ 24 | "./Cargo.toml" 25 | ], 26 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_bencode" 3 | description = "A Serde backed Bencode encoding/decoding library for Rust." 4 | version = "0.2.4" 5 | authors = ["Toby Padilla ", "Nautilus Cyberneering "] 6 | repository = "https://github.com/toby/serde-bencode" 7 | documentation = "https://docs.rs/serde_bencode/" 8 | license = "MIT" 9 | keywords = ["bencode", "serialize", "deserialize", "serde"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | serde = "1.0" 14 | serde_bytes = "0.11" 15 | 16 | [dev-dependencies] 17 | serde_derive = "1.0" 18 | serde_test = "1.0.176" 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ty Overby 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 | # Serde Bencode 2 | 3 | [![Checking](https://github.com/toby/serde-bencode/actions/workflows/checking.yml/badge.svg)](https://github.com/toby/serde-bencode/actions/workflows/checking.yml) [![Formatting](https://github.com/toby/serde-bencode/actions/workflows/formatting.yml/badge.svg)](https://github.com/toby/serde-bencode/actions/workflows/formatting.yml) [![Testing](https://github.com/toby/serde-bencode/actions/workflows/testing.yaml/badge.svg)](https://github.com/toby/serde-bencode/actions/workflows/testing.yaml) [![Benchmarking](https://github.com/toby/serde-bencode/actions/workflows/benchmarking.yml/badge.svg)](https://github.com/toby/serde-bencode/actions/workflows/benchmarking.yml) [![Coverage](https://github.com/toby/serde-bencode/actions/workflows/coverage.yaml/badge.svg)](https://github.com/toby/serde-bencode/actions/workflows/coverage.yaml) [![Crates.io](https://img.shields.io/crates/v/serde_bencode)](https://crates.io/crates/serde_bencode) [![docs.rs](https://img.shields.io/docsrs/serde_bencode)](https://docs.rs/serde_bencode) 4 | 5 | A [Serde](https://github.com/serde-rs/serde) backed [Bencode](https://en.wikipedia.org/wiki/Bencode) 6 | encoding/decoding library for Rust. 7 | 8 | ## Installation 9 | 10 | Add the following to your `Cargo.toml`: 11 | 12 | ```toml 13 | [dependencies] 14 | serde_bencode = "^0.2.4" 15 | serde = "^1.0.0" 16 | serde_derive = "^1.0.0" 17 | ``` 18 | 19 | ## Usage 20 | 21 | This is an abbreviated `.torrent` parsing example from [examples/parse_torrent.rs](examples/parse_torrent.rs). If you compile this crate as a binary, it will print metadata for any Torrent sent to stdin. 22 | 23 | ## Benchmarking 24 | 25 | ```console 26 | cargo bench 27 | ``` 28 | -------------------------------------------------------------------------------- /benches/benches.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | #[macro_use] 5 | extern crate serde_derive; 6 | 7 | use serde::Serialize; 8 | use serde_bencode::de::from_bytes; 9 | use serde_bencode::ser::Serializer; 10 | use test::Bencher; 11 | 12 | #[bench] 13 | fn ser_de_simple(b: &mut Bencher) { 14 | #[derive(Serialize, Deserialize)] 15 | struct Fake { 16 | a: i64, 17 | b: i64, 18 | } 19 | 20 | b.iter(|| { 21 | let a = Fake { a: 2, b: 7 }; 22 | let mut ser = Serializer::new(); 23 | a.serialize(&mut ser).unwrap(); 24 | let b: Fake = from_bytes(ser.as_ref()).unwrap(); 25 | b 26 | }); 27 | } 28 | 29 | #[bench] 30 | fn ser_de_nested(b: &mut Bencher) { 31 | #[derive(Serialize, Deserialize)] 32 | struct FakeA { 33 | a: i64, 34 | b: i64, 35 | } 36 | 37 | #[derive(Serialize, Deserialize)] 38 | struct FakeB { 39 | a: i64, 40 | b: FakeA, 41 | } 42 | 43 | b.iter(|| { 44 | let a = FakeB { 45 | a: 2, 46 | b: FakeA { a: 7, b: 9 }, 47 | }; 48 | let mut ser = Serializer::new(); 49 | a.serialize(&mut ser).unwrap(); 50 | let b: FakeB = from_bytes(ser.as_ref()).unwrap(); 51 | b 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /cSpell.json: -------------------------------------------------------------------------------- 1 | { 2 | "words": [ 3 | "bencoded", 4 | "Calisota", 5 | "Duckburg", 6 | "httpseeds", 7 | "Krpc", 8 | "lcov", 9 | "libfuzzer", 10 | "newtype", 11 | "rustflags", 12 | "Serde", 13 | "Walke" 14 | ], 15 | "enableFiletypes": [ 16 | "dockerfile", 17 | "shellscript", 18 | "toml" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/parse_torrent.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | use serde_bencode::de; 5 | use serde_bytes::ByteBuf; 6 | use std::io::{self, Read}; 7 | 8 | #[derive(Debug, Deserialize)] 9 | struct Node(String, i64); 10 | 11 | #[derive(Debug, Deserialize)] 12 | struct File { 13 | path: Vec, 14 | length: i64, 15 | #[serde(default)] 16 | md5sum: Option, 17 | } 18 | 19 | #[allow(dead_code)] 20 | #[derive(Debug, Deserialize)] 21 | struct Info { 22 | pub name: String, 23 | pub pieces: ByteBuf, 24 | #[serde(rename = "piece length")] 25 | pub piece_length: i64, 26 | #[serde(default)] 27 | pub md5sum: Option, 28 | #[serde(default)] 29 | pub length: Option, 30 | #[serde(default)] 31 | pub files: Option>, 32 | #[serde(default)] 33 | pub private: Option, 34 | #[serde(default)] 35 | pub path: Option>, 36 | #[serde(default)] 37 | #[serde(rename = "root hash")] 38 | pub root_hash: Option, 39 | } 40 | 41 | #[derive(Debug, Deserialize)] 42 | struct Torrent { 43 | info: Info, 44 | #[serde(default)] 45 | announce: Option, 46 | #[serde(default)] 47 | nodes: Option>, 48 | #[serde(default)] 49 | encoding: Option, 50 | #[serde(default)] 51 | httpseeds: Option>, 52 | #[serde(default)] 53 | #[serde(rename = "announce-list")] 54 | announce_list: Option>>, 55 | #[serde(default)] 56 | #[serde(rename = "creation date")] 57 | creation_date: Option, 58 | #[serde(rename = "comment")] 59 | comment: Option, 60 | #[serde(default)] 61 | #[serde(rename = "created by")] 62 | created_by: Option, 63 | } 64 | 65 | fn render_torrent(torrent: &Torrent) { 66 | println!("name:\t\t{}", torrent.info.name); 67 | println!("announce:\t{:?}", torrent.announce); 68 | println!("nodes:\t\t{:?}", torrent.nodes); 69 | if let Some(al) = &torrent.announce_list { 70 | for a in al { 71 | println!("announce list:\t{}", a[0]); 72 | } 73 | } 74 | println!("httpseeds:\t{:?}", torrent.httpseeds); 75 | println!("creation date:\t{:?}", torrent.creation_date); 76 | println!("comment:\t{:?}", torrent.comment); 77 | println!("created by:\t{:?}", torrent.created_by); 78 | println!("encoding:\t{:?}", torrent.encoding); 79 | println!("piece length:\t{:?}", torrent.info.piece_length); 80 | println!("private:\t{:?}", torrent.info.private); 81 | println!("root hash:\t{:?}", torrent.info.root_hash); 82 | println!("md5sum:\t\t{:?}", torrent.info.md5sum); 83 | println!("path:\t\t{:?}", torrent.info.path); 84 | if let Some(files) = &torrent.info.files { 85 | for f in files { 86 | println!("file path:\t{:?}", f.path); 87 | println!("file length:\t{}", f.length); 88 | println!("file md5sum:\t{:?}", f.md5sum); 89 | } 90 | } 91 | } 92 | 93 | fn main() { 94 | let stdin = io::stdin(); 95 | let mut buffer = Vec::new(); 96 | let mut handle = stdin.lock(); 97 | match handle.read_to_end(&mut buffer) { 98 | Ok(_) => match de::from_bytes::(&buffer) { 99 | Ok(t) => render_torrent(&t), 100 | Err(e) => println!("ERROR: {e:?}"), 101 | }, 102 | Err(e) => println!("ERROR: {e:?}"), 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_bencode_fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = "0.4" 13 | 14 | [dependencies.serde_bencode] 15 | path = ".." 16 | 17 | # Prevent this from interfering with workspaces 18 | [workspace] 19 | members = ["."] 20 | 21 | [[bin]] 22 | name = "from_bytes" 23 | path = "fuzz_targets/from_bytes.rs" 24 | test = false 25 | doc = false 26 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/from_bytes.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | use serde_bencode::from_bytes; 5 | use serde_bencode::value::Value; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | let _: Result = from_bytes(data); 9 | }); 10 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | //! Deserialize bencode data to a Rust data structure 2 | 3 | use crate::error::{Error, Result}; 4 | use serde::{ 5 | de::{self, Error as _, Unexpected}, 6 | forward_to_deserialize_any, 7 | }; 8 | use std::convert::TryFrom; 9 | use std::io::Read; 10 | use std::str; 11 | 12 | #[doc(hidden)] 13 | // todo: This should be pub(crate). 14 | pub struct BencodeAccess<'a, R: Read> { 15 | de: &'a mut Deserializer, 16 | len: Option, 17 | } 18 | 19 | impl<'a, R: 'a + Read> BencodeAccess<'a, R> { 20 | fn new(de: &'a mut Deserializer, len: Option) -> BencodeAccess<'a, R> { 21 | BencodeAccess { de, len } 22 | } 23 | } 24 | 25 | impl<'de, 'a, R: 'a + Read> de::SeqAccess<'de> for BencodeAccess<'a, R> { 26 | type Error = Error; 27 | 28 | fn next_element_seed>( 29 | &mut self, 30 | seed: T, 31 | ) -> Result> { 32 | let res = match self.de.parse()? { 33 | ParseResult::End => Ok(None), 34 | r => { 35 | self.de.next = Some(r); 36 | Ok(Some(seed.deserialize(&mut *self.de)?)) 37 | } 38 | }; 39 | if let Some(l) = self.len { 40 | let l = l - 1; 41 | self.len = Some(l); 42 | if l == 0 && ParseResult::End != self.de.parse()? { 43 | return Err(Error::InvalidType("expected `e`".to_string())); 44 | } 45 | } 46 | res 47 | } 48 | } 49 | 50 | impl<'de, 'a, R: 'a + Read> de::MapAccess<'de> for BencodeAccess<'a, R> { 51 | type Error = Error; 52 | fn next_key_seed(&mut self, seed: K) -> Result> 53 | where 54 | K: de::DeserializeSeed<'de>, 55 | { 56 | match self.de.parse()? { 57 | ParseResult::End => Ok(None), 58 | r => { 59 | self.de.next = Some(r); 60 | Ok(Some(seed.deserialize(&mut *self.de)?)) 61 | } 62 | } 63 | } 64 | 65 | fn next_value_seed(&mut self, seed: V) -> Result 66 | where 67 | V: de::DeserializeSeed<'de>, 68 | { 69 | seed.deserialize(&mut *self.de) 70 | } 71 | } 72 | 73 | impl<'de, 'a, R: 'a + Read> de::VariantAccess<'de> for BencodeAccess<'a, R> { 74 | type Error = Error; 75 | 76 | fn unit_variant(self) -> Result<()> { 77 | Ok(()) 78 | } 79 | 80 | fn newtype_variant_seed>(self, seed: T) -> Result { 81 | let res = seed.deserialize(&mut *self.de)?; 82 | if ParseResult::End != self.de.parse()? { 83 | return Err(Error::InvalidType("expected `e`".to_string())); 84 | } 85 | Ok(res) 86 | } 87 | 88 | fn tuple_variant>(self, len: usize, visitor: V) -> Result { 89 | let res = match self.de.parse()? { 90 | ParseResult::List => visitor.visit_seq(BencodeAccess::new(&mut *self.de, Some(len)))?, 91 | _ => return Err(Error::InvalidType("expected list".to_string())), 92 | }; 93 | if ParseResult::End != self.de.parse()? { 94 | return Err(Error::InvalidType("expected `e`".to_string())); 95 | } 96 | Ok(res) 97 | } 98 | 99 | fn struct_variant>( 100 | self, 101 | _: &'static [&'static str], 102 | visitor: V, 103 | ) -> Result { 104 | let res = de::Deserializer::deserialize_any(&mut *self.de, visitor)?; 105 | if ParseResult::End != self.de.parse()? { 106 | return Err(Error::InvalidType("expected `e`".to_string())); 107 | } 108 | Ok(res) 109 | } 110 | } 111 | 112 | impl<'de, 'a, R: 'a + Read> de::EnumAccess<'de> for BencodeAccess<'a, R> { 113 | type Error = Error; 114 | type Variant = Self; 115 | fn variant_seed>(self, seed: V) -> Result<(V::Value, Self)> { 116 | match self.de.parse()? { 117 | t @ ParseResult::Bytes(_) => { 118 | self.de.next = Some(t); 119 | Ok((seed.deserialize(&mut *self.de)?, self)) 120 | } 121 | ParseResult::Map => Ok((seed.deserialize(&mut *self.de)?, self)), 122 | t => Err(Error::InvalidValue(format!( 123 | "Expected bytes or map; got `{t:?}`" 124 | ))), 125 | } 126 | } 127 | } 128 | 129 | #[derive(Debug, Eq, PartialEq)] 130 | enum ParseResult { 131 | Int(i64), 132 | Bytes(Vec), 133 | /// list start 134 | List, 135 | /// map start 136 | Map, 137 | /// list or map end 138 | End, 139 | } 140 | 141 | impl ParseResult { 142 | fn to_unexpected_error(&self, expected: &str) -> Error { 143 | match self { 144 | Self::Int(i) => Error::invalid_type(Unexpected::Signed(*i), &expected), 145 | Self::Bytes(bytes) => Error::invalid_type(Unexpected::Bytes(bytes), &expected), 146 | Self::List => Error::invalid_type(Unexpected::Seq, &expected), 147 | Self::Map => Error::invalid_type(Unexpected::Map, &expected), 148 | Self::End => Error::custom(format_args!("unexpected end, expected {expected}")), 149 | } 150 | } 151 | } 152 | 153 | /// A structure for deserializing bencode into Rust values. 154 | #[derive(Debug)] 155 | pub struct Deserializer { 156 | reader: R, 157 | next: Option, 158 | } 159 | 160 | #[allow(clippy::extra_unused_lifetimes)] 161 | impl<'de, R: Read> Deserializer { 162 | /// Create a new deserializer. 163 | pub fn new(reader: R) -> Deserializer { 164 | Deserializer { reader, next: None } 165 | } 166 | 167 | fn parse_int(&mut self) -> Result { 168 | let mut buf = [0; 1]; 169 | let mut result = Vec::new(); 170 | loop { 171 | if 1 != self.reader.read(&mut buf).map_err(Error::IoError)? { 172 | return Err(Error::EndOfStream); 173 | } 174 | match buf[0] { 175 | b'e' => { 176 | let len_str = String::from_utf8(result).map_err(|_| { 177 | Error::InvalidValue("Non UTF-8 integer encoding".to_string()) 178 | })?; 179 | let len_int = len_str.parse().map_err(|_| { 180 | Error::InvalidValue(format!("Can't parse `{len_str}` as integer")) 181 | })?; 182 | return Ok(len_int); 183 | } 184 | n => result.push(n), 185 | } 186 | } 187 | } 188 | 189 | fn parse_bytes_len(&mut self, len_char: u8) -> Result { 190 | let mut buf = [0; 1]; 191 | let mut len = Vec::new(); 192 | len.push(len_char); 193 | loop { 194 | if 1 != self.reader.read(&mut buf).map_err(Error::IoError)? { 195 | return Err(Error::EndOfStream); 196 | } 197 | match buf[0] { 198 | b':' => { 199 | let len_str = String::from_utf8(len).map_err(|_| { 200 | Error::InvalidValue("Non UTF-8 integer encoding".to_string()) 201 | })?; 202 | let len_int = len_str.parse().map_err(|_| { 203 | Error::InvalidValue(format!("Can't parse `{len_str}` as string length")) 204 | })?; 205 | return Ok(len_int); 206 | } 207 | n => len.push(n), 208 | } 209 | } 210 | } 211 | 212 | fn parse_bytes(&mut self, len_char: u8) -> Result> { 213 | let len = self.parse_bytes_len(len_char)?; 214 | let mut buf = Vec::new(); 215 | 216 | let len_usize = u64::try_from(len) 217 | .map_err(|_| Error::InvalidLength(String::from("byte string length too large")))?; 218 | 219 | let actual_len = self 220 | .reader 221 | .by_ref() 222 | .take(len_usize) 223 | .read_to_end(&mut buf) 224 | .map_err(Error::IoError)?; 225 | 226 | if len != actual_len { 227 | return Err(Error::EndOfStream); 228 | } 229 | Ok(buf) 230 | } 231 | 232 | fn parse(&mut self) -> Result { 233 | if let Some(t) = self.next.take() { 234 | return Ok(t); 235 | } 236 | let mut buf = [0; 1]; 237 | if 1 != self.reader.read(&mut buf).map_err(Error::IoError)? { 238 | return Err(Error::EndOfStream); 239 | } 240 | match buf[0] { 241 | b'i' => Ok(ParseResult::Int(self.parse_int()?)), 242 | n @ b'0'..=b'9' => Ok(ParseResult::Bytes(self.parse_bytes(n)?)), 243 | b'l' => Ok(ParseResult::List), 244 | b'd' => Ok(ParseResult::Map), 245 | b'e' => Ok(ParseResult::End), 246 | c => Err(Error::InvalidValue(format!( 247 | "Invalid character `{}`", 248 | c as char 249 | ))), 250 | } 251 | } 252 | } 253 | 254 | impl<'de, 'a, R: Read> de::Deserializer<'de> for &'a mut Deserializer { 255 | type Error = Error; 256 | 257 | #[inline] 258 | fn deserialize_any>(self, visitor: V) -> Result { 259 | match self.parse()? { 260 | ParseResult::Int(i) => visitor.visit_i64(i), 261 | ParseResult::Bytes(s) => visitor.visit_bytes(s.as_ref()), 262 | ParseResult::List => visitor.visit_seq(BencodeAccess::new(self, None)), 263 | ParseResult::Map => visitor.visit_map(BencodeAccess::new(self, None)), 264 | ParseResult::End => Err(Error::EndOfStream), 265 | } 266 | } 267 | 268 | forward_to_deserialize_any! { 269 | bool char i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 unit bytes byte_buf seq map unit_struct 270 | tuple_struct ignored_any struct 271 | } 272 | 273 | #[inline] 274 | fn deserialize_newtype_struct>( 275 | self, 276 | _name: &'static str, 277 | visitor: V, 278 | ) -> Result { 279 | visitor.visit_newtype_struct(self) 280 | } 281 | 282 | #[inline] 283 | fn deserialize_option>(self, visitor: V) -> Result { 284 | visitor.visit_some(self) 285 | } 286 | 287 | #[inline] 288 | fn deserialize_enum( 289 | self, 290 | _name: &str, 291 | _variants: &'static [&'static str], 292 | visitor: V, 293 | ) -> Result 294 | where 295 | V: de::Visitor<'de>, 296 | { 297 | visitor.visit_enum(BencodeAccess::new(self, None)) 298 | } 299 | 300 | // Do not delegate this to `deserialize_any` because we want to call `visit_str` instead of 301 | // `visit_bytes` on the visitor, to correctly support adjacently tagged enums (the tag is 302 | // parsed as str, not bytes). 303 | fn deserialize_str(self, visitor: V) -> Result 304 | where 305 | V: de::Visitor<'de>, 306 | { 307 | let bytes = self.parse().and_then(|r| match r { 308 | ParseResult::Bytes(bytes) => Ok(bytes), 309 | _ => Err(r.to_unexpected_error("bytes")), 310 | })?; 311 | 312 | let s = str::from_utf8(&bytes) 313 | .map_err(|_| Error::invalid_value(Unexpected::Bytes(&bytes), &"utf-8 string"))?; 314 | visitor.visit_str(s) 315 | } 316 | 317 | fn deserialize_string(self, visitor: V) -> Result 318 | where 319 | V: de::Visitor<'de>, 320 | { 321 | self.deserialize_str(visitor) 322 | } 323 | 324 | fn deserialize_identifier(self, visitor: V) -> Result 325 | where 326 | V: de::Visitor<'de>, 327 | { 328 | self.deserialize_str(visitor) 329 | } 330 | 331 | fn deserialize_tuple(self, size: usize, visitor: V) -> Result 332 | where 333 | V: de::Visitor<'de>, 334 | { 335 | self.parse().and_then(|r| match r { 336 | ParseResult::List => Ok(()), 337 | _ => Err(r.to_unexpected_error("list")), 338 | })?; 339 | 340 | visitor.visit_seq(BencodeAccess::new(self, Some(size))) 341 | } 342 | } 343 | 344 | /// Deserialize an instance of type `T` from a string of bencode. 345 | /// 346 | /// # Examples 347 | /// ``` 348 | /// # fn main() -> Result<(), serde_bencode::Error> { 349 | /// use serde_derive::{Serialize, Deserialize}; 350 | /// 351 | /// #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] 352 | /// struct Address { 353 | /// street: String, 354 | /// city: String, 355 | /// } 356 | /// 357 | /// let encoded = "d4:city18:Duckburg, Calisota6:street17:1313 Webfoot Walke".to_string(); 358 | /// let decoded: Address = serde_bencode::from_str(&encoded)?; 359 | /// 360 | /// assert_eq!( 361 | /// decoded, 362 | /// Address { 363 | /// street: "1313 Webfoot Walk".to_string(), 364 | /// city: "Duckburg, Calisota".to_string(), 365 | /// } 366 | /// ); 367 | /// # Ok(()) 368 | /// # } 369 | /// ``` 370 | /// 371 | /// # Errors 372 | /// 373 | /// This conversion can fail if the input bencode is improperly formatted or if the structure of 374 | /// the input does not match the structure expected by `T`. It can also fail if `T`'s 375 | /// implementation of `Deserialize` decides to fail. 376 | pub fn from_str<'de, T>(s: &'de str) -> Result 377 | where 378 | T: de::Deserialize<'de>, 379 | { 380 | from_bytes(s.as_bytes()) 381 | } 382 | 383 | /// Deserialize an instance of type `T` from a bencode byte vector. 384 | /// 385 | /// # Examples 386 | /// ``` 387 | /// # fn main() -> Result<(), serde_bencode::Error> { 388 | /// use serde_derive::{Serialize, Deserialize}; 389 | /// 390 | /// #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] 391 | /// struct Address { 392 | /// street: String, 393 | /// city: String, 394 | /// } 395 | /// 396 | /// let encoded = "d4:city18:Duckburg, Calisota6:street17:1313 Webfoot Walke".as_bytes(); 397 | /// let decoded: Address = serde_bencode::from_bytes(&encoded)?; 398 | /// 399 | /// assert_eq!( 400 | /// decoded, 401 | /// Address { 402 | /// street: "1313 Webfoot Walk".to_string(), 403 | /// city: "Duckburg, Calisota".to_string(), 404 | /// } 405 | /// ); 406 | /// # Ok(()) 407 | /// # } 408 | /// ``` 409 | /// 410 | /// # Errors 411 | /// 412 | /// This conversion can fail if the input bencode is improperly formatted or if the structure of 413 | /// the input does not match the structure expected by `T`. It can also fail if `T`'s 414 | /// implementation of `Deserialize` decides to fail. 415 | pub fn from_bytes<'de, T>(b: &'de [u8]) -> Result 416 | where 417 | T: de::Deserialize<'de>, 418 | { 419 | de::Deserialize::deserialize(&mut Deserializer::new(b)) 420 | } 421 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Structures used to handle errors when serializing or deserializing goes wrong. 2 | 3 | use serde::de::Error as DeError; 4 | use serde::de::{Expected, Unexpected}; 5 | use serde::ser::Error as SerError; 6 | use std::error::Error as StdError; 7 | use std::fmt; 8 | use std::fmt::Display; 9 | use std::io::Error as IoError; 10 | use std::result::Result as StdResult; 11 | 12 | /// Alias for `Result`. 13 | pub type Result = StdResult; 14 | 15 | /// Represents all possible errors which can occur when serializing or deserializing bencode. 16 | #[derive(Debug)] 17 | pub enum Error { 18 | /// Raised when an IO error occurred. 19 | IoError(IoError), 20 | 21 | /// Raised when the value being deserialized is of the incorrect type. 22 | InvalidType(String), 23 | 24 | /// Raised when the value being deserialized is of the right type, but is wrong for some other 25 | /// reason. For example, this error may occur when deserializing to a String but the input data 26 | /// is not valid UTF-8. 27 | InvalidValue(String), 28 | 29 | /// Raised when deserializing a sequence or map, but the input data is the wrong length. 30 | InvalidLength(String), 31 | 32 | /// Raised when deserializing an enum, but the variant has an unrecognized name. 33 | UnknownVariant(String), 34 | 35 | /// Raised when deserializing a struct, but there was a field which does not match any of the 36 | /// expected fields. 37 | UnknownField(String), 38 | 39 | /// Raised when deserializing a struct, but there was a field which was expected but not 40 | /// present. 41 | MissingField(String), 42 | 43 | /// Raised when deserializing a struct, but there is more than one field with the same name. 44 | DuplicateField(String), 45 | 46 | /// Catchall for any other kind of error. 47 | Custom(String), 48 | 49 | /// Unexpected end of input stream. 50 | EndOfStream, 51 | } 52 | 53 | impl SerError for Error { 54 | fn custom(msg: T) -> Self { 55 | Error::Custom(msg.to_string()) 56 | } 57 | } 58 | 59 | impl DeError for Error { 60 | fn custom(msg: T) -> Self { 61 | Error::Custom(msg.to_string()) 62 | } 63 | 64 | fn invalid_type(unexpected: Unexpected<'_>, exp: &dyn Expected) -> Self { 65 | Error::InvalidType(format!("Invalid Type: {unexpected} (expected: `{exp}`)")) 66 | } 67 | 68 | fn invalid_value(unexpected: Unexpected<'_>, exp: &dyn Expected) -> Self { 69 | Error::InvalidValue(format!("Invalid Value: {unexpected} (expected: `{exp}`)")) 70 | } 71 | 72 | fn invalid_length(len: usize, exp: &dyn Expected) -> Self { 73 | Error::InvalidLength(format!("Invalid Length: {len} (expected: {exp})")) 74 | } 75 | 76 | fn unknown_variant(field: &str, expected: &'static [&'static str]) -> Self { 77 | Error::UnknownVariant(format!( 78 | "Unknown Variant: `{field}` (expected one of: {expected:?})" 79 | )) 80 | } 81 | 82 | fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { 83 | Error::UnknownField(format!( 84 | "Unknown Field: `{field}` (expected one of: {expected:?})" 85 | )) 86 | } 87 | 88 | fn missing_field(field: &'static str) -> Self { 89 | Error::MissingField(format!("Missing Field: `{field}`")) 90 | } 91 | 92 | fn duplicate_field(field: &'static str) -> Self { 93 | Error::DuplicateField(format!("Duplicate Field: `{field}`")) 94 | } 95 | } 96 | 97 | impl StdError for Error { 98 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 99 | match *self { 100 | Error::IoError(ref error) => Some(error), 101 | _ => None, 102 | } 103 | } 104 | } 105 | 106 | impl fmt::Display for Error { 107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 108 | let message = match *self { 109 | Error::IoError(ref error) => return error.fmt(f), 110 | Error::InvalidType(ref s) 111 | | Error::InvalidValue(ref s) 112 | | Error::InvalidLength(ref s) 113 | | Error::UnknownVariant(ref s) 114 | | Error::UnknownField(ref s) 115 | | Error::MissingField(ref s) 116 | | Error::DuplicateField(ref s) 117 | | Error::Custom(ref s) => s, 118 | Error::EndOfStream => "End of stream", 119 | }; 120 | f.write_str(message) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate is a Rust library for using the [Serde](https://github.com/serde-rs/serde) 2 | //! serialization framework with bencode data. 3 | //! 4 | //! # Examples 5 | //! 6 | //! ``` 7 | //! use serde_derive::{Serialize, Deserialize}; 8 | //! 9 | //! #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] 10 | //! struct Product { 11 | //! name: String, 12 | //! price: u32, 13 | //! } 14 | //! 15 | //! fn main() -> Result<(), Box> { 16 | //! let apple = Product { 17 | //! name: "Apple".to_string(), 18 | //! price: 130, 19 | //! }; 20 | //! 21 | //! let serialized = serde_bencode::to_string(&apple)?; 22 | //! 23 | //! // cspell:disable-next-line 24 | //! assert_eq!(serialized, "d4:name5:Apple5:pricei130ee".to_string()); 25 | //! 26 | //! let deserialized: Product = serde_bencode::from_str(&serialized)?; 27 | //! 28 | //! assert_eq!( 29 | //! deserialized, 30 | //! Product { 31 | //! name: "Apple".to_string(), 32 | //! price: 130, 33 | //! } 34 | //! ); 35 | //! 36 | //! Ok(()) 37 | //! } 38 | //! ``` 39 | 40 | pub mod de; 41 | pub mod error; 42 | pub mod ser; 43 | pub mod value; 44 | 45 | pub use de::{from_bytes, from_str, Deserializer}; 46 | pub use error::{Error, Result}; 47 | pub use ser::{to_bytes, to_string, Serializer}; 48 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | //! Serialize a Rust data structure into bencode data. 2 | 3 | mod string; 4 | 5 | use crate::error::{Error, Result}; 6 | use serde::ser; 7 | use std::mem; 8 | use std::str; 9 | 10 | /// A structure for serializing Rust values into bencode. 11 | #[derive(Default, Debug)] 12 | pub struct Serializer { 13 | buf: Vec, 14 | } 15 | 16 | impl Serializer { 17 | /// Create a new serializer. 18 | #[must_use] 19 | pub fn new() -> Serializer { 20 | Self::default() 21 | } 22 | 23 | /// Consume the serializer and return the contents as a byte vector. 24 | #[must_use] 25 | pub fn into_vec(self) -> Vec { 26 | self.buf 27 | } 28 | 29 | fn push>(&mut self, token: T) { 30 | self.buf.extend_from_slice(token.as_ref()); 31 | } 32 | } 33 | 34 | impl AsRef<[u8]> for Serializer { 35 | fn as_ref(&self) -> &[u8] { 36 | self.buf.as_ref() 37 | } 38 | } 39 | 40 | impl<'a> ser::SerializeSeq for &'a mut Serializer { 41 | type Ok = (); 42 | type Error = Error; 43 | fn serialize_element(&mut self, value: &T) -> Result<()> { 44 | value.serialize(&mut **self) 45 | } 46 | fn end(self) -> Result<()> { 47 | self.push("e"); 48 | Ok(()) 49 | } 50 | } 51 | 52 | impl<'a> ser::SerializeTuple for &'a mut Serializer { 53 | type Ok = (); 54 | type Error = Error; 55 | fn serialize_element(&mut self, value: &T) -> Result<()> { 56 | value.serialize(&mut **self) 57 | } 58 | fn end(self) -> Result<()> { 59 | ser::SerializeSeq::end(self) 60 | } 61 | } 62 | 63 | impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { 64 | type Ok = (); 65 | type Error = Error; 66 | fn serialize_field(&mut self, value: &T) -> Result<()> { 67 | value.serialize(&mut **self) 68 | } 69 | fn end(self) -> Result<()> { 70 | ser::SerializeSeq::end(self) 71 | } 72 | } 73 | 74 | impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { 75 | type Ok = (); 76 | type Error = Error; 77 | fn serialize_field(&mut self, value: &T) -> Result<()> { 78 | value.serialize(&mut **self) 79 | } 80 | fn end(self) -> Result<()> { 81 | self.push("ee"); 82 | Ok(()) 83 | } 84 | } 85 | 86 | #[doc(hidden)] 87 | // todo: This should be pub(crate). 88 | pub struct SerializeMap<'a> { 89 | ser: &'a mut Serializer, 90 | entries: Vec<(Vec, Vec)>, 91 | cur_key: Option>, 92 | } 93 | 94 | impl<'a> SerializeMap<'a> { 95 | pub fn new(ser: &'a mut Serializer, len: usize) -> SerializeMap<'_> { 96 | SerializeMap { 97 | ser, 98 | entries: Vec::with_capacity(len), 99 | cur_key: None, 100 | } 101 | } 102 | 103 | fn end_map(&mut self) -> Result<()> { 104 | if self.cur_key.is_some() { 105 | return Err(Error::InvalidValue( 106 | "`serialize_key` called without calling `serialize_value`".to_string(), 107 | )); 108 | } 109 | let mut entries = mem::take(&mut self.entries); 110 | entries.sort_by(|(a, _), (b, _)| a.cmp(b)); 111 | self.ser.push("d"); 112 | for (k, v) in entries { 113 | ser::Serializer::serialize_bytes(&mut *self.ser, k.as_ref())?; 114 | self.ser.push(v); 115 | } 116 | self.ser.push("e"); 117 | Ok(()) 118 | } 119 | } 120 | 121 | impl<'a> ser::SerializeMap for SerializeMap<'a> { 122 | type Ok = (); 123 | type Error = Error; 124 | fn serialize_key(&mut self, key: &T) -> Result<()> { 125 | if self.cur_key.is_some() { 126 | return Err(Error::InvalidValue( 127 | "`serialize_key` called multiple times without calling `serialize_value`" 128 | .to_string(), 129 | )); 130 | } 131 | self.cur_key = Some(key.serialize(&mut string::Serializer)?); 132 | Ok(()) 133 | } 134 | fn serialize_value(&mut self, value: &T) -> Result<()> { 135 | let key = self.cur_key.take().ok_or_else(|| { 136 | Error::InvalidValue( 137 | "`serialize_value` called without calling `serialize_key`".to_string(), 138 | ) 139 | })?; 140 | let mut ser = Serializer::new(); 141 | value.serialize(&mut ser)?; 142 | let value = ser.into_vec(); 143 | if !value.is_empty() { 144 | self.entries.push((key, value)); 145 | } 146 | Ok(()) 147 | } 148 | fn serialize_entry(&mut self, key: &K, value: &V) -> Result<()> 149 | where 150 | K: ?Sized + ser::Serialize, 151 | V: ?Sized + ser::Serialize, 152 | { 153 | if self.cur_key.is_some() { 154 | return Err(Error::InvalidValue( 155 | "`serialize_key` called multiple times without calling `serialize_value`" 156 | .to_string(), 157 | )); 158 | } 159 | let key = key.serialize(&mut string::Serializer)?; 160 | let mut ser = Serializer::new(); 161 | value.serialize(&mut ser)?; 162 | let value = ser.into_vec(); 163 | if !value.is_empty() { 164 | self.entries.push((key, value)); 165 | } 166 | Ok(()) 167 | } 168 | fn end(mut self) -> Result<()> { 169 | self.end_map() 170 | } 171 | } 172 | 173 | impl<'a> ser::SerializeStruct for SerializeMap<'a> { 174 | type Ok = (); 175 | type Error = Error; 176 | fn serialize_field( 177 | &mut self, 178 | key: &'static str, 179 | value: &T, 180 | ) -> Result<()> { 181 | ser::SerializeMap::serialize_entry(self, key, value) 182 | } 183 | fn end(mut self) -> Result<()> { 184 | self.end_map() 185 | } 186 | } 187 | 188 | impl<'a> ser::SerializeStructVariant for SerializeMap<'a> { 189 | type Ok = (); 190 | type Error = Error; 191 | fn serialize_field( 192 | &mut self, 193 | key: &'static str, 194 | value: &T, 195 | ) -> Result<()> { 196 | ser::SerializeMap::serialize_entry(self, key, value) 197 | } 198 | fn end(mut self) -> Result<()> { 199 | self.end_map()?; 200 | self.ser.push("e"); 201 | Ok(()) 202 | } 203 | } 204 | 205 | impl<'a> ser::Serializer for &'a mut Serializer { 206 | type Ok = (); 207 | type Error = Error; 208 | type SerializeSeq = Self; 209 | type SerializeTuple = Self; 210 | type SerializeTupleStruct = Self; 211 | type SerializeTupleVariant = Self; 212 | type SerializeMap = SerializeMap<'a>; 213 | type SerializeStruct = SerializeMap<'a>; 214 | type SerializeStructVariant = SerializeMap<'a>; 215 | 216 | fn serialize_bool(self, value: bool) -> Result<()> { 217 | self.serialize_i64(i64::from(value)) 218 | } 219 | fn serialize_i8(self, value: i8) -> Result<()> { 220 | self.serialize_i64(i64::from(value)) 221 | } 222 | fn serialize_i16(self, value: i16) -> Result<()> { 223 | self.serialize_i64(i64::from(value)) 224 | } 225 | fn serialize_i32(self, value: i32) -> Result<()> { 226 | self.serialize_i64(i64::from(value)) 227 | } 228 | fn serialize_i64(self, value: i64) -> Result<()> { 229 | self.push("i"); 230 | self.push(value.to_string()); 231 | self.push("e"); 232 | Ok(()) 233 | } 234 | fn serialize_u8(self, value: u8) -> Result<()> { 235 | self.serialize_u64(u64::from(value)) 236 | } 237 | fn serialize_u16(self, value: u16) -> Result<()> { 238 | self.serialize_u64(u64::from(value)) 239 | } 240 | fn serialize_u32(self, value: u32) -> Result<()> { 241 | self.serialize_u64(u64::from(value)) 242 | } 243 | fn serialize_u64(self, value: u64) -> Result<()> { 244 | self.push("i"); 245 | self.push(value.to_string()); 246 | self.push("e"); 247 | Ok(()) 248 | } 249 | fn serialize_f32(self, _value: f32) -> Result<()> { 250 | Err(Error::InvalidValue("Cannot serialize f32".to_string())) 251 | } 252 | fn serialize_f64(self, _value: f64) -> Result<()> { 253 | Err(Error::InvalidValue("Cannot serialize f64".to_string())) 254 | } 255 | fn serialize_char(self, value: char) -> Result<()> { 256 | let mut buffer = [0; 4]; 257 | self.serialize_bytes(value.encode_utf8(&mut buffer).as_bytes()) 258 | } 259 | 260 | fn serialize_str(self, value: &str) -> Result<()> { 261 | self.serialize_bytes(value.as_bytes()) 262 | } 263 | fn serialize_bytes(self, value: &[u8]) -> Result<()> { 264 | self.push(value.len().to_string()); 265 | self.push(":"); 266 | self.push(value); 267 | Ok(()) 268 | } 269 | fn serialize_unit(self) -> Result<()> { 270 | Ok(()) 271 | } 272 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 273 | self.serialize_unit() 274 | } 275 | fn serialize_unit_variant( 276 | self, 277 | _name: &'static str, 278 | _variant_index: u32, 279 | variant: &'static str, 280 | ) -> Result<()> { 281 | self.serialize_str(variant) 282 | } 283 | fn serialize_newtype_struct( 284 | self, 285 | _name: &'static str, 286 | value: &T, 287 | ) -> Result<()> { 288 | value.serialize(self) 289 | } 290 | fn serialize_newtype_variant( 291 | self, 292 | _name: &'static str, 293 | _variant_index: u32, 294 | variant: &'static str, 295 | value: &T, 296 | ) -> Result<()> { 297 | self.push("d"); 298 | self.serialize_bytes(variant.as_bytes())?; 299 | value.serialize(&mut *self)?; 300 | self.push("e"); 301 | Ok(()) 302 | } 303 | fn serialize_none(self) -> Result<()> { 304 | Ok(()) 305 | } 306 | fn serialize_some(self, value: &T) -> Result<()> { 307 | value.serialize(self) 308 | } 309 | fn serialize_seq(self, _len: Option) -> Result { 310 | self.push("l"); 311 | Ok(self) 312 | } 313 | fn serialize_tuple(self, size: usize) -> Result { 314 | self.serialize_seq(Some(size)) 315 | } 316 | fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { 317 | self.serialize_seq(Some(len)) 318 | } 319 | fn serialize_tuple_variant( 320 | self, 321 | _name: &'static str, 322 | _variant_index: u32, 323 | variant: &'static str, 324 | _len: usize, 325 | ) -> Result { 326 | self.push("d"); 327 | self.serialize_bytes(variant.as_bytes())?; 328 | self.push("l"); 329 | Ok(self) 330 | } 331 | fn serialize_map(self, len: Option) -> Result { 332 | Ok(SerializeMap::new(self, len.unwrap_or(0))) 333 | } 334 | fn serialize_struct(self, _name: &'static str, len: usize) -> Result { 335 | self.serialize_map(Some(len)) 336 | } 337 | fn serialize_struct_variant( 338 | self, 339 | _name: &'static str, 340 | _variant_index: u32, 341 | variant: &'static str, 342 | len: usize, 343 | ) -> Result { 344 | self.push("d"); 345 | self.serialize_bytes(variant.as_bytes())?; 346 | Ok(SerializeMap::new(self, len)) 347 | } 348 | } 349 | 350 | /// Serialize the given data into a bencode byte vector. 351 | /// 352 | /// # Examples 353 | /// ``` 354 | /// # fn main() -> Result<(), serde_bencode::Error> { 355 | /// use serde_derive::{Serialize, Deserialize}; 356 | /// 357 | /// #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] 358 | /// struct Address { 359 | /// street: String, 360 | /// city: String, 361 | /// } 362 | /// 363 | /// let address = Address { 364 | /// street: "1313 Webfoot Walk".to_string(), 365 | /// city: "Duckburg, Calisota".to_string(), 366 | /// }; 367 | /// 368 | /// let bytes = serde_bencode::to_bytes(&address)?; 369 | /// assert_eq!( 370 | /// String::from_utf8(bytes).unwrap(), 371 | /// "d4:city18:Duckburg, Calisota6:street17:1313 Webfoot Walke", 372 | /// ); 373 | /// # Ok(()) 374 | /// # } 375 | /// ``` 376 | /// 377 | /// # Errors 378 | /// 379 | /// Serialization can fail if `T`'s implementation of `Serialize` decides to fail or `T` contains 380 | /// floating point values, which bencode cannot serialize. 381 | pub fn to_bytes(b: &T) -> Result> { 382 | let mut ser = Serializer::new(); 383 | b.serialize(&mut ser)?; 384 | Ok(ser.into_vec()) 385 | } 386 | 387 | /// Serialize the given data into a String of bencode. 388 | /// 389 | /// # Examples 390 | /// ``` 391 | /// # fn main() -> Result<(), serde_bencode::Error> { 392 | /// use serde_derive::{Serialize, Deserialize}; 393 | /// 394 | /// #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] 395 | /// struct Address { 396 | /// street: String, 397 | /// city: String, 398 | /// } 399 | /// 400 | /// let address = Address { 401 | /// street: "1313 Webfoot Walk".to_string(), 402 | /// city: "Duckburg, Calisota".to_string(), 403 | /// }; 404 | /// 405 | /// assert_eq!( 406 | /// serde_bencode::to_string(&address)?, 407 | /// "d4:city18:Duckburg, Calisota6:street17:1313 Webfoot Walke".to_string(), 408 | /// ); 409 | /// # Ok(()) 410 | /// # } 411 | /// ``` 412 | /// 413 | /// # Errors 414 | /// 415 | /// Serialization can fail if `T`'s implementation of `Serialize` decides to fail or `T` contains 416 | /// floating point values, which bencode cannot serialize. 417 | pub fn to_string(b: &T) -> Result { 418 | let mut ser = Serializer::new(); 419 | b.serialize(&mut ser)?; 420 | str::from_utf8(ser.as_ref()) 421 | .map(std::string::ToString::to_string) 422 | .map_err(|_| Error::InvalidValue("Not an UTF-8".to_string())) 423 | } 424 | -------------------------------------------------------------------------------- /src/ser/string.rs: -------------------------------------------------------------------------------- 1 | //! Serializer for serializing *just* strings. 2 | 3 | use crate::error::{Error, Result}; 4 | use serde::de; 5 | use serde::ser; 6 | use std::fmt; 7 | use std::str; 8 | 9 | struct Expected; 10 | impl de::Expected for Expected { 11 | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(formatter, "a string or bytes") 13 | } 14 | } 15 | 16 | fn unexpected(unexp: de::Unexpected<'_>) -> Result { 17 | Err(de::Error::invalid_type(unexp, &Expected)) 18 | } 19 | 20 | #[doc(hidden)] 21 | /// StringSerializer for serializing *just* strings (bytes are also strings in bencode). 22 | /// The string is returned as Result>::Ok without any prefixing (without bencode string 23 | /// length prefix). 24 | // todo: This should be pub(crate). 25 | pub struct Serializer; 26 | 27 | impl<'a> ser::Serializer for &'a mut Serializer { 28 | type Ok = Vec; 29 | type Error = Error; 30 | type SerializeSeq = ser::Impossible, Error>; 31 | type SerializeTuple = ser::Impossible, Error>; 32 | type SerializeTupleStruct = ser::Impossible, Error>; 33 | type SerializeTupleVariant = ser::Impossible, Error>; 34 | type SerializeMap = ser::Impossible, Error>; 35 | type SerializeStruct = ser::Impossible, Error>; 36 | type SerializeStructVariant = ser::Impossible, Error>; 37 | 38 | fn serialize_bool(self, value: bool) -> Result> { 39 | unexpected(de::Unexpected::Bool(value)) 40 | } 41 | fn serialize_i8(self, value: i8) -> Result> { 42 | self.serialize_i64(i64::from(value)) 43 | } 44 | fn serialize_i16(self, value: i16) -> Result> { 45 | self.serialize_i64(i64::from(value)) 46 | } 47 | fn serialize_i32(self, value: i32) -> Result> { 48 | self.serialize_i64(i64::from(value)) 49 | } 50 | fn serialize_i64(self, value: i64) -> Result> { 51 | unexpected(de::Unexpected::Signed(value)) 52 | } 53 | fn serialize_u8(self, value: u8) -> Result> { 54 | self.serialize_u64(u64::from(value)) 55 | } 56 | fn serialize_u16(self, value: u16) -> Result> { 57 | self.serialize_u64(u64::from(value)) 58 | } 59 | fn serialize_u32(self, value: u32) -> Result> { 60 | self.serialize_u64(u64::from(value)) 61 | } 62 | fn serialize_u64(self, value: u64) -> Result> { 63 | unexpected(de::Unexpected::Unsigned(value)) 64 | } 65 | fn serialize_f32(self, value: f32) -> Result> { 66 | self.serialize_f64(f64::from(value)) 67 | } 68 | fn serialize_f64(self, value: f64) -> Result> { 69 | unexpected(de::Unexpected::Float(value)) 70 | } 71 | fn serialize_char(self, value: char) -> Result> { 72 | self.serialize_bytes(&[value as u8]) 73 | } 74 | fn serialize_str(self, value: &str) -> Result> { 75 | self.serialize_bytes(value.as_bytes()) 76 | } 77 | fn serialize_bytes(self, value: &[u8]) -> Result> { 78 | Ok(value.into()) 79 | } 80 | fn serialize_unit(self) -> Result> { 81 | unexpected(de::Unexpected::Unit) 82 | } 83 | fn serialize_unit_struct(self, _name: &'static str) -> Result> { 84 | self.serialize_unit() 85 | } 86 | fn serialize_unit_variant( 87 | self, 88 | _name: &'static str, 89 | _variant_index: u32, 90 | _variant: &'static str, 91 | ) -> Result> { 92 | unexpected(de::Unexpected::UnitVariant) 93 | } 94 | fn serialize_newtype_struct( 95 | self, 96 | _name: &'static str, 97 | _value: &T, 98 | ) -> Result> { 99 | unexpected(de::Unexpected::NewtypeStruct) 100 | } 101 | fn serialize_newtype_variant( 102 | self, 103 | _name: &'static str, 104 | _variant_index: u32, 105 | _variant: &'static str, 106 | _value: &T, 107 | ) -> Result> { 108 | unexpected(de::Unexpected::NewtypeVariant) 109 | } 110 | fn serialize_none(self) -> Result> { 111 | unexpected(de::Unexpected::Option) 112 | } 113 | fn serialize_some(self, _value: &T) -> Result> { 114 | unexpected(de::Unexpected::Option) 115 | } 116 | fn serialize_seq(self, _len: Option) -> Result, Error>> { 117 | unexpected(de::Unexpected::Seq) 118 | } 119 | fn serialize_tuple(self, _size: usize) -> Result, Error>> { 120 | unexpected(de::Unexpected::Seq) 121 | } 122 | fn serialize_tuple_struct( 123 | self, 124 | _name: &'static str, 125 | _len: usize, 126 | ) -> Result, Error>> { 127 | unexpected(de::Unexpected::NewtypeStruct) 128 | } 129 | fn serialize_tuple_variant( 130 | self, 131 | _name: &'static str, 132 | _variant_index: u32, 133 | _variant: &'static str, 134 | _len: usize, 135 | ) -> Result, Error>> { 136 | unexpected(de::Unexpected::TupleVariant) 137 | } 138 | fn serialize_map(self, _len: Option) -> Result, Error>> { 139 | unexpected(de::Unexpected::Map) 140 | } 141 | fn serialize_struct( 142 | self, 143 | _name: &'static str, 144 | _len: usize, 145 | ) -> Result, Error>> { 146 | unexpected(de::Unexpected::NewtypeStruct) 147 | } 148 | fn serialize_struct_variant( 149 | self, 150 | _name: &'static str, 151 | _variant_index: u32, 152 | _variant: &'static str, 153 | _len: usize, 154 | ) -> Result, Error>> { 155 | unexpected(de::Unexpected::StructVariant) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | //! Structures for representing bencoded values with Rust data types. 2 | 3 | use serde::de; 4 | use serde::ser::{self, SerializeMap, SerializeSeq}; 5 | use serde_bytes::{ByteBuf, Bytes}; 6 | use std::collections::HashMap; 7 | use std::fmt; 8 | 9 | /// All possible values which may be serialized in bencode. 10 | #[derive(PartialEq, Eq, Clone, Debug)] 11 | pub enum Value { 12 | /// A generic list of bytes. 13 | Bytes(Vec), 14 | 15 | /// An integer. 16 | Int(i64), 17 | 18 | /// A list of other bencoded values. 19 | List(Vec), 20 | 21 | /// A map of (key, value) pairs. 22 | Dict(HashMap, Value>), 23 | } 24 | 25 | impl ser::Serialize for Value { 26 | #[inline] 27 | fn serialize(&self, s: S) -> Result 28 | where 29 | S: ser::Serializer, 30 | { 31 | match *self { 32 | Value::Bytes(ref v) => s.serialize_bytes(v), 33 | Value::Int(v) => s.serialize_i64(v), 34 | Value::List(ref v) => { 35 | let mut seq = s.serialize_seq(Some(v.len()))?; 36 | for e in v { 37 | seq.serialize_element(e)?; 38 | } 39 | seq.end() 40 | } 41 | Value::Dict(ref vs) => { 42 | let mut map = s.serialize_map(Some(vs.len()))?; 43 | for (k, v) in vs { 44 | map.serialize_entry(&Bytes::new(k), v)?; 45 | } 46 | map.end() 47 | } 48 | } 49 | } 50 | } 51 | 52 | struct ValueVisitor; 53 | 54 | impl<'de> de::Visitor<'de> for ValueVisitor { 55 | type Value = Value; 56 | 57 | fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 58 | formatter.write_str("any valid BEncode value") 59 | } 60 | 61 | #[inline] 62 | fn visit_i64(self, value: i64) -> Result { 63 | Ok(Value::Int(value)) 64 | } 65 | 66 | #[allow(clippy::cast_possible_wrap)] 67 | #[inline] 68 | fn visit_u64(self, value: u64) -> Result { 69 | Ok(Value::Int(value as i64)) 70 | } 71 | 72 | #[inline] 73 | fn visit_str(self, value: &str) -> Result 74 | where 75 | E: de::Error, 76 | { 77 | Ok(Value::Bytes(value.into())) 78 | } 79 | 80 | #[inline] 81 | fn visit_string(self, value: String) -> Result { 82 | Ok(Value::Bytes(value.into())) 83 | } 84 | 85 | #[inline] 86 | fn visit_bytes(self, value: &[u8]) -> Result { 87 | Ok(Value::Bytes(value.into())) 88 | } 89 | 90 | #[inline] 91 | fn visit_seq(self, mut access: V) -> Result 92 | where 93 | V: de::SeqAccess<'de>, 94 | { 95 | let mut seq = Vec::new(); 96 | while let Some(e) = access.next_element()? { 97 | seq.push(e); 98 | } 99 | Ok(Value::List(seq)) 100 | } 101 | 102 | #[inline] 103 | fn visit_map(self, mut access: V) -> Result 104 | where 105 | V: de::MapAccess<'de>, 106 | { 107 | let mut map = HashMap::new(); 108 | while let Some((k, v)) = access.next_entry::()? { 109 | map.insert(k.into_vec(), v); 110 | } 111 | Ok(Value::Dict(map)) 112 | } 113 | } 114 | 115 | impl<'de> de::Deserialize<'de> for Value { 116 | #[inline] 117 | fn deserialize(deserializer: D) -> Result 118 | where 119 | D: de::Deserializer<'de>, 120 | { 121 | deserializer.deserialize_any(ValueVisitor) 122 | } 123 | } 124 | 125 | impl From for Value { 126 | fn from(v: i64) -> Value { 127 | Value::Int(v) 128 | } 129 | } 130 | 131 | impl From for Value { 132 | fn from(s: String) -> Value { 133 | Value::Bytes(s.into_bytes()) 134 | } 135 | } 136 | 137 | impl<'a> From<&'a str> for Value { 138 | fn from(v: &str) -> Value { 139 | Value::Bytes(v.as_bytes().to_vec()) 140 | } 141 | } 142 | 143 | impl From> for Value { 144 | fn from(v: Vec) -> Value { 145 | Value::Bytes(v) 146 | } 147 | } 148 | 149 | impl From> for Value { 150 | fn from(v: Vec) -> Value { 151 | Value::List(v) 152 | } 153 | } 154 | 155 | impl From, Value>> for Value { 156 | fn from(v: HashMap, Value>) -> Value { 157 | Value::Dict(v) 158 | } 159 | } 160 | 161 | #[cfg(test)] 162 | mod tests { 163 | use std::collections::HashMap; 164 | 165 | use serde_test::{assert_tokens, Token}; 166 | 167 | use super::Value; 168 | 169 | fn assert_bytes_eq(actual: &[u8], expected: &[u8]) { 170 | assert_eq!( 171 | actual, 172 | expected, 173 | "expected {:?} to equal {:?}", 174 | String::from_utf8_lossy(actual), 175 | String::from_utf8_lossy(expected) 176 | ); 177 | } 178 | 179 | #[test] 180 | fn test_ser_de_bytes() { 181 | let int = Value::Bytes(b"1".to_vec()); 182 | 183 | assert_tokens(&int, &[Token::Bytes(b"1")]); 184 | } 185 | 186 | #[test] 187 | fn test_ser_de_int() { 188 | let int = Value::Int(1); 189 | assert_tokens(&int, &[Token::I64(1)]); 190 | } 191 | 192 | #[test] 193 | fn test_ser_de_list() { 194 | let int = Value::List(vec![Value::Int(1)]); 195 | 196 | assert_tokens( 197 | &int, 198 | &[Token::Seq { len: Some(1) }, Token::I64(1), Token::SeqEnd], 199 | ); 200 | } 201 | 202 | #[test] 203 | fn test_ser_de_dict() { 204 | let dict = Value::Dict(HashMap::from([(b"key".to_vec(), Value::Int(1))])); 205 | 206 | assert_tokens( 207 | &dict, 208 | &[ 209 | Token::Map { len: Some(1) }, 210 | Token::Bytes(b"key"), 211 | Token::I64(1), 212 | Token::MapEnd, 213 | ], 214 | ); 215 | } 216 | 217 | mod it_should_be_converted_from { 218 | use std::collections::HashMap; 219 | 220 | use crate::value::Value; 221 | 222 | #[test] 223 | fn an_i64() { 224 | let value: Value = 11i64.into(); 225 | assert_eq!(value, Value::Int(11)); 226 | } 227 | 228 | #[test] 229 | fn a_string() { 230 | let value: Value = "11".into(); 231 | assert_eq!(value, Value::Bytes(b"11".to_vec())); 232 | } 233 | 234 | #[test] 235 | fn a_str_reference() { 236 | let value: Value = "11".to_string().into(); 237 | assert_eq!(value, Value::Bytes(b"11".to_vec())); 238 | } 239 | 240 | #[test] 241 | fn a_byte_vector() { 242 | let value: Value = vec![b'1', b'1'].into(); 243 | assert_eq!(value, Value::Bytes(b"11".to_vec())); 244 | } 245 | 246 | #[test] 247 | fn a_vector_of_other_values() { 248 | let value: Value = vec![Value::Bytes(b"11".to_vec())].into(); 249 | assert_eq!(value, Value::List(vec!(Value::Bytes(b"11".to_vec())))); 250 | } 251 | 252 | #[test] 253 | fn a_hash_map_of_other_values() { 254 | let value: Value = HashMap::from([(b"key".to_vec(), Value::Int(3))]).into(); 255 | assert_eq!( 256 | value, 257 | Value::Dict(HashMap::from([(b"key".to_vec(), Value::Int(3))])) 258 | ); 259 | } 260 | } 261 | 262 | mod for_serialization_and_deserialization_of_a { 263 | mod byte_string { 264 | 265 | mod empty { 266 | use crate::{from_bytes, Serializer}; 267 | 268 | use crate::value::tests::assert_bytes_eq; 269 | use crate::value::Value; 270 | use serde::Serialize; 271 | 272 | #[test] 273 | fn serialization() { 274 | let mut ser = Serializer::new(); 275 | 276 | let value = Value::Bytes(b"".to_vec()); 277 | let _unused = value.serialize(&mut ser); 278 | 279 | assert_bytes_eq(ser.as_ref(), b"0:"); 280 | } 281 | 282 | #[test] 283 | fn deserialization() { 284 | let value: Value = from_bytes(b"0:").unwrap(); 285 | 286 | assert_eq!(value, Value::Bytes(b"".to_vec())); 287 | } 288 | } 289 | 290 | mod non_empty { 291 | use crate::{from_bytes, Serializer}; 292 | 293 | use crate::value::tests::assert_bytes_eq; 294 | use crate::value::Value; 295 | use serde::Serialize; 296 | 297 | #[test] 298 | fn serialization() { 299 | let mut ser = Serializer::new(); 300 | 301 | let value = Value::Bytes(b"spam".to_vec()); 302 | let _unused = value.serialize(&mut ser); 303 | 304 | assert_bytes_eq(ser.as_ref(), b"4:spam"); 305 | } 306 | 307 | #[test] 308 | fn deserialization() { 309 | let value: Value = from_bytes(b"4:spam").unwrap(); 310 | 311 | assert_eq!(value, Value::Bytes(b"spam".to_vec())); 312 | } 313 | } 314 | } 315 | 316 | mod integer { 317 | 318 | mod positive { 319 | use serde::Serialize; 320 | 321 | use crate::{ 322 | from_bytes, 323 | value::{tests::assert_bytes_eq, Value}, 324 | Serializer, 325 | }; 326 | 327 | #[test] 328 | fn serialization() { 329 | let mut ser = Serializer::new(); 330 | 331 | let value = Value::Int(3); 332 | let _unused = value.serialize(&mut ser); 333 | 334 | assert_bytes_eq(ser.as_ref(), b"i3e"); 335 | } 336 | 337 | #[test] 338 | fn deserialization() { 339 | let value: Value = from_bytes(b"i3e").unwrap(); 340 | 341 | assert_eq!(value, Value::Int(3)); 342 | } 343 | } 344 | 345 | mod negative { 346 | use serde::Serialize; 347 | 348 | use crate::{ 349 | from_bytes, 350 | value::{tests::assert_bytes_eq, Value}, 351 | Serializer, 352 | }; 353 | 354 | #[test] 355 | fn serialization() { 356 | let mut ser = Serializer::new(); 357 | 358 | let value = Value::Int(-3); 359 | let _unused = value.serialize(&mut ser); 360 | 361 | assert_bytes_eq(ser.as_ref(), b"i-3e"); 362 | } 363 | 364 | #[test] 365 | fn deserialization() { 366 | let value: Value = from_bytes(b"i-3e").unwrap(); 367 | 368 | assert_eq!(value, Value::Int(-3)); 369 | } 370 | } 371 | } 372 | 373 | mod list { 374 | 375 | mod empty { 376 | use serde::Serialize; 377 | 378 | use crate::{ 379 | from_bytes, 380 | value::{tests::assert_bytes_eq, Value}, 381 | Serializer, 382 | }; 383 | 384 | #[test] 385 | fn serialization() { 386 | let mut ser = Serializer::new(); 387 | 388 | let value = Value::List(vec![]); 389 | let _unused = value.serialize(&mut ser); 390 | 391 | assert_bytes_eq(ser.as_ref(), b"le"); 392 | } 393 | 394 | #[test] 395 | fn deserialization() { 396 | let value: Value = from_bytes(b"le").unwrap(); 397 | 398 | assert_eq!(value, Value::List(vec![])); 399 | } 400 | } 401 | 402 | mod with_integers { 403 | 404 | mod with_one_integer { 405 | use serde::Serialize; 406 | 407 | use crate::{ 408 | from_bytes, 409 | value::{tests::assert_bytes_eq, Value}, 410 | Serializer, 411 | }; 412 | 413 | #[test] 414 | fn serialization() { 415 | let mut ser = Serializer::new(); 416 | 417 | let value = Value::List(vec![Value::Int(3)]); 418 | let _unused = value.serialize(&mut ser); 419 | 420 | assert_bytes_eq(ser.as_ref(), b"li3ee"); 421 | } 422 | 423 | #[test] 424 | fn deserialization() { 425 | let value: Value = from_bytes(b"li3ee").unwrap(); 426 | 427 | assert_eq!(value, Value::List(vec![Value::Int(3)])); 428 | } 429 | } 430 | 431 | mod with_multiple_integers { 432 | use serde::Serialize; 433 | 434 | use crate::{ 435 | from_bytes, 436 | value::{tests::assert_bytes_eq, Value}, 437 | Serializer, 438 | }; 439 | 440 | #[test] 441 | fn serialization() { 442 | let mut ser = Serializer::new(); 443 | 444 | let value = Value::List(vec![Value::Int(1), Value::Int(2)]); 445 | let _unused = value.serialize(&mut ser); 446 | 447 | assert_bytes_eq(ser.as_ref(), b"li1ei2ee"); 448 | } 449 | 450 | #[test] 451 | fn deserialization() { 452 | let value: Value = from_bytes(b"li1ei2ee").unwrap(); 453 | 454 | assert_eq!(value, Value::List(vec![Value::Int(1), Value::Int(2)])); 455 | } 456 | } 457 | } 458 | 459 | mod with_byte_strings { 460 | 461 | mod empty { 462 | use serde::Serialize; 463 | 464 | use crate::{ 465 | from_bytes, 466 | value::{tests::assert_bytes_eq, Value}, 467 | Serializer, 468 | }; 469 | 470 | #[test] 471 | fn serialization() { 472 | let mut ser = Serializer::new(); 473 | 474 | let value = Value::List(vec![Value::Bytes(b"".to_vec())]); 475 | let _unused = value.serialize(&mut ser); 476 | 477 | assert_bytes_eq(ser.as_ref(), b"l0:e"); 478 | } 479 | 480 | #[test] 481 | fn deserialization() { 482 | let value: Value = from_bytes(b"l0:e").unwrap(); 483 | 484 | assert_eq!(value, Value::List(vec![Value::Bytes(b"".to_vec())])); 485 | } 486 | } 487 | 488 | mod one_string { 489 | use serde::Serialize; 490 | 491 | use crate::{ 492 | from_bytes, 493 | value::{tests::assert_bytes_eq, Value}, 494 | Serializer, 495 | }; 496 | 497 | #[test] 498 | fn serialization() { 499 | let mut ser = Serializer::new(); 500 | 501 | let value = Value::List(vec![Value::Bytes(b"spam".to_vec())]); 502 | let _unused = value.serialize(&mut ser); 503 | 504 | // cspell: disable-next-line 505 | assert_bytes_eq(ser.as_ref(), b"l4:spame"); 506 | } 507 | 508 | #[test] 509 | fn deserialization() { 510 | // cspell: disable-next-line 511 | let value: Value = from_bytes(b"l4:spame").unwrap(); 512 | 513 | assert_eq!(value, Value::List(vec![Value::Bytes(b"spam".to_vec())])); 514 | } 515 | } 516 | 517 | mod multiple_strings { 518 | use serde::Serialize; 519 | 520 | use crate::{ 521 | from_bytes, 522 | value::{tests::assert_bytes_eq, Value}, 523 | Serializer, 524 | }; 525 | 526 | #[test] 527 | fn serialization() { 528 | let mut ser = Serializer::new(); 529 | 530 | let value = Value::List(vec![ 531 | Value::Bytes(b"spam1".to_vec()), 532 | Value::Bytes(b"spam1".to_vec()), 533 | ]); 534 | let _unused = value.serialize(&mut ser); 535 | 536 | assert_bytes_eq(ser.as_ref(), b"l5:spam15:spam1e"); 537 | } 538 | 539 | #[test] 540 | fn deserialization() { 541 | let value: Value = from_bytes(b"l5:spam15:spam1e").unwrap(); 542 | 543 | assert_eq!( 544 | value, 545 | Value::List(vec![ 546 | Value::Bytes(b"spam1".to_vec()), 547 | Value::Bytes(b"spam1".to_vec()), 548 | ]) 549 | ); 550 | } 551 | } 552 | } 553 | 554 | mod with_dictionaries { 555 | 556 | mod empty { 557 | 558 | use std::collections::HashMap; 559 | 560 | use serde::Serialize; 561 | 562 | use crate::{ 563 | from_bytes, 564 | value::{tests::assert_bytes_eq, Value}, 565 | Serializer, 566 | }; 567 | 568 | #[test] 569 | fn serialization() { 570 | let mut ser = Serializer::new(); 571 | 572 | let value = Value::List(vec![Value::Dict(HashMap::new())]); 573 | let _unused = value.serialize(&mut ser); 574 | 575 | // cspell: disable-next-line 576 | assert_bytes_eq(ser.as_ref(), b"ldee"); 577 | } 578 | 579 | #[test] 580 | fn deserialization() { 581 | // cspell: disable-next-line 582 | let value: Value = from_bytes(b"ldee").unwrap(); 583 | 584 | assert_eq!(value, Value::List(vec![Value::Dict(HashMap::new())])); 585 | } 586 | } 587 | 588 | mod non_empty { 589 | use std::collections::HashMap; 590 | 591 | use serde::Serialize; 592 | 593 | use crate::{ 594 | from_bytes, 595 | value::{tests::assert_bytes_eq, Value}, 596 | Serializer, 597 | }; 598 | 599 | #[test] 600 | fn serialization() { 601 | let mut ser = Serializer::new(); 602 | 603 | let value = Value::List(vec![Value::Dict(HashMap::from([( 604 | b"key".to_vec(), 605 | Value::Int(3), 606 | )]))]); 607 | let _unused = value.serialize(&mut ser); 608 | 609 | // cspell: disable-next-line 610 | assert_bytes_eq(ser.as_ref(), b"ld3:keyi3eee"); 611 | } 612 | 613 | #[test] 614 | fn deserialization() { 615 | // cspell: disable-next-line 616 | let value: Value = from_bytes(b"ld3:keyi3eee").unwrap(); 617 | 618 | assert_eq!( 619 | value, 620 | Value::List(vec![Value::Dict(HashMap::from([( 621 | b"key".to_vec(), 622 | Value::Int(3), 623 | )]))]) 624 | ); 625 | } 626 | } 627 | } 628 | } 629 | 630 | mod dictionary { 631 | 632 | mod empty { 633 | use std::collections::HashMap; 634 | 635 | use serde::Serialize; 636 | 637 | use crate::{ 638 | from_bytes, 639 | value::{tests::assert_bytes_eq, Value}, 640 | Serializer, 641 | }; 642 | 643 | #[test] 644 | fn serialization() { 645 | let mut ser = Serializer::new(); 646 | 647 | let value = Value::Dict(HashMap::new()); 648 | let _unused = value.serialize(&mut ser); 649 | 650 | assert_bytes_eq(ser.as_ref(), b"de"); 651 | } 652 | 653 | #[test] 654 | fn deserialization() { 655 | let value: Value = from_bytes(b"de").unwrap(); 656 | 657 | assert_eq!(value, Value::Dict(HashMap::new())); 658 | } 659 | } 660 | 661 | mod with_integer_keys { 662 | mod one_key { 663 | use std::collections::HashMap; 664 | 665 | use serde::Serialize; 666 | 667 | use crate::{ 668 | from_bytes, 669 | value::{tests::assert_bytes_eq, Value}, 670 | Serializer, 671 | }; 672 | 673 | #[test] 674 | fn serialization() { 675 | let mut ser = Serializer::new(); 676 | 677 | let value = Value::Dict(HashMap::from([(b"key".to_vec(), Value::Int(3))])); 678 | let _unused = value.serialize(&mut ser); 679 | 680 | // cspell: disable-next-line 681 | assert_bytes_eq(ser.as_ref(), b"d3:keyi3ee"); 682 | } 683 | 684 | #[test] 685 | fn deserialization() { 686 | // cspell: disable-next-line 687 | let value: Value = from_bytes(b"d3:keyi3ee").unwrap(); 688 | 689 | assert_eq!( 690 | value, 691 | Value::Dict(HashMap::from([(b"key".to_vec(), Value::Int(3))])) 692 | ); 693 | } 694 | } 695 | 696 | mod multiple_keys { 697 | use std::collections::HashMap; 698 | 699 | use serde::Serialize; 700 | 701 | use crate::{ 702 | from_bytes, 703 | value::{tests::assert_bytes_eq, Value}, 704 | Serializer, 705 | }; 706 | 707 | #[test] 708 | fn serialization() { 709 | let mut ser = Serializer::new(); 710 | 711 | let value = Value::Dict(HashMap::from([ 712 | (b"key1".to_vec(), Value::Int(1)), 713 | (b"key2".to_vec(), Value::Int(2)), 714 | ])); 715 | let _unused = value.serialize(&mut ser); 716 | 717 | // cspell: disable-next-line 718 | assert_bytes_eq(ser.as_ref(), b"d4:key1i1e4:key2i2ee"); 719 | } 720 | 721 | #[test] 722 | fn deserialization() { 723 | // cspell: disable-next-line 724 | let value: Value = from_bytes(b"d4:key1i1e4:key2i2ee").unwrap(); 725 | 726 | assert_eq!( 727 | value, 728 | Value::Dict(HashMap::from([ 729 | (b"key1".to_vec(), Value::Int(1)), 730 | (b"key2".to_vec(), Value::Int(2)), 731 | ])) 732 | ); 733 | } 734 | } 735 | } 736 | 737 | mod with_byte_string_keys { 738 | mod one_key { 739 | use std::collections::HashMap; 740 | 741 | use serde::Serialize; 742 | 743 | use crate::{ 744 | from_bytes, 745 | value::{tests::assert_bytes_eq, Value}, 746 | Serializer, 747 | }; 748 | 749 | #[test] 750 | fn serialization() { 751 | let mut ser = Serializer::new(); 752 | 753 | let value = Value::Dict(HashMap::from([( 754 | b"key".to_vec(), 755 | Value::Bytes(b"spam".to_vec()), 756 | )])); 757 | let _unused = value.serialize(&mut ser); 758 | 759 | // cspell: disable-next-line 760 | assert_bytes_eq(ser.as_ref(), b"d3:key4:spame"); 761 | } 762 | 763 | #[test] 764 | fn deserialization() { 765 | // cspell: disable-next-line 766 | let value: Value = from_bytes(b"d3:key4:spame").unwrap(); 767 | 768 | assert_eq!( 769 | value, 770 | Value::Dict(HashMap::from([( 771 | b"key".to_vec(), 772 | Value::Bytes(b"spam".to_vec()), 773 | )])) 774 | ); 775 | } 776 | } 777 | 778 | mod multiple_keys { 779 | use std::collections::HashMap; 780 | 781 | use serde::Serialize; 782 | 783 | use crate::{ 784 | from_bytes, 785 | value::{tests::assert_bytes_eq, Value}, 786 | Serializer, 787 | }; 788 | 789 | #[test] 790 | fn serialization() { 791 | let mut ser = Serializer::new(); 792 | 793 | let value = Value::Dict(HashMap::from([ 794 | (b"key1".to_vec(), Value::Bytes(b"spam1".to_vec())), 795 | (b"key2".to_vec(), Value::Bytes(b"spam2".to_vec())), 796 | ])); 797 | let _unused = value.serialize(&mut ser); 798 | 799 | // cspell: disable-next-line 800 | assert_bytes_eq(ser.as_ref(), b"d4:key15:spam14:key25:spam2e"); 801 | } 802 | 803 | #[test] 804 | fn deserialization() { 805 | // cspell: disable-next-line 806 | let value: Value = from_bytes(b"d4:key15:spam14:key25:spam2e").unwrap(); 807 | 808 | assert_eq!( 809 | value, 810 | Value::Dict(HashMap::from([ 811 | (b"key1".to_vec(), Value::Bytes(b"spam1".to_vec())), 812 | (b"key2".to_vec(), Value::Bytes(b"spam2".to_vec())), 813 | ])) 814 | ); 815 | } 816 | } 817 | } 818 | 819 | mod with_list_keys { 820 | mod empty { 821 | use std::collections::HashMap; 822 | 823 | use serde::Serialize; 824 | 825 | use crate::{ 826 | from_bytes, 827 | value::{tests::assert_bytes_eq, Value}, 828 | Serializer, 829 | }; 830 | 831 | #[test] 832 | fn serialization() { 833 | let mut ser = Serializer::new(); 834 | 835 | let value = Value::Dict(HashMap::from([( 836 | b"key".to_vec(), 837 | Value::List(vec![Value::Int(1)]), 838 | )])); 839 | let _unused = value.serialize(&mut ser); 840 | 841 | // cspell: disable-next-line 842 | assert_bytes_eq(ser.as_ref(), b"d3:keyli1eee"); 843 | } 844 | 845 | #[test] 846 | fn deserialization() { 847 | // cspell: disable-next-line 848 | let value: Value = from_bytes(b"d3:keyli1eee").unwrap(); 849 | 850 | assert_eq!( 851 | value, 852 | Value::Dict(HashMap::from([( 853 | b"key".to_vec(), 854 | Value::List(vec![Value::Int(1)]), 855 | )])) 856 | ); 857 | } 858 | } 859 | 860 | mod non_empty {} 861 | } 862 | } 863 | } 864 | } 865 | -------------------------------------------------------------------------------- /tests/fixtures/torrents/with-one-node.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toby/serde-bencode/f36b82c6d528a4f84e7627c6fb06b7e3f4bdb1c4/tests/fixtures/torrents/with-one-node.torrent -------------------------------------------------------------------------------- /tests/fixtures/torrents/with-two-nodes.torrent: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toby/serde-bencode/f36b82c6d528a4f84e7627c6fb06b7e3f4bdb1c4/tests/fixtures/torrents/with-two-nodes.torrent -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | use serde::Serialize; 3 | use serde_bencode::de::{from_bytes, from_str}; 4 | use serde_bencode::error::Result; 5 | use serde_bencode::ser::{to_bytes, to_string, Serializer}; 6 | use serde_bencode::value::Value; 7 | use serde_derive::{Deserialize, Serialize}; 8 | use std::collections::HashMap; 9 | use std::fmt::Debug; 10 | 11 | fn test_value_ser_de>(a: T) { 12 | // Serialize 13 | let a = a.into(); 14 | let mut ser = Serializer::new(); 15 | a.serialize(&mut ser).unwrap(); 16 | println!("bytes: {:?}", String::from_utf8_lossy(ser.as_ref())); 17 | 18 | // Deserialize 19 | let b: Value = from_bytes(ser.as_ref()).unwrap(); 20 | println!("value: {b:?}"); 21 | 22 | assert_eq!(a, b); 23 | } 24 | 25 | fn test_value_de_ser(s: &str) { 26 | let d: Value = from_str(s).unwrap(); 27 | let e = to_string(&d).unwrap(); 28 | assert_eq!(s, e); 29 | } 30 | 31 | #[allow(clippy::needless_pass_by_value)] 32 | fn test_ser_de_eq(a: T) 33 | where 34 | T: Serialize + DeserializeOwned + Debug + Eq, 35 | { 36 | let mut ser = Serializer::new(); 37 | a.serialize(&mut ser).unwrap(); 38 | println!("bytes: {:?}", String::from_utf8_lossy(ser.as_ref())); 39 | let b: T = from_bytes(ser.as_ref()).unwrap(); 40 | assert_eq!(a, b); 41 | } 42 | 43 | #[test] 44 | fn ser_de_int() { 45 | test_value_ser_de(666i64); 46 | } 47 | 48 | #[test] 49 | fn ser_de_string() { 50 | test_value_ser_de("yoda"); 51 | } 52 | 53 | #[test] 54 | fn ser_de_value_list_mixed() { 55 | test_value_ser_de(Value::List(vec![ 56 | "one".into(), 57 | "two".into(), 58 | "three".into(), 59 | 4i64.into(), 60 | ])); 61 | } 62 | 63 | #[test] 64 | fn ser_de_value_list_nested() { 65 | let l_grandchild = Value::List(vec!["two".into()]); 66 | let l_child = Value::List(vec!["one".into(), l_grandchild]); 67 | test_value_ser_de(vec![ 68 | "one".into(), 69 | "two".into(), 70 | "three".into(), 71 | 4i64.into(), 72 | l_child, 73 | ]); 74 | } 75 | 76 | #[test] 77 | fn ser_de_value_map() { 78 | let mut m = HashMap::new(); 79 | m.insert("Mc".into(), "Burger".into()); 80 | test_value_ser_de(m); 81 | } 82 | 83 | #[test] 84 | fn ser_de_map_value_mixed() { 85 | let mut ma = HashMap::new(); 86 | ma.insert("M jr.".into(), "nuggets".into()); 87 | let s = Value::List(vec![ 88 | "one".into(), 89 | "two".into(), 90 | "three".into(), 91 | 4i64.into(), 92 | ]); 93 | let mut m = HashMap::new(); 94 | m.insert("Mc".into(), "Burger".into()); 95 | m.insert("joint".into(), ma.into()); 96 | m.insert("woah".into(), s); 97 | test_value_ser_de(m); 98 | } 99 | 100 | #[test] 101 | fn serialize_i64() { 102 | assert_eq!(to_string(&666i64).unwrap(), "i666e"); 103 | } 104 | 105 | #[test] 106 | fn serialize_str() { 107 | assert_eq!(to_string(&"xxx").unwrap(), "3:xxx"); 108 | } 109 | 110 | #[test] 111 | fn serialize_ascii_char() { 112 | assert_eq!(to_string(&'a').unwrap(), "1:a"); 113 | } 114 | 115 | #[test] 116 | fn serialize_uncode_char() { 117 | assert_eq!(to_string(&'\u{1F9D0}').unwrap(), "4:\u{01F9D0}"); 118 | } 119 | 120 | #[test] 121 | fn serialize_bool() { 122 | assert_eq!(to_string(&false).unwrap(), "i0e"); 123 | assert_eq!(to_string(&true).unwrap(), "i1e"); 124 | } 125 | 126 | #[test] 127 | fn deserialize_to_string() { 128 | let r: String = from_str("3:yes").unwrap(); 129 | assert_eq!(r, "yes"); 130 | } 131 | 132 | #[test] 133 | fn deserialize_to_i64() { 134 | let r: i64 = from_str("i666e").unwrap(); 135 | assert_eq!(r, 666); 136 | } 137 | 138 | #[test] 139 | fn deserialize_to_vec() { 140 | let r: Vec = from_str("li666ee").unwrap(); 141 | assert_eq!(r, [666]); 142 | } 143 | 144 | #[test] 145 | fn deserialize_to_freestyle() { 146 | // cspell:disable-next-line 147 | let s = "li666e4:wontd3:onei666e4:yoyoli69ei89e4:yoyoeee"; 148 | test_value_de_ser(s); 149 | } 150 | 151 | #[test] 152 | #[should_panic(expected = "assertion failed")] 153 | fn trailing_chars() { 154 | let s = "i666ed"; 155 | let r: Result = from_str(s); 156 | assert!(r.is_err()); 157 | } 158 | 159 | #[test] 160 | fn stop_short() { 161 | let s = "3:we"; 162 | let r: Result = from_str(s); 163 | assert!(r.is_err()); 164 | } 165 | 166 | #[test] 167 | fn multi_digit_string_length() { 168 | let s: String = from_str("1:o").unwrap(); 169 | assert_eq!(s, "o"); 170 | 171 | let s: String = from_str("10:oooooooooo").unwrap(); 172 | assert_eq!(s, "oooooooooo"); 173 | 174 | let s: String = from_str("100:oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo").unwrap(); 175 | assert_eq!(s, 176 | "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"); 177 | } 178 | 179 | #[test] 180 | fn serialize_struct() { 181 | #[derive(Debug, Serialize)] 182 | struct Fake { 183 | x: i64, 184 | y: String, 185 | } 186 | let f = Fake { 187 | x: 1111, 188 | y: "dog".to_string(), 189 | }; 190 | assert_eq!(to_string(&f).unwrap(), "d1:xi1111e1:y3:doge"); 191 | } 192 | 193 | #[test] 194 | fn deserialize_to_struct() { 195 | #[derive(PartialEq, Debug, Deserialize)] 196 | struct Fake { 197 | y: String, 198 | x: i64, 199 | } 200 | 201 | let b = "d1:xi1111e1:y3:doge"; 202 | 203 | assert_eq!( 204 | from_str::(b).unwrap(), 205 | Fake { 206 | x: 1111, 207 | y: "dog".to_string(), 208 | } 209 | ); 210 | } 211 | 212 | #[test] 213 | fn deserialize_to_struct_with_option() { 214 | #[derive(PartialEq, Debug, Deserialize)] 215 | struct Fake { 216 | y: String, 217 | x: i64, 218 | #[serde(default)] 219 | z: Option, 220 | #[serde(default)] 221 | a: Option, 222 | } 223 | 224 | let b = "d1:xi1111e1:y3:dog1:z2:yoe"; 225 | 226 | let r: Fake = from_str(b).unwrap(); 227 | 228 | assert_eq!( 229 | r, 230 | Fake { 231 | x: 1111, 232 | y: "dog".to_string(), 233 | z: Some("yo".to_string()), 234 | a: None, 235 | } 236 | ); 237 | } 238 | 239 | #[test] 240 | fn deserialize_to_value() { 241 | let b = "d1:xi1111e1:y3:doge"; 242 | let r: Value = from_str(b).unwrap(); 243 | let mut d = HashMap::new(); 244 | d.insert("x".into(), 1111.into()); 245 | d.insert("y".into(), "dog".into()); 246 | assert_eq!(r, Value::Dict(d)); 247 | } 248 | 249 | #[test] 250 | fn deserialize_to_value_struct_mix() { 251 | #[derive(PartialEq, Debug, Deserialize)] 252 | struct Fake { 253 | y: String, 254 | x: i64, 255 | z: Value, 256 | q: Vec, 257 | } 258 | 259 | let b = "d1:xi1111e1:y3:dog1:zi66e1:qli666eee"; 260 | 261 | let r: Fake = from_str(b).unwrap(); 262 | 263 | assert_eq!( 264 | r, 265 | Fake { 266 | x: 1111, 267 | y: "dog".to_string(), 268 | z: Value::Int(66), 269 | q: vec![666], 270 | } 271 | ); 272 | } 273 | 274 | #[test] 275 | fn ser_de_nested_list_with_mixed_types_in_child_list() { 276 | let nested_list_with_mixed_types = Value::List(vec![ 277 | Value::List(vec![ 278 | Value::Bytes("188.163.121.224".as_bytes().to_vec()), 279 | Value::Int(56711), 280 | ]), 281 | Value::List(vec![ 282 | Value::Bytes("162.250.131.26".as_bytes().to_vec()), 283 | Value::Int(13386), 284 | ]), 285 | ]); 286 | 287 | test_value_ser_de(nested_list_with_mixed_types); 288 | } 289 | 290 | #[test] 291 | fn serialize_nested_list_with_mixed_types_in_child_list() { 292 | let n = vec![ 293 | ("188.163.121.224".to_string(), 56711), 294 | ("162.250.131.26".to_string(), 13386), 295 | ]; 296 | 297 | assert_eq!( 298 | to_string(&n).unwrap(), 299 | "ll15:188.163.121.224i56711eel14:162.250.131.26i13386eee" 300 | ); 301 | } 302 | 303 | #[test] 304 | fn serialize_nested_list_with_integers_in_child_list() { 305 | let n = vec![(56711), (13386)]; 306 | 307 | assert_eq!(to_string(&n).unwrap(), "li56711ei13386ee"); 308 | } 309 | 310 | #[test] 311 | fn serialize_nested_list_with_two_integers_in_child_list() { 312 | let n = vec![(111, 222), (333, 444)]; 313 | 314 | // cspell:disable-next-line 315 | assert_eq!(to_string(&n).unwrap(), "lli111ei222eeli333ei444eee"); 316 | } 317 | 318 | #[test] 319 | fn deserialize_to_list_with_tuples_with_different_types() { 320 | let b = "ll15:188.163.121.224i56711eel14:162.250.131.26i13386eee"; 321 | 322 | let r: Vec<(String, i64)> = from_str(b).unwrap(); 323 | 324 | assert_eq!( 325 | r, 326 | vec![ 327 | ("188.163.121.224".to_string(), 56711), 328 | ("162.250.131.26".to_string(), 13386) 329 | ] 330 | ); 331 | } 332 | 333 | #[test] 334 | fn deserialize_to_list_with_tuple_structs_with_different_types() { 335 | // todo: deserializes only the first element 336 | 337 | #[derive(PartialEq, Debug, Deserialize)] 338 | struct Node(String, i64); 339 | 340 | let b = "ll15:188.163.121.224i56711eel14:162.250.131.26i13386eee"; 341 | 342 | let r: Vec = from_str(b).unwrap(); 343 | 344 | assert_eq!( 345 | r, 346 | vec![ 347 | Node("188.163.121.224".to_string(), 56711), 348 | //Node("162.250.131.26".to_string(), 13386) 349 | ] 350 | ); 351 | } 352 | 353 | #[test] 354 | fn deserialize_to_nested_list_with_integer_list_items() { 355 | // todo: deserializes only the first element 356 | 357 | #[derive(PartialEq, Debug, Deserialize)] 358 | struct Item { 359 | port: i64, 360 | } 361 | 362 | // cspell:disable-next-line 363 | let b = "lli56711eeli13386eee"; 364 | 365 | let r: Vec = from_str(b).unwrap(); 366 | 367 | //assert_eq!(r, vec![Item { port: 56711 }, Item { port: 13386 }]); 368 | assert_eq!(r, vec![Item { port: 56711 }]); 369 | } 370 | 371 | #[test] 372 | fn deserialize_to_nested_list_with_child_lists_with_two_integers() { 373 | // todo: deserializes only the first element 374 | 375 | #[derive(PartialEq, Debug, Deserialize)] 376 | struct Item { 377 | x: i64, 378 | y: i64, 379 | } 380 | 381 | // cspell:disable-next-line 382 | let b = "lli111ei222eeli333ei444eee"; 383 | // "l e"; // parent list 384 | // " li111ei222ee "; // first child list 385 | // " li333ei444ee "; // second child list 386 | // " i111ei222e i333ei444e "; // integers 387 | 388 | let r: Vec = from_str(b).unwrap(); 389 | 390 | //assert_eq!(r, vec![Item { x: 111, y: 222 }, Item { x: 333, y: 444 }]); 391 | assert_eq!(r, vec![Item { x: 111, y: 222 }]); 392 | } 393 | 394 | #[test] 395 | fn serialize_lexical_sorted_keys() { 396 | #[derive(Serialize)] 397 | struct Fake { 398 | aaa: i32, 399 | bb: i32, 400 | z: i32, 401 | c: i32, 402 | } 403 | let f = Fake { 404 | aaa: 1, 405 | bb: 2, 406 | z: 3, 407 | c: 4, 408 | }; 409 | assert_eq!(to_string(&f).unwrap(), "d3:aaai1e2:bbi2e1:ci4e1:zi3ee"); 410 | } 411 | 412 | #[test] 413 | fn serialize_newtype_struct() { 414 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 415 | struct Fake(i32); 416 | let f = Fake(66); 417 | assert_eq!(to_string(&f).unwrap(), "i66e"); 418 | test_ser_de_eq(f); 419 | } 420 | 421 | #[test] 422 | fn serialize_some() { 423 | let f = Some(1); 424 | assert_eq!(to_string(&f).unwrap(), "i1e"); 425 | } 426 | 427 | #[test] 428 | fn serialize_none() { 429 | let f: Option = None; 430 | assert_eq!(to_string(&f).unwrap(), ""); 431 | } 432 | 433 | #[test] 434 | fn serialize_tuple() { 435 | let f = (1, 2, 3, "one"); 436 | assert_eq!(to_string(&f).unwrap(), "li1ei2ei3e3:onee"); 437 | } 438 | 439 | #[test] 440 | fn serialize_tuple_struct() { 441 | #[derive(Serialize)] 442 | struct Fake(i32, i32); 443 | let f = Fake(66, 66); 444 | assert_eq!(to_string(&f).unwrap(), "li66ei66ee"); 445 | } 446 | 447 | #[test] 448 | fn readme_value_example() { 449 | let list: Vec = vec!["one".into(), "two".into(), "three".into(), 4i64.into()]; 450 | assert_eq!(to_string(&list).unwrap(), "l3:one3:two5:threei4ee"); 451 | } 452 | 453 | #[test] 454 | fn struct_none_vals() { 455 | #[derive(Serialize)] 456 | struct Fake { 457 | a: Option, 458 | b: Option, 459 | } 460 | let f = Fake { 461 | a: None, 462 | b: Some(1), 463 | }; 464 | assert_eq!(to_string(&f).unwrap(), "d1:bi1ee"); 465 | } 466 | 467 | #[test] 468 | fn ser_de_variant_unit() { 469 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 470 | enum Mock { 471 | A, 472 | B, 473 | } 474 | test_ser_de_eq(("pre".to_string(), Mock::A, "post".to_string())); 475 | test_ser_de_eq(("pre".to_string(), Mock::B, "post".to_string())); 476 | } 477 | 478 | #[test] 479 | fn ser_de_variant_newtype() { 480 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 481 | enum Mock { 482 | A(i64), 483 | B(i64), 484 | } 485 | test_ser_de_eq(("pre".to_string(), Mock::A(123), "post".to_string())); 486 | test_ser_de_eq(("pre".to_string(), Mock::B(321), "post".to_string())); 487 | } 488 | 489 | #[test] 490 | fn ser_de_variant_tuple() { 491 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 492 | enum Mock { 493 | A(i64, i64), 494 | B(i64, i64), 495 | } 496 | test_ser_de_eq(("pre".to_string(), Mock::A(123, 321), "post".to_string())); 497 | test_ser_de_eq(("pre".to_string(), Mock::B(321, 123), "post".to_string())); 498 | } 499 | 500 | #[test] 501 | fn ser_de_variant_struct() { 502 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 503 | enum Mock { 504 | A { a: i64, b: i64 }, 505 | B { c: i64, d: i64 }, 506 | } 507 | test_ser_de_eq(( 508 | "pre".to_string(), 509 | Mock::A { a: 123, b: 321 }, 510 | "post".to_string(), 511 | )); 512 | test_ser_de_eq(( 513 | "pre".to_string(), 514 | Mock::B { c: 321, d: 123 }, 515 | "post".to_string(), 516 | )); 517 | } 518 | 519 | #[test] 520 | fn test_to_bytes() { 521 | assert_eq!(to_bytes(&"test").unwrap(), b"4:test"); 522 | } 523 | 524 | #[test] 525 | fn ser_de_adjacently_tagged_enum() { 526 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 527 | #[serde(tag = "t", content = "c")] 528 | enum Mock { 529 | A, 530 | B, 531 | } 532 | 533 | test_ser_de_eq(Mock::A); 534 | test_ser_de_eq(Mock::B); 535 | } 536 | 537 | #[test] 538 | #[ignore] 539 | fn ser_de_flattened_adjacently_tagged_enum() { 540 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 541 | struct Message { 542 | id: u64, 543 | #[serde(flatten)] 544 | body: Body, 545 | } 546 | 547 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 548 | #[serde(tag = "type", content = "content")] 549 | enum Body { 550 | Request { token: u64 }, 551 | Response, 552 | } 553 | 554 | test_ser_de_eq(Message { 555 | id: 123, 556 | body: Body::Request { token: 456 }, 557 | }); 558 | } 559 | 560 | // https://github.com/toby/serde-bencode/issues/16 (simplified) 561 | #[test] 562 | fn ser_de_vec_of_tuples() { 563 | test_ser_de_eq(vec![(1, 2), (3, 4)]); 564 | } 565 | 566 | // https://github.com/toby/serde-bencode/issues/17 567 | #[test] 568 | fn ser_de_field_vec_tuple() { 569 | #[derive(Deserialize, Serialize, Eq, PartialEq, Debug)] 570 | struct StructWithVecOfPairs { 571 | bar: Vec<(u16,)>, 572 | } 573 | 574 | let deserialized_struct = StructWithVecOfPairs { 575 | bar: vec![(1,), (3,)], 576 | }; 577 | 578 | test_ser_de_eq(deserialized_struct); 579 | } 580 | 581 | #[test] 582 | #[ignore] 583 | fn ser_de_flattened_enum() { 584 | #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] 585 | struct KrpcMessage { 586 | message_type: MessageType, 587 | } 588 | 589 | #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] 590 | enum MessageType { 591 | Query, 592 | Response, 593 | } 594 | 595 | #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] 596 | struct KrpcResponse { 597 | #[serde(flatten)] 598 | krpc: KrpcMessage, 599 | } 600 | 601 | // Passes 602 | test_ser_de_eq(KrpcMessage { 603 | message_type: MessageType::Response, 604 | }); 605 | 606 | // Fails 607 | test_ser_de_eq(KrpcResponse { 608 | krpc: KrpcMessage { 609 | message_type: MessageType::Response, 610 | }, 611 | }); 612 | } 613 | 614 | #[test] 615 | fn deserialize_too_long_byte_string() { 616 | let _unused: Result = from_str("123456789123:1"); 617 | } 618 | 619 | mod torrent_file { 620 | use serde_bencode::de::{self, from_str}; 621 | use serde_bencode::ser::to_string; 622 | use serde_bytes::ByteBuf; 623 | use serde_derive::{Deserialize, Serialize}; 624 | use std::fmt::Debug; 625 | use std::fs; 626 | use std::io::Read; 627 | 628 | #[test] 629 | fn serialization() { 630 | #[allow(dead_code)] 631 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 632 | struct Torrent { 633 | info: Info, 634 | 635 | #[serde(default)] 636 | nodes: Option>, 637 | } 638 | 639 | #[allow(dead_code)] 640 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 641 | struct Info { 642 | #[serde(default)] 643 | pub length: Option, 644 | 645 | #[serde(default)] 646 | pub name: String, 647 | 648 | #[serde(rename = "piece length")] 649 | pub piece_length: i64, 650 | 651 | #[serde(default)] 652 | pub pieces: ByteBuf, 653 | } 654 | 655 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 656 | struct Node(String, i64); 657 | 658 | let torrent = Torrent { 659 | info: Info { 660 | name: "minimal.txt".to_string(), 661 | pieces: ByteBuf::from(vec![b'p']), 662 | piece_length: 1, 663 | length: Some(8), 664 | }, 665 | nodes: Some(vec![ 666 | Node("188.163.121.224".to_string(), 56711), 667 | Node("162.250.131.26".to_string(), 13386), 668 | ]), 669 | }; 670 | 671 | // cspell:disable-next-line 672 | assert_eq!(to_string(&torrent).unwrap(), "d4:infod6:lengthi8e4:name11:minimal.txt12:piece lengthi1e6:pieces1:pe5:nodesll15:188.163.121.224i56711eel14:162.250.131.26i13386eeee"); 673 | } 674 | 675 | #[test] 676 | fn deserialization() { 677 | // todo: you cannot deserialize to the same struct used in serialization. 678 | // It does not work with a tuple struct `struct Node(String, i64)` 679 | // instead of a tuple `(String, i64)`. 680 | 681 | #[allow(dead_code)] 682 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 683 | struct Torrent { 684 | info: Info, 685 | #[serde(default)] 686 | nodes: Option>, 687 | } 688 | 689 | #[allow(dead_code)] 690 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 691 | struct Info { 692 | #[serde(default)] 693 | pub length: Option, 694 | 695 | #[serde(default)] 696 | pub name: String, 697 | 698 | #[serde(rename = "piece length")] 699 | pub piece_length: i64, 700 | 701 | #[serde(default)] 702 | pub pieces: ByteBuf, 703 | } 704 | 705 | #[derive(PartialEq, Debug, Serialize, Deserialize)] 706 | struct Node(String, i64); 707 | 708 | // cspell:disable-next-line 709 | let b = "d4:infod6:lengthi8e4:name11:minimal.txt12:piece lengthi1e6:pieces1:pe5:nodesll15:188.163.121.224i56711eel14:162.250.131.26i13386eeee"; 710 | 711 | let r: Torrent = from_str(b).unwrap(); 712 | 713 | assert_eq!( 714 | r, 715 | Torrent { 716 | info: Info { 717 | name: "minimal.txt".to_string(), 718 | pieces: ByteBuf::from(vec![b'p']), 719 | piece_length: 1, 720 | length: Some(8), 721 | }, 722 | nodes: Some(vec![ 723 | ("188.163.121.224".to_string(), 56711), 724 | ("162.250.131.26".to_string(), 13386), 725 | ]), 726 | } 727 | ); 728 | } 729 | 730 | #[allow(clippy::too_many_lines)] 731 | #[test] 732 | fn deserialize_full_torrent_fixtures() { 733 | #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] 734 | pub struct Torrent { 735 | pub info: TorrentInfoDictionary, // 736 | #[serde(default)] 737 | pub announce: Option, 738 | #[serde(default)] 739 | pub nodes: Option>, 740 | #[serde(default)] 741 | pub encoding: Option, 742 | #[serde(default)] 743 | pub httpseeds: Option>, 744 | #[serde(default)] 745 | #[serde(rename = "announce-list")] 746 | pub announce_list: Option>>, 747 | #[serde(default)] 748 | #[serde(rename = "creation date")] 749 | pub creation_date: Option, 750 | #[serde(default)] 751 | pub comment: Option, 752 | #[serde(default)] 753 | #[serde(rename = "created by")] 754 | pub created_by: Option, 755 | } 756 | 757 | #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] 758 | pub struct TorrentInfoDictionary { 759 | pub name: String, 760 | #[serde(default)] 761 | pub pieces: ByteBuf, 762 | #[serde(rename = "piece length")] 763 | pub piece_length: i64, 764 | #[serde(default)] 765 | pub md5sum: Option, 766 | #[serde(default)] 767 | pub length: Option, 768 | #[serde(default)] 769 | pub files: Option>, 770 | #[serde(default)] 771 | pub private: Option, 772 | #[serde(default)] 773 | pub path: Option>, 774 | #[serde(default)] 775 | #[serde(rename = "root hash")] 776 | pub root_hash: Option, 777 | #[serde(default)] 778 | pub source: Option, 779 | } 780 | 781 | #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] 782 | pub struct TorrentFile { 783 | pub path: Vec, 784 | pub length: i64, 785 | #[serde(default)] 786 | pub md5sum: Option, 787 | } 788 | 789 | fn render_torrent(torrent: &Torrent) { 790 | println!("announce: {:?}", torrent.announce); 791 | println!("nodes: {:?}", torrent.nodes); 792 | if let Some(al) = &torrent.announce_list { 793 | for a in al { 794 | println!("announce list: {}", a[0]); 795 | } 796 | } 797 | println!("httpseeds: {:?}", torrent.httpseeds); 798 | println!("creation date: {:?}", torrent.creation_date); 799 | println!("comment: {:?}", torrent.comment); 800 | println!("created by: {:?}", torrent.created_by); 801 | println!("encoding: {:?}", torrent.encoding); 802 | 803 | println!("name: {}", torrent.info.name); 804 | println!("pieces length: {}", torrent.info.pieces.len()); 805 | println!("piece length: {:?}", torrent.info.piece_length); 806 | println!("private: {:?}", torrent.info.private); 807 | println!("root hash: {:?}", torrent.info.root_hash); 808 | println!("md5sum: {:?}", torrent.info.md5sum); 809 | println!("length: {:?}", torrent.info.length); 810 | println!("path: {:?}", torrent.info.path); 811 | if let Some(files) = &torrent.info.files { 812 | for f in files { 813 | println!("file path: {:?}", f.path); 814 | println!("file length: {}", f.length); 815 | println!("file md5sum: {:?}", f.md5sum); 816 | } 817 | } 818 | } 819 | 820 | let torrents_dir = "./tests/fixtures/torrents"; 821 | let paths = fs::read_dir(torrents_dir).expect("Failed to read torrents directory"); 822 | 823 | for path in paths { 824 | let path = path.expect("Failed to read path").path(); 825 | if path.extension().unwrap_or_default() == "torrent" { 826 | println!("Parsing torrent file: {path:?}"); 827 | 828 | let mut file = std::fs::File::open(&path).expect("Failed to open torrent file"); 829 | let mut bytes = Vec::new(); 830 | 831 | file.read_to_end(&mut bytes) 832 | .expect("Failed to read torrent file to end"); 833 | 834 | match de::from_bytes::(&bytes) { 835 | Ok(t) => { 836 | render_torrent(&t); 837 | println!(); 838 | } 839 | Err(e) => { 840 | panic!("ERROR: {}", e) 841 | } 842 | } 843 | } 844 | } 845 | } 846 | } 847 | --------------------------------------------------------------------------------