Skip to content

Commit

Permalink
feat: ser/de for IpAddr type; schema for Ipv4Addr/Ipv6Addr/`IpA…
Browse files Browse the repository at this point in the history
…ddr` types (#309)

* WIP: add serialization and deserialization for IpAddr type

* WIP: add IpAddr to fuzzer

* add a test

* WIP: add IpAddr to schema

* fix: serializer refers to existing ones

* fixes

* test: roundtrip test for IpAddr

* chore: remove assertion

* ci: fmt

* chore: revert adding implementation for Schema

* feat: add derived schema implementation for ipaddr types

* ci: remove pkg downgrade section

* test: de-macro added rountrip test

---------

Co-authored-by: dj8yf0μl <[email protected]>
  • Loading branch information
willco-1 and dj8yf0μl authored Oct 8, 2024
1 parent f16cd07 commit b416d11
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 14 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ jobs:
run: rustup default ${{ matrix.rust_version }}
- name: print rustc version
run: rustc --version
- name: downgrade `toml_edit`, time`, `toml_datetime` crate to support older Rust toolchain
if: matrix.rust_version == '1.67.0'
run: |
cargo update -p toml_edit --precise 0.21.0
# - name: downgrade `toml_edit`, time`, `toml_datetime` crate to support older Rust toolchain
# if: matrix.rust_version == '1.67.0'
# run: |
# cargo update -p toml_edit --precise 0.21.0
- name: Run tests
run: ./.github/test.sh

Expand Down
8 changes: 4 additions & 4 deletions borsh-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Attribute takes literal string value, which is the syn's [Path] to `borsh` crate
Attribute is optional.
1. If the attribute is not provided, [crate_name](proc_macro_crate::crate_name) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
Expand Down Expand Up @@ -361,7 +361,7 @@ Attribute takes literal string value, which is the syn's [Path] to `borsh` crate
Attribute is optional.
1. If the attribute is not provided, [crate_name](proc_macro_crate::crate_name) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
Expand Down Expand Up @@ -697,7 +697,7 @@ Attribute takes literal string value, which is the syn's [Path] to `borsh` crate
Attribute is optional.
1. If the attribute is not provided, [crate_name](proc_macro_crate::crate_name) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
Expand Down Expand Up @@ -824,7 +824,7 @@ Attribute takes literal string value, which is a comma-separated list of `Parame
It may be used in order to:
1. fix complex cases, when derive hasn't figured out the right bounds on type parameters and
declaration parameters automatically.
declaration parameters automatically.
2. remove parameters, which do not take part in serialization/deserialization, from bounded ones and from declaration parameters.
`ParameterOverride` describes an entry like `order_param => override_type`,
Expand Down
24 changes: 24 additions & 0 deletions borsh/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,30 @@ impl BorshDeserialize for std::net::SocketAddr {
}
}

#[cfg(feature = "std")]
impl BorshDeserialize for std::net::IpAddr {
#[inline]
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
let kind = u8::deserialize_reader(reader)?;
match kind {
0u8 => {
// Deserialize an Ipv4Addr and convert it to IpAddr::V4
let ipv4_addr = std::net::Ipv4Addr::deserialize_reader(reader)?;
Ok(std::net::IpAddr::V4(ipv4_addr))
}
1u8 => {
// Deserialize an Ipv6Addr and convert it to IpAddr::V6
let ipv6_addr = std::net::Ipv6Addr::deserialize_reader(reader)?;
Ok(std::net::IpAddr::V6(ipv6_addr))
}
value => Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid IpAddr variant: {}", value),
)),
}
}
}

#[cfg(feature = "std")]
impl BorshDeserialize for std::net::SocketAddrV4 {
#[inline]
Expand Down
59 changes: 59 additions & 0 deletions borsh/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,62 @@ impl_tuple!(
impl_tuple!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20
);

#[cfg(feature = "std")]
mod id_addr_std_derive_impl {
use crate::BorshSchema as BorshSchemaMacro;

#[derive(BorshSchemaMacro)]
#[borsh(crate = "crate")]
pub struct Ipv4Addr {
octets: [u8; 4],
}

#[derive(BorshSchemaMacro)]
#[borsh(crate = "crate")]
pub struct Ipv6Addr {
octets: [u8; 16],
}

#[derive(BorshSchemaMacro)]
#[borsh(crate = "crate")]
pub enum IpAddr {
/// An IPv4 address.
V4(std::net::Ipv4Addr),
/// An IPv6 address.
V6(std::net::Ipv6Addr),
}
}

#[cfg(feature = "std")]
impl BorshSchema for std::net::Ipv4Addr {
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
<id_addr_std_derive_impl::Ipv4Addr>::add_definitions_recursively(definitions);
}

fn declaration() -> Declaration {
id_addr_std_derive_impl::Ipv4Addr::declaration()
}
}

#[cfg(feature = "std")]
impl BorshSchema for std::net::Ipv6Addr {
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
<id_addr_std_derive_impl::Ipv6Addr>::add_definitions_recursively(definitions);
}

fn declaration() -> Declaration {
id_addr_std_derive_impl::Ipv6Addr::declaration()
}
}

#[cfg(feature = "std")]
impl BorshSchema for std::net::IpAddr {
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
<id_addr_std_derive_impl::IpAddr>::add_definitions_recursively(definitions);
}

fn declaration() -> Declaration {
id_addr_std_derive_impl::IpAddr::declaration()
}
}
16 changes: 16 additions & 0 deletions borsh/src/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,22 @@ impl BorshSerialize for std::net::Ipv6Addr {
}
}

#[cfg(feature = "std")]
impl BorshSerialize for std::net::IpAddr {
#[inline]
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
match self {
std::net::IpAddr::V4(ipv4) => {
writer.write_all(&0u8.to_le_bytes())?;
ipv4.serialize(writer)
}
std::net::IpAddr::V6(ipv6) => {
writer.write_all(&1u8.to_le_bytes())?;
ipv6.serialize(writer)
}
}
}
}
impl<T: BorshSerialize + ?Sized> BorshSerialize for Box<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
self.as_ref().serialize(writer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
source: borsh/tests/roundtrip/test_ip_addr.rs
expression: encoded
---
[
3,
0,
0,
0,
0,
192,
168,
0,
1,
1,
32,
1,
13,
184,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
1,
254,
128,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: borsh/tests/roundtrip/test_ip_addr.rs
expression: encoded
---
[
192,
168,
0,
1,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: borsh/tests/roundtrip/test_ip_addr.rs
expression: encoded
---
[
0,
192,
168,
0,
1,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: borsh/tests/roundtrip/test_ip_addr.rs
expression: encoded
---
[
32,
1,
13,
184,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: borsh/tests/roundtrip/test_ip_addr.rs
expression: encoded
---
[
1,
32,
1,
13,
184,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
]
55 changes: 55 additions & 0 deletions borsh/tests/roundtrip/test_ip_addr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

#[test]
fn test_ipv4_addr_roundtrip_enum() {
let original = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1));
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<IpAddr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}

#[test]
fn test_ipv4_addr_roundtrip() {
let original = Ipv4Addr::new(192, 168, 0, 1);
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Ipv4Addr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}

#[test]
fn test_ipv6_addr_roundtrip_enum() {
let original = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<IpAddr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}

#[test]
fn test_ipv6_addr_roundtrip() {
let original = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Ipv6Addr>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}

#[test]
fn test_ipaddr_vec_roundtrip() {
let original = vec![
IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)),
];
let encoded = borsh::to_vec(&original).expect("Serialization failed");
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);
let decoded = borsh::from_slice::<Vec<IpAddr>>(&encoded).expect("Deserialization failed");
assert_eq!(original, decoded);
}
Loading

0 comments on commit b416d11

Please sign in to comment.