diff --git a/ci/Cargo.lock.min b/ci/Cargo.lock.min index dca80b16..8532f584 100644 --- a/ci/Cargo.lock.min +++ b/ci/Cargo.lock.min @@ -683,6 +683,7 @@ dependencies = [ "serde_json", "serde_with 3.0.0", "tap", + "test-case", "testcontainers", "testcontainers-modules", "thiserror", @@ -863,6 +864,32 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro-error" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d259aa4825fa1a2371419d30a520219feff9fb3591550a209b4477d2ebaae4f" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.67", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd21889899aa8e1ca2b924c1d3f08086631fc90768225b3268b5d5c3e806a503" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.67", + "syn-mid", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1255,6 +1282,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.67", +] + [[package]] name = "tap" version = "1.0.1" @@ -1281,6 +1319,41 @@ dependencies = [ "redox_termios", ] +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c25e2cb8f5fcd7318157634e8838aa6f7e4715c96637f969fabaccd1ef5462" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "test-case-macros" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37cfd7bbc88a0104e304229fba519bdc45501a30b760fb72240342f1289ad257" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.28", + "test-case-core", +] + [[package]] name = "testcontainers" version = "0.15.0" @@ -1309,18 +1382,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79067843a369b7df0391d33d48636936ee91aa6d6370931eebe1278e6f88a723" +checksum = "a6e37992ba0f05f3aa079be7f453c91b55d15a01a7aad202baa9c6fa48c3e6b5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc6215837a07b345fd86880601a9c55bb5974e0cec507c48bbd3aaa006559a9" +checksum = "0f3308bffe57639095bdb057e73850afecf2730c3b5539af96e6ae096644f4db" dependencies = [ "proc-macro2", "quote", diff --git a/ci/Cargo.lock.msrv b/ci/Cargo.lock.msrv index c0c1b7f5..61c8ebef 100644 --- a/ci/Cargo.lock.msrv +++ b/ci/Cargo.lock.msrv @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -439,9 +439,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -471,9 +471,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "hex" @@ -557,9 +557,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "lock_api" @@ -669,6 +669,7 @@ dependencies = [ "serde_json", "serde_with 3.1.0", "tap", + "test-case", "testcontainers", "testcontainers-modules", "thiserror", @@ -713,7 +714,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.4", "libc", ] @@ -844,9 +845,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1156,9 +1157,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -1224,13 +1225,46 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "test-case-core", +] + [[package]] name = "testcontainers" version = "0.15.0" @@ -1367,9 +1401,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -1411,9 +1445,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", ] @@ -1432,9 +1466,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1442,9 +1476,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -1457,9 +1491,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1467,9 +1501,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -1480,15 +1514,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 1fad86fc..bbbed408 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -26,7 +26,7 @@ neo4rs-macros = { version = "0.3.0", path = "../macros" } paste = "1.0.0" pin-project-lite = "0.2.9" serde = "1.0.0" -thiserror = "1.0.2" +thiserror = "1.0.7" tokio = { version = "1.5.0", features = ["full"] } tokio-rustls = "0.24.0" url = "2.0.0" @@ -40,6 +40,7 @@ serde_bytes = "0.11.0" serde_json = "1.0.0" serde_with = "3.0.0" tap = "1.0.1" +test-case = "3.3.1" testcontainers = "0.15.0" testcontainers-modules = { version = "0.1.4", features = ["neo4j"] } time = { version = "0.3.0", features = ["serde"] } diff --git a/lib/src/bolt/de.rs b/lib/src/bolt/de.rs new file mode 100644 index 00000000..a70f7603 --- /dev/null +++ b/lib/src/bolt/de.rs @@ -0,0 +1,502 @@ +use std::{ + fmt, + marker::PhantomData, + ops::{BitOr, BitOrAssign}, +}; + +use bytes::{Buf, Bytes}; +use serde::{ + de::{ + self, value::SeqDeserializer, DeserializeSeed, EnumAccess, IntoDeserializer as _, + MapAccess, SeqAccess, VariantAccess, Visitor, + }, + forward_to_deserialize_any, +}; + +pub(super) struct Deserializer<'a> { + bytes: &'a mut Bytes, +} + +impl<'a: 'de, 'de> de::Deserializer<'de> for Deserializer<'a> { + type Error = Error; + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 str + string newtype_struct tuple_struct ignored_any + map unit_struct struct enum identifier + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.parse_next_item(Visitation::default(), visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.parse_next_item(Visitation::MAP_AS_SEQ, visitor) + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_seq(ItemsParser::new(len, self.bytes)) + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + struct U32ToCharVisitor { + visitor: V, + } + + impl<'de, V: Visitor<'de>> U32ToCharVisitor { + fn visit, E: de::Error>(self, value: T) -> Result { + let char = value + .try_into() + .ok() + .and_then(char::from_u32) + .ok_or_else(|| { + de::Error::invalid_value( + de::Unexpected::Other("u32"), + &"a valid unicode code point", + ) + })?; + self.visitor.visit_char(char) + } + } + + impl<'de, V: Visitor<'de>> Visitor<'de> for U32ToCharVisitor { + type Value = V::Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a u32") + } + + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + self.visit(value) + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + self.visit(value) + } + } + + self.deserialize_u32(U32ToCharVisitor { visitor }) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.parse_next_item(Visitation::BYTES_AS_BYTES, visitor) + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.bytes.is_empty() { + visitor.visit_none() + } else if self.bytes[0] == 0xC0 { + self.bytes.advance(1); + visitor.visit_none() + } else { + visitor.visit_some(self) + } + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if !self.bytes.is_empty() && self.bytes[0] == 0xC0 { + self.bytes.advance(1); + } + + visitor.visit_unit() + } +} + +impl<'de> Deserializer<'de> { + pub(super) fn new(bytes: &'de mut Bytes) -> Self { + Self { bytes } + } + + fn parse_next_item>( + self, + v: Visitation, + visitor: V, + ) -> Result { + if self.bytes.is_empty() { + return Err(Error::Empty); + } + + Self::parse(v, self.bytes, visitor) + } + + fn parse>( + v: Visitation, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + let marker = bytes.get_u8(); + + let (hi, lo) = (marker >> 4, marker & 0x0F); + + match hi { + 0x8 => Self::parse_string(lo as _, bytes, visitor), + 0x9 => Self::parse_list(lo as _, bytes, visitor), + 0xA => Self::parse_map(v, lo as _, bytes, visitor), + 0xB => Self::parse_struct(lo as _, bytes, visitor), + 0xC => match lo { + 0x0 => visitor.visit_unit(), + 0x1 => visitor.visit_f64(bytes.get_f64()), + 0x2 => visitor.visit_bool(false), + 0x3 => visitor.visit_bool(true), + 0x8 => visitor.visit_i8(bytes.get_i8()), + 0x9 => visitor.visit_i16(bytes.get_i16()), + 0xA => visitor.visit_i32(bytes.get_i32()), + 0xB => visitor.visit_i64(bytes.get_i64()), + 0xC => Self::parse_bytes(v, bytes.get_u8() as _, bytes, visitor), + 0xD => Self::parse_bytes(v, bytes.get_u16() as _, bytes, visitor), + 0xE => Self::parse_bytes(v, bytes.get_u32() as _, bytes, visitor), + _ => Err(Error::UnknownMarker(marker)), + }, + 0xD => match lo { + 0x0 => Self::parse_string(bytes.get_u8() as _, bytes, visitor), + 0x1 => Self::parse_string(bytes.get_u16() as _, bytes, visitor), + 0x2 => Self::parse_string(bytes.get_u32() as _, bytes, visitor), + 0x4 => Self::parse_list(bytes.get_u8() as _, bytes, visitor), + 0x5 => Self::parse_list(bytes.get_u16() as _, bytes, visitor), + 0x6 => Self::parse_list(bytes.get_u32() as _, bytes, visitor), + 0x8 => Self::parse_map(v, bytes.get_u8() as _, bytes, visitor), + 0x9 => Self::parse_map(v, bytes.get_u16() as _, bytes, visitor), + 0xA => Self::parse_map(v, bytes.get_u32() as _, bytes, visitor), + // C, D => struct 8/16 + _ => Err(Error::UnknownMarker(marker)), + }, + 0xE => Err(Error::UnknownMarker(marker)), + _ => visitor.visit_i8(marker as i8), + } + } + + fn parse_bytes>( + v: Visitation, + len: usize, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + debug_assert!(bytes.len() >= len); + + let bytes = bytes.split_to(len); + if v.visit_bytes_as_bytes() { + visitor.visit_bytes(&bytes) + } else { + visitor.visit_seq(SeqDeserializer::new(bytes.into_iter())) + } + } + + fn parse_string>( + len: usize, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + debug_assert!(bytes.len() >= len); + + let bytes = bytes.split_to(len); + + match std::str::from_utf8(&bytes) { + Ok(s) => visitor.visit_str(s), + Err(e) => Err(Error::InvalidUtf8(e)), + } + } + + fn parse_list>( + len: usize, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + visitor.visit_seq(ItemsParser::new(len, bytes)) + } + + fn parse_map>( + v: Visitation, + len: usize, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + if v.visit_map_as_seq() { + visitor.visit_seq(ItemsParser::new(len, bytes)) + } else { + visitor.visit_map(ItemsParser::new(len, bytes)) + } + } + + fn parse_struct>( + len: usize, + bytes: &mut Bytes, + visitor: V, + ) -> Result { + let tag = bytes.get_u8(); + visitor.visit_enum(StructParser { + tag, + items: ItemsParser::new(len, bytes), + }) + } +} + +struct ItemsParser<'a> { + len: usize, + bytes: SharedBytes<'a>, +} + +impl<'a> ItemsParser<'a> { + fn new(len: usize, bytes: &'a mut Bytes) -> Self { + Self { + len, + bytes: SharedBytes::new(bytes), + } + } +} + +impl<'a, 'de> SeqAccess<'de> for ItemsParser<'a> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeSeed<'de>, + { + if self.len == 0 { + return Ok(None); + } + self.len -= 1; + + let bytes = self.bytes.get(); + seed.deserialize(Deserializer { bytes }).map(Some) + } +} + +impl<'a, 'de> MapAccess<'de> for ItemsParser<'a> { + type Error = Error; + + fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> + where + K: DeserializeSeed<'de>, + { + if self.len == 0 { + return Ok(None); + } + self.len -= 1; + + let bytes = self.bytes.get(); + seed.deserialize(Deserializer { bytes }).map(Some) + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + let bytes = self.bytes.get(); + seed.deserialize(Deserializer { bytes }) + } +} + +impl<'a, 'de> VariantAccess<'de> for ItemsParser<'a> { + type Error = Error; + + fn unit_variant(mut self) -> Result<(), Self::Error> { + self.next_value() + // if self.len != 0 { + // return Err(Error::InvalidLength { + // expected: 0, + // actual: self.len, + // }); + // } + // Ok(()) + } + + fn newtype_variant_seed(mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + self.next_value_seed(seed) + // let bytes = self.bytes.get(); + // seed.deserialize(Deserializer { bytes }) + } + + fn tuple_variant(mut self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + struct TupleVariant { + len: usize, + visitor: V, + } + + impl<'de, V> DeserializeSeed<'de> for TupleVariant + where + V: Visitor<'de>, + { + type Value = V::Value; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_tuple(self.len, self.visitor) + } + } + + self.next_value_seed(TupleVariant { len, visitor }) + + // if len != self.len { + // return Err(Error::InvalidLength { + // expected: len, + // actual: self.len, + // }); + // } + // visitor.visit_seq(self) + } + + fn struct_variant( + mut self, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + struct StructVariant { + visitor: V, + } + + impl<'de, V> DeserializeSeed<'de> for StructVariant + where + V: Visitor<'de>, + { + type Value = V::Value; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(self.visitor) + } + } + + self.next_value_seed(StructVariant { visitor }) + + // visitor.visit_map(self) + } +} + +struct StructParser<'a> { + tag: u8, + items: ItemsParser<'a>, +} + +impl<'a, 'de> EnumAccess<'de> for StructParser<'a> { + type Error = Error; + + type Variant = ItemsParser<'a>; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>, + { + seed.deserialize(self.tag.into_deserializer()) + .map(|o| (o, self.items)) + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +struct Visitation(u8); + +impl Visitation { + const BYTES_AS_BYTES: Self = Self(1); + const MAP_AS_SEQ: Self = Self(2); + + fn visit_bytes_as_bytes(self) -> bool { + self.0 & Self::BYTES_AS_BYTES.0 != 0 + } + + fn visit_map_as_seq(self) -> bool { + self.0 & Self::MAP_AS_SEQ.0 != 0 + } +} + +impl BitOr for Visitation { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl BitOrAssign for Visitation { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +struct SharedBytes<'a> { + bytes: *mut Bytes, + _lifetime: PhantomData<&'a mut ()>, +} + +impl<'a> SharedBytes<'a> { + fn new(bytes: &'a mut Bytes) -> Self { + Self { + bytes, + _lifetime: PhantomData, + } + } + + fn get<'x>(&mut self) -> &'x mut Bytes { + unsafe { &mut *self.bytes } + } +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum Error { + #[error("Not enough data to parse a bolt stream.")] + Empty, + + #[error("Unknown marker: {0:02X}")] + UnknownMarker(u8), + + #[error("The bytes do no contain valid UTF-8 to produce a string: {0}")] + InvalidUtf8(#[source] std::str::Utf8Error), + + // TODO: copy DeError + #[error("Deserialization error: {0}")] + DeserializationError(String), +} + +impl de::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Self::DeserializationError(msg.to_string()) + } +} diff --git a/lib/src/bolt/mod.rs b/lib/src/bolt/mod.rs new file mode 100644 index 00000000..0d034aa7 --- /dev/null +++ b/lib/src/bolt/mod.rs @@ -0,0 +1,32 @@ +use bytes::Bytes; +use serde::{de::DeserializeOwned, Serialize}; + +mod packstream; + +pub use packstream::{de, ser}; + +pub(crate) trait Message: Serialize { + /// Serialize this type into a packstream encoded byte slice. + fn to_bytes(&self) -> Result; +} + +impl Message for T { + fn to_bytes(&self) -> Result { + packstream::to_bytes(self) + } +} + +pub(crate) trait MessageResponse: DeserializeOwned { + /// Deserialize this type from a packstream encoded byte slice. + fn parse(bytes: Bytes) -> Result; +} + +impl MessageResponse for T { + fn parse(bytes: Bytes) -> Result { + packstream::from_bytes(bytes) + } +} + +pub(crate) trait ExpectedResponse { + type Response: MessageResponse; +} diff --git a/lib/src/bolt/packstream.rs b/lib/src/bolt/packstream.rs new file mode 100644 index 00000000..c427c22b --- /dev/null +++ b/lib/src/bolt/packstream.rs @@ -0,0 +1,210 @@ +use bytes::Bytes; +use serde::de::{Deserialize, DeserializeOwned}; + +#[path = "de.rs"] +pub mod de; +#[path = "ser.rs"] +pub mod ser; + +/// Parse and deserialize a packstream value from the given bytes. +pub fn from_bytes(mut bytes: Bytes) -> Result +where + T: DeserializeOwned, +{ + from_bytes_ref(&mut bytes) +} + +/// Parse and deserialize a packstream value from the given bytes. +pub fn from_bytes_ref<'de, T>(bytes: &'de mut Bytes) -> Result +where + T: Deserialize<'de>, +{ + let de = de::Deserializer::new(bytes); + let value = T::deserialize(de)?; + + Ok(value) +} + +/// Serialize and packstream encode the given value. +pub fn to_bytes(value: &T) -> Result +where + T: serde::Serialize, +{ + let mut ser = ser::Serializer::empty(); + value.serialize(&mut ser)?; + + Ok(ser.end()) +} + +#[cfg(test)] +mod tests { + use std::{collections::BTreeMap, fmt::Debug}; + + use super::*; + + use serde::{Deserialize, Serialize}; + use test_case::test_case; + + #[test] + fn unit() { + roundtrip(&[], ()); + } + + #[test] + fn null() { + roundtrip(&[0xC0], None::); + } + + #[test_case(0xC2, false ; "false value")] + #[test_case(0xC3, true ; "true value")] + fn boolean(byte: u8, expected: bool) { + roundtrip(&[byte], expected); + } + + #[test_case(&[0x2A], 42; "tiny_int")] + #[test_case(&[0xF4], -12; "neg_tiny_int")] + #[test_case(&[0xC8, 0xD6], -42; "int_8")] + #[test_case(&[0xC9, 0x00, 0xD6], 214; "int_16_u8")] + #[test_case(&[0xC9, 0x05, 0x39], 1337; "int_16_i16")] + #[test_case(&[0xC9, 0xFA, 0xC7], -1337; "neg_int_16")] + #[test_case(&[0xCA, 0x00, 0x00, 0xA4, 0x64], 42084; "int_32_u16")] + #[test_case(&[0xCA, 0x00, 0x40, 0x1B, 0x79], 4201337; "int_32_i32")] + #[test_case(&[0xCA, 0xFF, 0xBF, 0xE4, 0x87], -4201337; "neg_int_32")] + #[test_case(&[0xCB, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x97, 0x05, 0x79], 4204201337; "int_64_u32")] + #[test_case(&[0xCB, 0x00, 0x00, 0x00, 0x03, 0x1c, 0xf0, 0x6c, 0xc4], 13370420420; "int_64_i64")] + #[test_case(&[0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x68, 0xFA, 0x87], -4204201337; "neg_int_64")] + fn int(input: &'static [u8], expected: i64) { + roundtrip(input, expected); + } + + #[test_case(&[0x2A]; "tiny_int")] + #[test_case(&[0xC8, 0x2A]; "int_8")] + #[test_case(&[0xC9, 0x00, 0x2A]; "int_16")] + #[test_case(&[0xCA, 0x00, 0x00, 0x00, 0x2A]; "int_32")] + #[test_case(&[0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A]; "int_64")] + fn int_42(input: &'static [u8]) { + let bytes = Bytes::from_static(input); + assert_eq!(from_bytes::(bytes).unwrap(), 42); + } + + #[test_case(&[0xC1, 0x3F, 0xF3, 0xAE, 0x14, 0x7A, 0xE1, 0x47, 0xAE], 1.23_f64; "float_f64")] + #[test_case(&[0xC1, 0x3F, 0xF3, 0xAE, 0x14, 0x80, 0x00, 0x00, 0x00], 1.23_f32 as f64; "float_f32")] + fn float(input: &'static [u8], expected: f64) { + roundtrip(input, expected) + } + + #[test_case(&[0xCC, 0x00], Bytes::new(); "empty bytes")] + #[test_case(&[0xCC, 0x03, 0x01, 0x02, 0x03], Bytes::from_static(&[1, 2, 3]); "123")] + #[test_case(&[0xCC, 0x0C, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x72, 0x75, 0x73, 0x74, 0x21], Bytes::from_static(b"hello, rust!"); "hell")] + fn bytes(input: &'static [u8], expected: Bytes) { + roundtrip(input, expected) + } + + #[test_case(&[0xCC, 0x00], Bytes::new(); "empty bytes")] + #[test_case(&[0xCC, 0x04, 0x68, 0x65, 0x6C, 0x6C], Bytes::from_static(b"hell"); "hell")] + #[test_case(&[0xCD, 0x00, 0x04, 0x6F, 0x2C, 0x20, 0x72], Bytes::from_static(b"o, r"); "o, r")] + #[test_case(&[0xCE, 0x00, 0x00, 0x00, 0x04, 0x75, 0x73, 0x74, 0x21], Bytes::from_static(b"ust!"); "ust!")] + fn bytes_parse(input: &'static [u8], expected: Bytes) { + let bytes = Bytes::from_static(input); + assert_eq!(from_bytes::(bytes).unwrap(), expected); + } + + #[test_case(&[0xCC, 0x00], Vec::new(); "empty bytes")] + #[test_case(&[0xCC, 0x04, 0x68, 0x65, 0x6C, 0x6C], Vec::from(b"hell".as_slice()); "hell")] + #[test_case(&[0xCD, 0x00, 0x04, 0x6F, 0x2C, 0x20, 0x72], Vec::from(b"o, r".as_slice()); "o, r")] + #[test_case(&[0xCE, 0x00, 0x00, 0x00, 0x04, 0x75, 0x73, 0x74, 0x21], Vec::from(b"ust!".as_slice()); "ust!")] + fn bytes_vec(input: &'static [u8], expected: Vec) { + let bytes = Bytes::from_static(input); + assert_eq!(from_bytes::>(bytes).unwrap(), expected); + } + + #[test_case(&[0x80], ""; "empty string")] + #[test_case(&[0x81, 0x41], "A"; "A")] + #[test_case(&[0xD0, 0x1A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A], "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; "alphabet")] + #[test_case(&[0xD0, 0x12, 0x47, 0x72, 0xC3, 0xB6, 0xC3, 0x9F, 0x65, 0x6E, 0x6D, 0x61, 0xC3, 0x9F, 0x73, 0x74, 0xC3, 0xA4, 0x62, 0x65],"Größenmaßstäbe"; "umlauts")] + #[test_case(&[0xD0, 0x19, 0xF0, 0x9F, 0x91, 0xA9, 0xE2, 0x80, 0x8D, 0xF0, 0x9F, 0x91, 0xA9, 0xE2, 0x80, 0x8D, 0xF0, 0x9F, 0x91, 0xA7, 0xE2, 0x80, 0x8D, 0xF0, 0x9F, 0x91, 0xA7], "👩‍👩‍👧‍👧"; "emojis")] + fn string(input: &'static [u8], expected: &str) { + roundtrip(input, expected.to_owned()) + } + + #[test_case(&[0x81, 0x41], "A"; "tiny")] + #[test_case(&[0xD0, 0x01, 0x41], "A"; "string_8")] + #[test_case(&[0xD1, 0x00, 0x01, 0x41], "A"; "string_16")] + #[test_case(&[0xD2, 0x00, 0x00, 0x00, 0x01, 0x41], "A"; "string_32")] + fn string_parse(input: &'static [u8], expected: &str) { + let bytes = Bytes::copy_from_slice(input); + assert_eq!(from_bytes::(bytes).unwrap(), expected); + } + + #[test_case(&[0x90], Vec::new(); "empty list")] + #[test_case(&[0x93, 0x01, 0x02, 0x03], vec![1, 2, 3]; "list_2")] + #[test_case(&[0xD4, 0x28, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28], (1..=40).collect(); "list_3")] + fn list(input: &'static [u8], expected: Vec) { + roundtrip(input, expected) + } + + #[test_case(&[0xA0], BTreeMap::new(); "empty")] + #[test_case(&[0xA1, 0x83, 0x6F, 0x6E, 0x65, 0x2A], [("one".to_owned(), 42)].into_iter().collect(); "tiny")] + #[test_case(&[0xD8, 0x1A, 0x81, 0x41, 0x01, 0x81, 0x42, 0x02, 0x81, 0x43, 0x03, 0x81, 0x44, 0x04, 0x81, 0x45, 0x05, 0x81, 0x46, 0x06, 0x81, 0x47, 0x07, 0x81, 0x48, 0x08, 0x81, 0x49, 0x09, 0x81, 0x4A, 0x0A, 0x81, 0x4B, 0x0B, 0x81, 0x4C, 0x0C, 0x81, 0x4D, 0x0D, 0x81, 0x4E, 0x0E, 0x81, 0x4F, 0x0F, 0x81, 0x50, 0x10, 0x81, 0x51, 0x11, 0x81, 0x52, 0x12, 0x81, 0x53, 0x13, 0x81, 0x54, 0x14, 0x81, 0x55, 0x15, 0x81, 0x56, 0x16, 0x81, 0x57, 0x17, 0x81, 0x58, 0x18, 0x81, 0x59, 0x19, 0x81, 0x5A, 0x1A], ('A'..='Z').map(|c| (c.to_string(), ((c as u32) - ('@' as u32)) as i64)).collect(); "map_8")] + fn dictionary(input: &'static [u8], expected: BTreeMap) { + roundtrip(input, expected) + } + + #[test_case(&[0xA0], Vec::new(); "empty")] + #[test_case(&[0xA1, 0x83, 0x6F, 0x6E, 0x65, 0x2A], [("one".to_owned(), 42)].into_iter().collect(); "tiny")] + #[test_case(&[0xD8, 0x1A, 0x81, 0x41, 0x01, 0x81, 0x42, 0x02, 0x81, 0x43, 0x03, 0x81, 0x44, 0x04, 0x81, 0x45, 0x05, 0x81, 0x46, 0x06, 0x81, 0x47, 0x07, 0x81, 0x48, 0x08, 0x81, 0x49, 0x09, 0x81, 0x4A, 0x0A, 0x81, 0x4B, 0x0B, 0x81, 0x4C, 0x0C, 0x81, 0x4D, 0x0D, 0x81, 0x4E, 0x0E, 0x81, 0x4F, 0x0F, 0x81, 0x50, 0x10, 0x81, 0x51, 0x11, 0x81, 0x52, 0x12, 0x81, 0x53, 0x13, 0x81, 0x54, 0x14, 0x81, 0x55, 0x15, 0x81, 0x56, 0x16, 0x81, 0x57, 0x17, 0x81, 0x58, 0x18, 0x81, 0x59, 0x19, 0x81, 0x5A, 0x1A], ('A'..='Z').map(|c| (c.to_string(), ((c as u32) - ('@' as u32)) as i64)).collect(); "map_8")] + fn dictionary_vec(input: &'static [u8], expected: Vec<(String, i64)>) { + let bytes = Bytes::copy_from_slice(input); + assert_eq!(from_bytes::>(bytes).unwrap(), expected); + } + + #[test] + fn dictionary_duplicates() { + let bytes = Bytes::from_static(&[ + 0xA3, 0x85, 0x6B, 0x65, 0x79, 0x5F, 0x31, 0x01, 0x85, 0x6B, 0x65, 0x79, 0x5F, 0x32, + 0x02, 0x85, 0x6B, 0x65, 0x79, 0x5F, 0x31, 0x03, + ]); + + let expected = [("key_1".to_owned(), 3), ("key_2".to_owned(), 2)] + .into_iter() + .collect(); + + assert_eq!( + from_bytes::>(bytes.clone()).unwrap(), + expected + ); + + let expected = vec![ + ("key_1".to_owned(), 1), + ("key_2".to_owned(), 2), + ("key_1".to_owned(), 3), + ]; + + assert_eq!(from_bytes::>(bytes).unwrap(), expected); + } + + #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] + enum Structure { + Unit, + Int(i64), + Pair(bool, String), + List(Vec), + } + + #[test_case(&[0xB0, 0x00], Structure::Unit; "unit")] + #[test_case(&[0xB1, 0x01, 0x2A], Structure::Int(42); "int")] + #[test_case(&[0xB2, 0x02, 0xC2, 0x81, 0x42], Structure::Pair(false, "B".to_string()); "pair")] + #[test_case(&[0xB1, 0x03, 0x91, 0x2A], Structure::List(vec![42]); "list")] + fn structure(input: &'static [u8], expected: Structure) { + roundtrip(input, expected) + } + + fn roundtrip(input: &[u8], expected: T) { + let bytes = Bytes::copy_from_slice(input); + assert_eq!(from_bytes::(bytes).unwrap(), expected); + + let actual = to_bytes(&expected).unwrap(); + let expected = Bytes::copy_from_slice(input); + assert_eq!(actual, expected); + } +} diff --git a/lib/src/bolt/ser.rs b/lib/src/bolt/ser.rs new file mode 100644 index 00000000..c7612ad1 --- /dev/null +++ b/lib/src/bolt/ser.rs @@ -0,0 +1,660 @@ +use std::fmt; + +use bytes::{BufMut, Bytes, BytesMut}; +use serde::ser::{self, Impossible, SerializeTupleVariant}; + +pub(super) struct Serializer { + bytes: BytesMut, +} + +impl Serializer { + pub(super) fn empty() -> Self { + Self::new(BytesMut::new()) + } + + pub(super) fn new(bytes: BytesMut) -> Self { + Self { bytes } + } + + pub(super) fn end(self) -> Bytes { + self.bytes.freeze() + } +} + +impl<'a> ser::Serializer for &'a mut Serializer { + type Ok = (); + type Error = Error; + + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + + fn serialize_bool(self, v: bool) -> Result { + self.bytes.reserve(1); + self.bytes.put_u8(0xC2 + (u8::from(v))); + Ok(()) + } + + fn serialize_i8(self, v: i8) -> Result { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i16(self, v: i16) -> Result { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i32(self, v: i32) -> Result { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i64(self, v: i64) -> Result { + match v { + -16..=127 => { + self.bytes.reserve(1); + self.bytes.put_i8(v as i8); + } + -128..=-17 => { + self.bytes.reserve(2); + self.bytes.put_u8(0xC8); + self.bytes.put_i8(v as i8); + } + -32768..=32767 => { + self.bytes.reserve(3); + self.bytes.put_u8(0xC9); + self.bytes.put_i16(v as i16); + } + -2147483648..=2147483647 => { + self.bytes.reserve(5); + self.bytes.put_u8(0xCA); + self.bytes.put_i32(v as i32); + } + _ => { + self.bytes.reserve(9); + self.bytes.put_u8(0xCB); + self.bytes.put_i64(v); + } + }; + Ok(()) + } + + fn serialize_u8(self, v: u8) -> Result { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u16(self, v: u16) -> Result { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u32(self, v: u32) -> Result { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u64(self, v: u64) -> Result { + self.serialize_i64(v as i64) + } + + fn serialize_f32(self, v: f32) -> Result { + self.serialize_f64(f64::from(v)) + } + + fn serialize_f64(self, v: f64) -> Result { + self.bytes.reserve(9); + self.bytes.put_u8(0xC1); + self.bytes.put_f64(v); + Ok(()) + } + + fn serialize_char(self, v: char) -> Result { + self.serialize_u32(u32::from(v)) + } + + fn serialize_str(self, v: &str) -> Result { + let len = v.len(); + match len { + 0..=15 => { + self.bytes.reserve(1 + len); + self.bytes.put_u8(0x80 | len as u8); + } + 16..=255 => { + self.bytes.reserve(2 + len); + self.bytes.put_u8(0xD0); + self.bytes.put_u8(len as u8); + } + 256..=65_535 => { + self.bytes.reserve(3 + len); + self.bytes.put_u8(0xD1); + self.bytes.put_u16(len as u16); + } + 65_536..=2_147_483_647 => { + self.bytes.reserve(5 + len); + self.bytes.put_u8(0xD2); + self.bytes.put_u32(len as u32); + } + _ => return Err(Error::LengthOutOfBounds(len)), + }; + self.bytes.put_slice(v.as_bytes()); + Ok(()) + } + + fn serialize_bytes(self, v: &[u8]) -> Result { + let len = v.len(); + match len { + 0..=255 => { + self.bytes.reserve(2 + len); + self.bytes.put_u8(0xCC); + self.bytes.put_u8(len as u8); + } + 256..=65_535 => { + self.bytes.reserve(3 + len); + self.bytes.put_u8(0xCD); + self.bytes.put_u16(len as u16); + } + 65_536..=2_147_483_647 => { + self.bytes.reserve(5 + len); + self.bytes.put_u8(0xCE); + self.bytes.put_u32(len as u32); + } + _ => return Err(Error::LengthOutOfBounds(len)), + }; + self.bytes.put_slice(v); + Ok(()) + } + + fn serialize_none(self) -> Result { + self.bytes.reserve(1); + self.bytes.put_u8(0xC0); + Ok(()) + } + + fn serialize_some(self, value: &T) -> Result + where + T: ser::Serialize, + { + value.serialize(self) + } + + fn serialize_unit(self) -> Result { + Ok(()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Ok(()) + } + + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_tuple_variant(name, variant_index, variant, 0)? + .end() + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ser::Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ser::Serialize, + { + let mut s = self.serialize_tuple_variant(name, variant_index, variant, 1)?; + (&mut s).serialize_field(value)?; + s.end() + } + + fn serialize_seq(self, len: Option) -> Result { + let len = len.ok_or(Error::UnknownLength)?; + self.serialize_tuple(len) + } + + fn serialize_tuple(self, len: usize) -> Result { + match len { + 0..=15 => { + self.bytes.reserve(1); + self.bytes.put_u8(0x90 | len as u8); + } + 16..=255 => { + self.bytes.reserve(2); + self.bytes.put_u8(0xD4); + self.bytes.put_u8(len as u8); + } + 256..=65_535 => { + self.bytes.reserve(3); + self.bytes.put_u8(0xD5); + self.bytes.put_u16(len as u16); + } + 65_536..=2_147_483_647 => { + self.bytes.reserve(5); + self.bytes.put_u8(0xD6); + self.bytes.put_u32(len as u32); + } + _ => return Err(Error::LengthOutOfBounds(len)), + } + Ok(self) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_tuple(len) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + variant_index: u32, + _variant: &'static str, + len: usize, + ) -> Result { + // TODO: struct 8/16 + if len > 15 { + return Err(Error::LengthOutOfBounds(len)); + } + self.bytes.reserve(2); + self.bytes.put_u8(0xB0 | len as u8); + self.bytes.put_int(i64::from(variant_index), 1); + Ok(self) + } + + fn serialize_map(self, len: Option) -> Result { + let len = len.ok_or(Error::UnknownLength)?; + match len { + 0..=15 => { + self.bytes.reserve(1); + self.bytes.put_u8(0xA0 | len as u8); + } + 16..=255 => { + self.bytes.reserve(2); + self.bytes.put_u8(0xD8); + self.bytes.put_u8(len as u8); + } + 256..=65_535 => { + self.bytes.reserve(3); + self.bytes.put_u8(0xD9); + self.bytes.put_u16(len as u16); + } + 65_536..=2_147_483_647 => { + self.bytes.reserve(5); + self.bytes.put_u8(0xDA); + self.bytes.put_u32(len as u32); + } + _ => return Err(Error::LengthOutOfBounds(len)), + } + Ok(self) + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + self.serialize_tuple_variant(name, variant_index, variant, len) + } +} + +impl<'a> ser::SerializeSeq for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeTuple for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeMap for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + key.serialize(StrSerializer { inner: self }) + } + + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeStruct for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + ser::Serializer::serialize_str(&mut **self, key)?; + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +impl<'a> ser::SerializeStructVariant for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field( + &mut self, + _key: &'static str, + value: &T, + ) -> Result<(), Self::Error> + where + T: ser::Serialize, + { + value.serialize(&mut **self) + } + + fn end(self) -> Result { + Ok(()) + } +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum Error { + #[error("Unknown sequence length. The length of a sequence must be known in advance.")] + UnknownLength, + + #[error("The value is too long: {0}")] + LengthOutOfBounds(usize), + + #[error("Map key is not a string")] + MapKeyNotString, + + // TODO: copy DeError + #[error("Serialization error: {0}")] + Serialization(String), +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + Self::Serialization(msg.to_string()) + } +} + +struct StrSerializer<'a> { + inner: &'a mut Serializer, +} + +impl<'a> ser::Serializer for StrSerializer<'a> { + type Ok = (); + type Error = Error; + + type SerializeSeq = Impossible<(), Error>; + type SerializeTuple = Impossible<(), Error>; + type SerializeTupleStruct = Impossible<(), Error>; + type SerializeTupleVariant = Impossible<(), Error>; + type SerializeMap = Impossible<(), Error>; + type SerializeStruct = Impossible<(), Error>; + type SerializeStructVariant = Impossible<(), Error>; + + fn serialize_bool(self, _v: bool) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_i8(self, _v: i8) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_i16(self, _v: i16) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_i32(self, _v: i32) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_i64(self, _v: i64) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_u8(self, _v: u8) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_u16(self, _v: u16) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_u32(self, _v: u32) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_u64(self, _v: u64) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_f32(self, _v: f32) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_f64(self, _v: f64) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_char(self, _v: char) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_str(self, v: &str) -> Result { + self.inner.serialize_str(v) + } + + fn serialize_bytes(self, _v: &[u8]) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_none(self) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_some(self, _value: &T) -> Result + where + T: ser::Serialize, + { + Err(Error::MapKeyNotString) + } + + fn serialize_unit(self) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + _value: &T, + ) -> Result + where + T: ser::Serialize, + { + Err(Error::MapKeyNotString) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ser::Serialize, + { + Err(Error::MapKeyNotString) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Error::MapKeyNotString) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::MapKeyNotString) + } +} + +#[cfg(test)] +mod tests { + // use super::*; + + #[test] + fn test_name() { + // TODO: test serde token stuff + } +} diff --git a/lib/src/connection.rs b/lib/src/connection.rs index 76c767f8..7ab73f1b 100644 --- a/lib/src/connection.rs +++ b/lib/src/connection.rs @@ -1,4 +1,5 @@ use crate::{ + bolt::{ExpectedResponse, Message, MessageResponse}, errors::{Error, Result}, messages::{BoltRequest, BoltResponse}, version::Version, @@ -120,9 +121,36 @@ impl Connection { self.recv().await } + pub(crate) async fn _send_recv_as( + &mut self, + message: T, + ) -> Result { + self._send_as(message).await?; + self._recv_as().await + } + pub async fn send(&mut self, message: BoltRequest) -> Result<()> { - let end_marker: [u8; 2] = [0, 0]; let bytes: Bytes = message.into_bytes(self.version)?; + self.send_bytes(bytes).await + } + + pub(crate) async fn _send_as(&mut self, message: T) -> Result<()> { + let bytes = message.to_bytes()?; + self.send_bytes(bytes).await + } + + pub async fn recv(&mut self) -> Result { + let bytes = self.recv_bytes().await?; + BoltResponse::parse(self.version, bytes) + } + + pub(crate) async fn _recv_as(&mut self) -> Result { + let bytes = self.recv_bytes().await?; + Ok(T::parse(bytes)?) + } + + async fn send_bytes(&mut self, bytes: Bytes) -> Result<()> { + let end_marker: [u8; 2] = [0, 0]; for c in bytes.chunks(MAX_CHUNK_SIZE) { self.stream.write_u16(c.len() as u16).await?; self.stream.write_all(c).await?; @@ -132,7 +160,7 @@ impl Connection { Ok(()) } - pub async fn recv(&mut self) -> Result { + async fn recv_bytes(&mut self) -> Result { let mut bytes = BytesMut::new(); let mut chunk_size = 0; while chunk_size == 0 { @@ -144,7 +172,7 @@ impl Connection { chunk_size = self.read_chunk_size().await?; } - BoltResponse::parse(self.version, bytes.freeze()) + Ok(bytes.freeze()) } async fn read_chunk_size(&mut self) -> Result { diff --git a/lib/src/errors.rs b/lib/src/errors.rs index 04f381f9..21057799 100644 --- a/lib/src/errors.rs +++ b/lib/src/errors.rs @@ -1,4 +1,7 @@ -use crate::DeError; +use crate::{ + bolt::{de, ser}, + DeError, +}; pub type Result = std::result::Result; @@ -13,6 +16,12 @@ pub enum Error { #[error("Invalid URI: {0}")] UrlParseError(#[from] url::ParseError), + #[error(transparent)] + WriteError(#[from] ser::Error), + + #[error(transparent)] + ParseError(#[from] de::Error), + #[error("Unsupported URI scheme: {0}")] UnsupportedScheme(String), diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 88ae04d5..1ea3fcc2 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -421,6 +421,7 @@ //! ``` //! //! +mod bolt; mod config; mod connection; mod convert; diff --git a/lib/src/types/map.rs b/lib/src/types/map.rs index 7df144b1..d7693688 100644 --- a/lib/src/types/map.rs +++ b/lib/src/types/map.rs @@ -79,7 +79,7 @@ impl BoltWireFormat for BoltMap { fn parse(version: Version, input: &mut Bytes) -> Result { let marker = input.get_u8(); let size = match marker { - 0xA0..=0xAF => 0x0F & marker as usize, + 0xA0..=0xAF => (marker & 0x0F) as usize, SMALL => input.get_u8() as usize, MEDIUM => input.get_u16() as usize, LARGE => input.get_u32() as usize,