Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Bug: nested list deserialization is not working #12

Closed
josecelano opened this issue Sep 25, 2023 · 5 comments
Closed

Bug: nested list deserialization is not working #12

josecelano opened this issue Sep 25, 2023 · 5 comments
Labels
bug Something isn't working

Comments

@josecelano
Copy link
Member

Relates to: torrust/torrust-index#266

There is a failing test:

#[test]
fn deserialize_to_nested_list() {
    // [
    //   [
    //     "188.163.121.224",
    //     56711
    //   ],
    //   [
    //     "162.250.131.26",
    //     13386
    //   ]
    // ]

    #[derive(PartialEq, Debug, Deserialize)]
    struct Item {
        ip: String,
        port: i64,
    }

    let b = "d1:0l15:188.163.121.224i56711ee1:1l14:162.250.131.26i13386eee";

    let r: Vec<Item> = from_str(b).unwrap();

    assert_eq!(
        r,
        vec![
            Item {
                ip: "188.163.121.224".to_string(),
                port: 56711
            },
            Item {
                ip: "162.250.131.26".to_string(),
                port: 13386
            }
        ]
    );
}
@josecelano josecelano added the bug Something isn't working label Sep 25, 2023
@josecelano
Copy link
Member Author

The test was wrong. I used https://chocobo1.github.io/bencode_online/ to generate the encoded data from the nested lists, but it added an extra dictionary, which is not needed.

The new test is the following:

#[test]
fn deserialize_to_nested_list_with_mixed_types_in_child_list() {
    #[derive(PartialEq, Debug, Deserialize)]
    struct Item {
        ip: String,
        port: i64,
    }

    let b = "ll15:188.163.121.224i56711eel14:162.250.131.26i13386eee";

    let r: Vec<Item> = from_str(b).unwrap();

    assert_eq!(
        r,
        vec![
            Item {
                ip: "188.163.121.224".to_string(),
                port: 56711
            },
            Item {
                ip: "162.250.131.26".to_string(),
                port: 13386
            }
        ]
    );
}

It includes the first item in the list but not the second:

---- deserialize_to_nested_list_with_mixed_types_in_child_list stdout ----
thread 'deserialize_to_nested_list_with_mixed_types_in_child_list' panicked at tests/tests.rs:302:5:
assertion `left == right` failed
  left: [Item { ip: "188.163.121.224", port: 56711 }]
 right: [Item { ip: "188.163.121.224", port: 56711 }, Item { ip: "162.250.131.26", port: 13386 }]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

josecelano added a commit that referenced this issue Sep 26, 2023
a7fd343 test: add more failing tests (Jose Celano)

Pull request description:

  Where child lists contain integers.

  Relates to: #12

Top commit has no ACKs.

Tree-SHA512: f65969955f34a34307f38a09ecf7b0c672839564e768a91d9a6e681ac858c467eaf504da97a31ec70c70f7899f52a116f4d8aefd88909362245f55ab73a7b121
@josecelano
Copy link
Member Author

The problem could be that it's not possible to deserialize nested lists into a struct. If that's the case, it should return an error, not only the first element.

It works if we deserialize into an array of tuples like de following:

#[test]
fn deserialize_to_nested_list_tuples() {
    let b = "ll15:188.163.121.224i56711eel14:162.250.131.26i13386eee";

    let r: Vec<(String, i64)> = from_str(b).unwrap();

    assert_eq!(
        r,
        vec![
            ("188.163.121.224".to_string(), 56711),
            ("162.250.131.26".to_string(), 13386)
        ]
    );
}

@josecelano
Copy link
Member Author

I've added more tests for torrent files.

One test is passing using the same torrent fixture that is not working here.

@josecelano
Copy link
Member Author

Finally, it seems that you cannot deserialize tuple structs.

Check the new test:

   #[test]
    fn deserialization() {
        // todo: you cannot deserialize to the same struct used in serialization.
        // It does not work with a tuple struct `struct Node(String, i64)`
        // instead of a tuple `(String, i64)`.

        #[allow(dead_code)]
        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Torrent {
            info: Info,
            #[serde(default)]
            nodes: Option<Vec<(String, i64)>>,
        }

        #[allow(dead_code)]
        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Info {
            #[serde(default)]
            pub length: Option<i64>,

            #[serde(default)]
            pub name: String,

            #[serde(rename = "piece length")]
            pub piece_length: i64,

            #[serde(default)]
            pub pieces: ByteBuf,
        }

        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Node(String, i64);

        // cspell:disable-next-line
        let b = "d4:infod6:lengthi8e4:name11:minimal.txt12:piece lengthi1e6:pieces1:pe5:nodesll15:188.163.121.224i56711eel14:162.250.131.26i13386eeee";

        let r: Torrent = from_str(b).unwrap();

        assert_eq!(
            r,
            Torrent {
                info: Info {
                    name: "minimal.txt".to_string(),
                    pieces: ByteBuf::from(vec![b'p']),
                    piece_length: 1,
                    length: Some(8),
                },
                nodes: Some(vec![
                    ("188.163.121.224".to_string(), 56711),
                    ("162.250.131.26".to_string(), 13386),
                ]),
            }
        );
    }

Although you can serialize them:

    #[test]
    fn serialization() {
        #[allow(dead_code)]
        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Torrent {
            info: Info,

            #[serde(default)]
            nodes: Option<Vec<Node>>,
        }

        #[allow(dead_code)]
        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Info {
            #[serde(default)]
            pub length: Option<i64>,

            #[serde(default)]
            pub name: String,

            #[serde(rename = "piece length")]
            pub piece_length: i64,

            #[serde(default)]
            pub pieces: ByteBuf,
        }

        #[derive(PartialEq, Debug, Serialize, Deserialize)]
        struct Node(String, i64);

        let torrent = Torrent {
            info: Info {
                name: "minimal.txt".to_string(),
                pieces: ByteBuf::from(vec![b'p']),
                piece_length: 1,
                length: Some(8),
            },
            nodes: Some(vec![
                Node("188.163.121.224".to_string(), 56711),
                Node("162.250.131.26".to_string(), 13386),
            ]),
        };

        // cspell:disable-next-line
        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");
    }

Both options:

  • nodes: Option<Vec<Node>>, where Node is struct Node(String, i64)
  • nodes: Option<Vec<(String, i64)>>,

produce the same bencoded, but you can only deserialize the second one. The first one gives you only one item in the parent list (one node).

I am still determining if that's a bug. As bencoded format is ordered, there should be no problem assigning the values to the tuple struct fields (0 and 1). The first value should be assign to the first element in the tuple.

@josecelano
Copy link
Member Author

I'm closing this issue because it's possible to deserialize the nested list. The problem is only when you use tuple structs. I've created a new issue #18, just to define the issue in a more straightforward way now I know the exact problem.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant