From af6a595fdf941456dd040a860a06185385da2126 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 1 Apr 2025 12:33:18 +0200 Subject: [PATCH] Further downstream code size improvements (#3300) --- axum/src/extract/path/mod.rs | 41 +++++++++-------- axum/src/form.rs | 20 +++++---- axum/src/json.rs | 86 +++++++++++++++++++----------------- 3 files changed, 80 insertions(+), 67 deletions(-) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index e883c643..f37fff4d 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -162,27 +162,30 @@ where type Rejection = PathRejection; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - let params = match parts.extensions.get::() { - Some(UrlParams::Params(params)) => params, - Some(UrlParams::InvalidUtf8InPathParam { key }) => { - let err = PathDeserializationError { - kind: ErrorKind::InvalidUtf8InPathParam { - key: key.to_string(), - }, - }; - let err = FailedToDeserializePathParams(err); - return Err(err.into()); + // Extracted into separate fn so it's only compiled once for all T. + fn get_params(parts: &Parts) -> Result<&[(Arc, PercentDecodedStr)], PathRejection> { + match parts.extensions.get::() { + Some(UrlParams::Params(params)) => Ok(params), + Some(UrlParams::InvalidUtf8InPathParam { key }) => { + let err = PathDeserializationError { + kind: ErrorKind::InvalidUtf8InPathParam { + key: key.to_string(), + }, + }; + Err(FailedToDeserializePathParams(err).into()) + } + None => Err(MissingPathParams.into()), } - None => { - return Err(MissingPathParams.into()); - } - }; + } - T::deserialize(de::PathDeserializer::new(params)) - .map_err(|err| { - PathRejection::FailedToDeserializePathParams(FailedToDeserializePathParams(err)) - }) - .map(Path) + fn failed_to_deserialize_path_params(err: PathDeserializationError) -> PathRejection { + PathRejection::FailedToDeserializePathParams(FailedToDeserializePathParams(err)) + } + + match T::deserialize(de::PathDeserializer::new(get_params(parts)?)) { + Ok(val) => Ok(Path(val)), + Err(e) => Err(failed_to_deserialize_path_params(e)), + } } } diff --git a/axum/src/form.rs b/axum/src/form.rs index fd7c0338..dabfb653 100644 --- a/axum/src/form.rs +++ b/axum/src/form.rs @@ -110,17 +110,21 @@ where T: Serialize, { fn into_response(self) -> Response { - match serde_urlencoded::to_string(&self.0) { - Ok(body) => ( - [(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())], - body, - ) - .into_response(), - Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(), + // Extracted into separate fn so it's only compiled once for all T. + fn make_response(ser_result: Result) -> Response { + match ser_result { + Ok(body) => ( + [(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())], + body, + ) + .into_response(), + Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(), + } } + + make_response(serde_urlencoded::to_string(&self.0)) } } - axum_core::__impl_deref!(Form); #[cfg(test)] diff --git a/axum/src/json.rs b/axum/src/json.rs index 9cd97730..a59b60c1 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -176,31 +176,31 @@ where /// but special cases may require first extracting a `Request` into `Bytes` then optionally /// constructing a `Json`. pub fn from_bytes(bytes: &[u8]) -> Result { - let deserializer = &mut serde_json::Deserializer::from_slice(bytes); - - let value = match serde_path_to_error::deserialize(deserializer) { - Ok(value) => value, - Err(err) => { - let rejection = match err.inner().classify() { - serde_json::error::Category::Data => JsonDataError::from_err(err).into(), - serde_json::error::Category::Syntax | serde_json::error::Category::Eof => { + // Extracted into separate fn so it's only compiled once for all T. + fn make_rejection(err: serde_path_to_error::Error) -> JsonRejection { + match err.inner().classify() { + serde_json::error::Category::Data => JsonDataError::from_err(err).into(), + serde_json::error::Category::Syntax | serde_json::error::Category::Eof => { + JsonSyntaxError::from_err(err).into() + } + serde_json::error::Category::Io => { + if cfg!(debug_assertions) { + // we don't use `serde_json::from_reader` and instead always buffer + // bodies first, so we shouldn't encounter any IO errors + unreachable!() + } else { JsonSyntaxError::from_err(err).into() } - serde_json::error::Category::Io => { - if cfg!(debug_assertions) { - // we don't use `serde_json::from_reader` and instead always buffer - // bodies first, so we shouldn't encounter any IO errors - unreachable!() - } else { - JsonSyntaxError::from_err(err).into() - } - } - }; - return Err(rejection); + } } - }; + } - Ok(Json(value)) + let deserializer = &mut serde_json::Deserializer::from_slice(bytes); + + match serde_path_to_error::deserialize(deserializer) { + Ok(value) => Ok(Json(value)), + Err(err) => Err(make_rejection(err)), + } } } @@ -209,28 +209,34 @@ where T: Serialize, { fn into_response(self) -> Response { + // Extracted into separate fn so it's only compiled once for all T. + fn make_response(buf: BytesMut, ser_result: serde_json::Result<()>) -> Response { + match ser_result { + Ok(()) => ( + [( + header::CONTENT_TYPE, + HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), + )], + buf.freeze(), + ) + .into_response(), + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + [( + header::CONTENT_TYPE, + HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()), + )], + err.to_string(), + ) + .into_response(), + } + } + // Use a small initial capacity of 128 bytes like serde_json::to_vec // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189 let mut buf = BytesMut::with_capacity(128).writer(); - match serde_json::to_writer(&mut buf, &self.0) { - Ok(()) => ( - [( - header::CONTENT_TYPE, - HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), - )], - buf.into_inner().freeze(), - ) - .into_response(), - Err(err) => ( - StatusCode::INTERNAL_SERVER_ERROR, - [( - header::CONTENT_TYPE, - HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()), - )], - err.to_string(), - ) - .into_response(), - } + let res = serde_json::to_writer(&mut buf, &self.0); + make_response(buf.into_inner(), res) } }