diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cacfb8dac..53d65d66d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/borsh-derive/src/lib.rs b/borsh-derive/src/lib.rs index ae541187b..4ecf8dbfd 100644 --- a/borsh-derive/src/lib.rs +++ b/borsh-derive/src/lib.rs @@ -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 @@ -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 @@ -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 @@ -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`, diff --git a/borsh/src/de/mod.rs b/borsh/src/de/mod.rs index bce4f1267..0abb36e3e 100644 --- a/borsh/src/de/mod.rs +++ b/borsh/src/de/mod.rs @@ -659,6 +659,30 @@ impl BorshDeserialize for std::net::SocketAddr { } } +#[cfg(feature = "std")] +impl BorshDeserialize for std::net::IpAddr { + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + 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] diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index 1fd0059e0..65b5bb1c9 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -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) { + ::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) { + ::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) { + ::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + id_addr_std_derive_impl::IpAddr::declaration() + } +} diff --git a/borsh/src/ser/mod.rs b/borsh/src/ser/mod.rs index bb1980806..b7da5ebd7 100644 --- a/borsh/src/ser/mod.rs +++ b/borsh/src/ser/mod.rs @@ -505,6 +505,22 @@ impl BorshSerialize for std::net::Ipv6Addr { } } +#[cfg(feature = "std")] +impl BorshSerialize for std::net::IpAddr { + #[inline] + fn serialize(&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 BorshSerialize for Box { fn serialize(&self, writer: &mut W) -> Result<()> { self.as_ref().serialize(writer) diff --git a/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipaddr_vec_roundtrip.snap b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipaddr_vec_roundtrip.snap new file mode 100644 index 000000000..457d914ca --- /dev/null +++ b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipaddr_vec_roundtrip.snap @@ -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, +] diff --git a/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip.snap b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip.snap new file mode 100644 index 000000000..3272fbb1c --- /dev/null +++ b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip.snap @@ -0,0 +1,10 @@ +--- +source: borsh/tests/roundtrip/test_ip_addr.rs +expression: encoded +--- +[ + 192, + 168, + 0, + 1, +] diff --git a/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip_enum.snap b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip_enum.snap new file mode 100644 index 000000000..0353f9cb4 --- /dev/null +++ b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv4_addr_roundtrip_enum.snap @@ -0,0 +1,11 @@ +--- +source: borsh/tests/roundtrip/test_ip_addr.rs +expression: encoded +--- +[ + 0, + 192, + 168, + 0, + 1, +] diff --git a/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip.snap b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip.snap new file mode 100644 index 000000000..2ff0ed944 --- /dev/null +++ b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip.snap @@ -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, +] diff --git a/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip_enum.snap b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip_enum.snap new file mode 100644 index 000000000..66e2984bd --- /dev/null +++ b/borsh/tests/roundtrip/snapshots/tests__roundtrip__test_ip_addr__ipv6_addr_roundtrip_enum.snap @@ -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, +] diff --git a/borsh/tests/roundtrip/test_ip_addr.rs b/borsh/tests/roundtrip/test_ip_addr.rs new file mode 100644 index 000000000..3a45dd8eb --- /dev/null +++ b/borsh/tests/roundtrip/test_ip_addr.rs @@ -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::(&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::(&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::(&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::(&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::>(&encoded).expect("Deserialization failed"); + assert_eq!(original, decoded); +} diff --git a/borsh/tests/schema/snapshots/tests__schema__test_ip_addr__ip_addr_schema.snap b/borsh/tests/schema/snapshots/tests__schema__test_ip_addr__ip_addr_schema.snap new file mode 100644 index 000000000..68f0c7109 --- /dev/null +++ b/borsh/tests/schema/snapshots/tests__schema__test_ip_addr__ip_addr_schema.snap @@ -0,0 +1,68 @@ +--- +source: borsh/tests/schema/test_ip_addr.rs +expression: "format!(\"{:#?}\", defs)" +--- +{ + "IpAddr": Enum { + tag_width: 1, + variants: [ + ( + 0, + "V4", + "IpAddrV4", + ), + ( + 1, + "V6", + "IpAddrV6", + ), + ], + }, + "IpAddrV4": Struct { + fields: UnnamedFields( + [ + "Ipv4Addr", + ], + ), + }, + "IpAddrV6": Struct { + fields: UnnamedFields( + [ + "Ipv6Addr", + ], + ), + }, + "Ipv4Addr": Struct { + fields: NamedFields( + [ + ( + "octets", + "[u8; 4]", + ), + ], + ), + }, + "Ipv6Addr": Struct { + fields: NamedFields( + [ + ( + "octets", + "[u8; 16]", + ), + ], + ), + }, + "[u8; 16]": Sequence { + length_width: 0, + length_range: 16..=16, + elements: "u8", + }, + "[u8; 4]": Sequence { + length_width: 0, + length_range: 4..=4, + elements: "u8", + }, + "u8": Primitive( + 1, + ), +} diff --git a/borsh/tests/schema/test_ip_addr.rs b/borsh/tests/schema/test_ip_addr.rs new file mode 100644 index 000000000..297542a92 --- /dev/null +++ b/borsh/tests/schema/test_ip_addr.rs @@ -0,0 +1,11 @@ +use crate::common_macro::schema_imports::*; +use std::net::IpAddr; + +#[test] +fn ip_addr_schema() { + let actual_name = IpAddr::declaration(); + assert_eq!("IpAddr", actual_name); + let mut defs = Default::default(); + IpAddr::add_definitions_recursively(&mut defs); + insta::assert_snapshot!(format!("{:#?}", defs)); +} diff --git a/borsh/tests/tests.rs b/borsh/tests/tests.rs index daaac5ec2..ea8cad5d4 100644 --- a/borsh/tests/tests.rs +++ b/borsh/tests/tests.rs @@ -12,7 +12,7 @@ mod custom_reader { /// this module doesn't contain runnable tests; /// it's included into module tree to ensure derived code doesn't raise compilation -/// errors +/// errors #[rustfmt::skip] #[cfg(feature = "derive")] mod compile_derives { @@ -31,13 +31,15 @@ mod compile_derives { /// These are full roundtrip `BorshSerialize`/`BorshDeserialize` tests #[rustfmt::skip] mod roundtrip { - mod test_strings; + mod test_strings; #[cfg(feature = "ascii")] - mod test_ascii_strings; + mod test_ascii_strings; mod test_arrays; mod test_vecs; mod test_tuple; mod test_primitives; + #[cfg(feature = "std")] + mod test_ip_addr; mod test_nonzero_integers; mod test_range; // mod test_phantom_data; // NOTE: there's nothing corresponding to `schema::test_phantom_data` @@ -59,7 +61,7 @@ mod roundtrip { mod test_generic_enums; mod test_recursive_structs; mod test_recursive_enums; - mod test_serde_with_third_party; + mod test_serde_with_third_party; mod test_enum_discriminants; #[cfg(feature = "bytes")] mod test_ultimate_many_features_combined; @@ -73,12 +75,14 @@ mod roundtrip { #[rustfmt::skip] mod schema { #[cfg(feature = "ascii")] - mod test_ascii_strings; - mod test_strings; + mod test_ascii_strings; + mod test_strings; mod test_arrays; mod test_vecs; mod test_tuple; mod test_primitives; + #[cfg(feature = "std")] + mod test_ip_addr; // mod test_nonzero_integers; // NOTE: there's nothing corresponding to `roundtrip::test_nonzero_integers` mod test_range; mod test_phantom_data;