From 90826796094063f5c844fd60d9ccabc2015b952d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lecrenier?= Date: Thu, 2 Jun 2022 11:39:12 +0200 Subject: [PATCH] Add Jayson actix-web extractor --- Cargo.lock | 27 +++++++- meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/extractors/jayson.rs | 79 +++++++++++++++++++++++ meilisearch-http/src/extractors/mod.rs | 1 + 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 meilisearch-http/src/extractors/jayson.rs diff --git a/Cargo.lock b/Cargo.lock index 1bd47e355..969153813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + [[package]] name = "cookie" version = "0.16.0" @@ -921,7 +927,7 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2 1.0.39", "quote 1.0.18", "rustc_version 0.4.0", @@ -1626,6 +1632,24 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "jayson" +version = "0.1.0" +dependencies = [ + "jayson-internal", + "serde_json", +] + +[[package]] +name = "jayson-internal" +version = "0.1.0" +dependencies = [ + "convert_case 0.5.0", + "proc-macro2 1.0.39", + "quote 1.0.18", + "syn 1.0.96", +] + [[package]] name = "jieba-rs" version = "0.6.6" @@ -2045,6 +2069,7 @@ dependencies = [ "http", "indexmap", "itertools", + "jayson", "jsonwebtoken", "log", "manifest-dir-macros", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 763337888..7f8b9931f 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -42,6 +42,7 @@ futures-util = "0.3.21" http = "0.2.6" indexmap = { version = "1.8.0", features = ["serde-1"] } itertools = "0.10.3" +jayson = { path = "../../jayson", features = ["serde-json"] } jsonwebtoken = "8.0.1" log = "0.4.14" meilisearch-auth = { path = "../meilisearch-auth" } diff --git a/meilisearch-http/src/extractors/jayson.rs b/meilisearch-http/src/extractors/jayson.rs new file mode 100644 index 000000000..3af7e4876 --- /dev/null +++ b/meilisearch-http/src/extractors/jayson.rs @@ -0,0 +1,79 @@ +use actix_web::{dev::Payload, web::Json, FromRequest, HttpRequest}; +use futures::ready; +use jayson::{DeserializeError, DeserializeFromValue}; +use meilisearch_types::error::{Code, ErrorCode, ResponseError}; +use std::{ + fmt::Debug, + future::Future, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +/// Extractor for typed data from Json request payloads +/// deserialised by Jayson. +/// +/// # Extractor +/// To extract typed data from a request body, the inner type `T` must implement the +/// [`jayson::DeserializeFromError`] trait. The inner type `E` must implement the +/// [`ErrorCode`](meilisearch_error::ErrorCode) trait. +#[derive(Debug)] +pub struct ValidatedJson(pub T, PhantomData<*const E>); + +impl ValidatedJson { + pub fn new(data: T) -> Self { + ValidatedJson(data, PhantomData) + } + pub fn into_inner(self) -> T { + self.0 + } +} + +impl FromRequest for ValidatedJson +where + E: DeserializeError + ErrorCode + 'static, + T: DeserializeFromValue, +{ + type Error = ResponseError; + type Future = ValidatedJsonExtractFut; + + #[inline] + fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { + ValidatedJsonExtractFut { + fut: Json::::from_request(req, payload), + _phantom: PhantomData, + } + } +} + +pub struct ValidatedJsonExtractFut { + fut: as FromRequest>::Future, + _phantom: PhantomData<*const (T, E)>, +} + +impl Future for ValidatedJsonExtractFut +where + T: DeserializeFromValue, + E: DeserializeError + ErrorCode + 'static, +{ + type Output = Result, ResponseError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + + let res = ready!(Pin::new(&mut this.fut).poll(cx)); + + let res = match res { + Err(err) => Err(ResponseError::from_msg( + format!("{err}"), + Code::MalformedPayload, + )), + Ok(data) => match jayson::deserialize::<_, _, E>(data.into_inner()) { + Ok(data) => Ok(ValidatedJson::new(data)), + Err(e) => Err(e.into()), + }, + }; + + Poll::Ready(res) + } +} diff --git a/meilisearch-http/src/extractors/mod.rs b/meilisearch-http/src/extractors/mod.rs index 98a22f8c9..efb74a15a 100644 --- a/meilisearch-http/src/extractors/mod.rs +++ b/meilisearch-http/src/extractors/mod.rs @@ -1,4 +1,5 @@ pub mod payload; #[macro_use] pub mod authentication; +pub mod jayson; pub mod sequential_extractor;