Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ser/de for IpAddr type; schema for Ipv4Addr/Ipv6Addr/IpAddr types #309

Merged
merged 13 commits into from
Oct 8, 2024
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
Loading