diff --git a/ruma-api-macros/src/api/request/outgoing.rs b/ruma-api-macros/src/api/request/outgoing.rs index 46aed9b6..f53d5cd5 100644 --- a/ruma-api-macros/src/api/request/outgoing.rs +++ b/ruma-api-macros/src/api/request/outgoing.rs @@ -11,10 +11,10 @@ impl Request { lifetimes: &TokenStream, ruma_api: &TokenStream, ) -> TokenStream { + let bytes = quote! { #ruma_api::exports::bytes }; let http = quote! { #ruma_api::exports::http }; let percent_encoding = quote! { #ruma_api::exports::percent_encoding }; let ruma_serde = quote! { #ruma_api::exports::ruma_serde }; - let serde_json = quote! { #ruma_api::exports::serde_json }; let method = &metadata.method; let request_path_string = if self.has_path_fields() { @@ -165,7 +165,7 @@ impl Request { let request_body = if let Some(field) = self.newtype_raw_body_field() { let field_name = field.ident.as_ref().expect("expected field to have an identifier"); - quote! { self.#field_name } + quote! { #ruma_serde::slice_to_buf(&self.#field_name) } } else if self.has_body_fields() || self.newtype_body_field().is_some() { let request_body_initializers = if let Some(field) = self.newtype_body_field() { let field_name = @@ -177,13 +177,10 @@ impl Request { }; quote! { - { - let request_body = RequestBody #request_body_initializers; - #serde_json::to_vec(&request_body)? - } + #ruma_serde::json_to_buf(&RequestBody #request_body_initializers)? } } else { - quote! { Vec::new() } + quote! { ::default() } }; let non_auth_impls = metadata.authentication.iter().map(|auth| { @@ -210,14 +207,11 @@ impl Request { const METADATA: #ruma_api::Metadata = self::METADATA; - fn try_into_http_request( + fn try_into_http_request( self, base_url: &::std::primitive::str, access_token: #ruma_api::SendAccessToken<'_>, - ) -> ::std::result::Result< - #http::Request<::std::vec::Vec<::std::primitive::u8>>, - #ruma_api::error::IntoHttpError, - > { + ) -> ::std::result::Result<#http::Request, #ruma_api::error::IntoHttpError> { let metadata = self::METADATA; let mut req_builder = #http::Request::builder() diff --git a/ruma-api-macros/src/api/response/outgoing.rs b/ruma-api-macros/src/api/response/outgoing.rs index 937442c5..b9a0d7d4 100644 --- a/ruma-api-macros/src/api/response/outgoing.rs +++ b/ruma-api-macros/src/api/response/outgoing.rs @@ -5,8 +5,9 @@ use super::{Response, ResponseField}; impl Response { pub fn expand_outgoing(&self, ruma_api: &TokenStream) -> TokenStream { + let bytes = quote! { #ruma_api::exports::bytes }; let http = quote! { #ruma_api::exports::http }; - let serde_json = quote! { #ruma_api::exports::serde_json }; + let ruma_serde = quote! { #ruma_api::exports::ruma_serde }; let serialize_response_headers = self.fields.iter().map(|response_field| { if let ResponseField::Header(field, header_name) = response_field { @@ -40,11 +41,11 @@ impl Response { let body = if let Some(field) = self.newtype_raw_body_field() { let field_name = field.ident.as_ref().expect("expected field to have an identifier"); - quote! { self.#field_name } + quote! { #ruma_serde::slice_to_buf(&self.#field_name) } } else if let Some(field) = self.newtype_body_field() { let field_name = field.ident.as_ref().expect("expected field to have an identifier"); quote! { - #serde_json::to_vec(&self.#field_name)? + #ruma_serde::json_to_buf(&self.#field_name)? } } else { let fields = self.fields.iter().map(|response_field| { @@ -63,7 +64,7 @@ impl Response { }); quote! { - #serde_json::to_vec(&ResponseBody { #(#fields)* })? + #ruma_serde::json_to_buf(&ResponseBody { #(#fields)* })? } }; @@ -72,12 +73,9 @@ impl Response { #[cfg(feature = "server")] #[allow(clippy::unknown_clippy_lints, clippy::inconsistent_struct_constructor)] impl #ruma_api::OutgoingResponse for Response { - fn try_into_http_response( + fn try_into_http_response( self, - ) -> ::std::result::Result< - #http::Response<::std::vec::Vec<::std::primitive::u8>>, - #ruma_api::error::IntoHttpError, - > { + ) -> ::std::result::Result<#http::Response, #ruma_api::error::IntoHttpError> { let mut resp_builder = #http::Response::builder() .header(#http::header::CONTENT_TYPE, "application/json"); diff --git a/ruma-api/Cargo.toml b/ruma-api/Cargo.toml index b2f2ae17..c07adf13 100644 --- a/ruma-api/Cargo.toml +++ b/ruma-api/Cargo.toml @@ -19,6 +19,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +bytes = "1.0.1" http = "0.2.2" percent-encoding = "2.1.0" ruma-api-macros = { version = "=0.17.0-alpha.4", path = "../ruma-api-macros" } diff --git a/ruma-api/src/error.rs b/ruma-api/src/error.rs index 154c3d42..d38c9204 100644 --- a/ruma-api/src/error.rs +++ b/ruma-api/src/error.rs @@ -4,6 +4,7 @@ use std::{error::Error as StdError, fmt}; +use bytes::BufMut; use thiserror::Error; use crate::{EndpointError, OutgoingResponse}; @@ -14,7 +15,9 @@ use crate::{EndpointError, OutgoingResponse}; pub enum Void {} impl OutgoingResponse for Void { - fn try_into_http_response(self) -> Result>, IntoHttpError> { + fn try_into_http_response( + self, + ) -> Result, IntoHttpError> { match self {} } } diff --git a/ruma-api/src/lib.rs b/ruma-api/src/lib.rs index 5a474533..0ba544cb 100644 --- a/ruma-api/src/lib.rs +++ b/ruma-api/src/lib.rs @@ -22,6 +22,7 @@ compile_error!("ruma_api's Cargo features only exist as a workaround are not mea use std::{convert::TryInto as _, error::Error as StdError}; +use bytes::BufMut; use http::Method; use ruma_identifiers::UserId; @@ -203,6 +204,7 @@ pub mod error; /// It is not considered part of ruma-api's public API. #[doc(hidden)] pub mod exports { + pub use bytes; pub use http; pub use percent_encoding; pub use ruma_serde; @@ -267,11 +269,11 @@ pub trait OutgoingRequest: Sized { /// The endpoints path will be appended to the given `base_url`, for example /// `https://matrix.org`. Since all paths begin with a slash, it is not necessary for the /// `base_url` to have a trailing slash. If it has one however, it will be ignored. - fn try_into_http_request( + fn try_into_http_request( self, base_url: &str, access_token: SendAccessToken<'_>, - ) -> Result>, IntoHttpError>; + ) -> Result, IntoHttpError>; } /// A response type for a Matrix API endpoint, used for receiving responses. @@ -291,12 +293,12 @@ pub trait OutgoingRequestAppserviceExt: OutgoingRequest { /// [assert Appservice identity][id_assert]. /// /// [id_assert]: https://matrix.org/docs/spec/application_service/r0.1.2#identity-assertion - fn try_into_http_request_with_user_id( + fn try_into_http_request_with_user_id( self, base_url: &str, access_token: SendAccessToken<'_>, user_id: UserId, - ) -> Result>, IntoHttpError> { + ) -> Result, IntoHttpError> { let mut http_request = self.try_into_http_request(base_url, access_token)?; let user_id_query = ruma_serde::urlencoded::to_string(&[("user_id", &user_id.into_string())])?; @@ -346,7 +348,9 @@ pub trait OutgoingResponse { /// /// This method should only fail when when invalid header values are specified. It may also /// fail with a serialization error in case of bugs in Ruma though. - fn try_into_http_response(self) -> Result>, IntoHttpError>; + fn try_into_http_response( + self, + ) -> Result, IntoHttpError>; } /// Gives users the ability to define their own serializable / deserializable errors. diff --git a/ruma-api/tests/conversions.rs b/ruma-api/tests/conversions.rs index 14e941d5..b521279e 100644 --- a/ruma-api/tests/conversions.rs +++ b/ruma-api/tests/conversions.rs @@ -48,8 +48,10 @@ fn request_serde() { user: user_id!("@bazme:ruma.io"), }; - let http_req = - req.clone().try_into_http_request("https://homeserver.tld", SendAccessToken::None).unwrap(); + let http_req = req + .clone() + .try_into_http_request::>("https://homeserver.tld", SendAccessToken::None) + .unwrap(); let req2 = Request::try_from_http_request(http_req).unwrap(); assert_eq!(req.hello, req2.hello); @@ -73,7 +75,7 @@ fn request_with_user_id_serde() { let user_id = user_id!("@_virtual_:ruma.io"); let http_req = req - .try_into_http_request_with_user_id( + .try_into_http_request_with_user_id::>( "https://homeserver.tld", SendAccessToken::None, user_id, @@ -131,7 +133,7 @@ mod without_query { let user_id = user_id!("@_virtual_:ruma.io"); let http_req = req - .try_into_http_request_with_user_id( + .try_into_http_request_with_user_id::>( "https://homeserver.tld", SendAccessToken::None, user_id, diff --git a/ruma-api/tests/header_override.rs b/ruma-api/tests/header_override.rs index e3dd9102..26673996 100644 --- a/ruma-api/tests/header_override.rs +++ b/ruma-api/tests/header_override.rs @@ -28,7 +28,7 @@ ruma_api! { #[test] fn response_content_type_override() { let res = Response { stuff: "magic".into() }; - let mut http_res = res.try_into_http_response().unwrap(); + let mut http_res = res.try_into_http_response::>().unwrap(); // Test that we correctly replaced the default content type, // not adding another content-type header. @@ -45,8 +45,9 @@ fn response_content_type_override() { #[test] fn request_content_type_override() { let req = Request { location: None, stuff: "magic".into() }; - let mut http_req = - req.try_into_http_request("https://homeserver.tld", SendAccessToken::None).unwrap(); + let mut http_req = req + .try_into_http_request::>("https://homeserver.tld", SendAccessToken::None) + .unwrap(); assert_eq!( match http_req.headers_mut().entry(CONTENT_TYPE) { diff --git a/ruma-api/tests/manual_endpoint_impl.rs b/ruma-api/tests/manual_endpoint_impl.rs index 19a886cc..43bf5b84 100644 --- a/ruma-api/tests/manual_endpoint_impl.rs +++ b/ruma-api/tests/manual_endpoint_impl.rs @@ -2,6 +2,7 @@ use std::convert::TryFrom; +use bytes::BufMut; use http::{header::CONTENT_TYPE, method::Method}; use ruma_api::{ error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError, ServerError, Void}, @@ -38,11 +39,11 @@ impl OutgoingRequest for Request { const METADATA: Metadata = METADATA; - fn try_into_http_request( + fn try_into_http_request( self, base_url: &str, _access_token: SendAccessToken<'_>, - ) -> Result>, IntoHttpError> { + ) -> Result, IntoHttpError> { let url = (base_url.to_owned() + METADATA.path) .replace(":room_alias", &self.room_alias.to_string()); @@ -51,7 +52,7 @@ impl OutgoingRequest for Request { let http_request = http::Request::builder() .method(METADATA.method) .uri(url) - .body(serde_json::to_vec(&request_body)?) + .body(ruma_serde::json_to_buf(&request_body)?) // this cannot fail because we don't give user-supplied data to any of the // builder methods .unwrap(); @@ -113,10 +114,12 @@ impl IncomingResponse for Response { } impl OutgoingResponse for Response { - fn try_into_http_response(self) -> Result>, IntoHttpError> { + fn try_into_http_response( + self, + ) -> Result, IntoHttpError> { let response = http::Response::builder() .header(CONTENT_TYPE, "application/json") - .body(b"{}".to_vec()) + .body(ruma_serde::slice_to_buf(b"{}")) .unwrap(); Ok(response) diff --git a/ruma-api/tests/no_fields.rs b/ruma-api/tests/no_fields.rs index 925a1cae..17bdb55f 100644 --- a/ruma-api/tests/no_fields.rs +++ b/ruma-api/tests/no_fields.rs @@ -17,8 +17,9 @@ ruma_api! { #[test] fn empty_request_http_repr() { let req = Request {}; - let http_req = - req.try_into_http_request("https://homeserver.tld", SendAccessToken::None).unwrap(); + let http_req = req + .try_into_http_request::>("https://homeserver.tld", SendAccessToken::None) + .unwrap(); assert!(http_req.body().is_empty()); } @@ -26,7 +27,7 @@ fn empty_request_http_repr() { #[test] fn empty_response_http_repr() { let res = Response {}; - let http_res = res.try_into_http_response().unwrap(); + let http_res = res.try_into_http_response::>().unwrap(); assert_eq!(http_res.body(), b"{}"); } diff --git a/ruma-api/tests/ui/05-request-only.rs b/ruma-api/tests/ui/05-request-only.rs index 9e01fc95..61579fca 100644 --- a/ruma-api/tests/ui/05-request-only.rs +++ b/ruma-api/tests/ui/05-request-only.rs @@ -1,3 +1,4 @@ +use bytes::BufMut; use ruma_api::{ error::{FromHttpResponseError, IntoHttpError, Void}, ruma_api, IncomingResponse, OutgoingResponse, @@ -35,7 +36,9 @@ impl IncomingResponse for Response { } impl OutgoingResponse for Response { - fn try_into_http_response(self) -> Result>, IntoHttpError> { + fn try_into_http_response( + self, + ) -> Result, IntoHttpError> { todo!() } } diff --git a/ruma-client-api/Cargo.toml b/ruma-client-api/Cargo.toml index 66ea9070..093d8159 100644 --- a/ruma-client-api/Cargo.toml +++ b/ruma-client-api/Cargo.toml @@ -17,6 +17,7 @@ edition = "2018" [dependencies] assign = "1.1.1" +bytes = "1.0.1" http = "0.2.2" js_int = { version = "0.2.0", features = ["serde"] } maplit = "1.0.2" diff --git a/ruma-client-api/src/error.rs b/ruma-client-api/src/error.rs index f0e599e1..19382387 100644 --- a/ruma-client-api/src/error.rs +++ b/ruma-client-api/src/error.rs @@ -2,13 +2,14 @@ use std::{collections::BTreeMap, fmt, time::Duration}; +use bytes::BufMut; use ruma_api::{ error::{IntoHttpError, ResponseDeserializationError}, EndpointError, OutgoingResponse, }; use ruma_identifiers::RoomVersionId; use serde::{Deserialize, Serialize}; -use serde_json::{from_slice as from_json_slice, to_vec as to_json_vec, Value as JsonValue}; +use serde_json::{from_slice as from_json_slice, Value as JsonValue}; /// Deserialize and Serialize implementations for ErrorKind. /// Separate module because it's a lot of code. @@ -236,11 +237,13 @@ impl ErrorBody { } impl OutgoingResponse for Error { - fn try_into_http_response(self) -> Result>, IntoHttpError> { + fn try_into_http_response( + self, + ) -> Result, IntoHttpError> { http::Response::builder() .header(http::header::CONTENT_TYPE, "application/json") .status(self.status_code) - .body(to_json_vec(&ErrorBody::from(self))?) + .body(ruma_serde::json_to_buf(&ErrorBody::from(self))?) .map_err(Into::into) } } diff --git a/ruma-client-api/src/r0/directory/get_public_rooms.rs b/ruma-client-api/src/r0/directory/get_public_rooms.rs index 53afc040..d4daba8c 100644 --- a/ruma-client-api/src/r0/directory/get_public_rooms.rs +++ b/ruma-client-api/src/r0/directory/get_public_rooms.rs @@ -84,7 +84,10 @@ mod tests { since: Some("hello"), server: Some(&server_name!("test.tld")), } - .try_into_http_request("https://homeserver.tld", SendAccessToken::IfRequired("auth_tok")) + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + ) .unwrap(); let uri = req.uri(); @@ -107,7 +110,7 @@ mod tests { prev_batch: Some("prev_batch_token".into()), total_room_count_estimate: Some(uint!(10)), } - .try_into_http_response() + .try_into_http_response::>() .unwrap(); assert_eq!( diff --git a/ruma-client-api/src/r0/message/get_message_events.rs b/ruma-client-api/src/r0/message/get_message_events.rs index 9bc5646a..6d9f9c13 100644 --- a/ruma-client-api/src/r0/message/get_message_events.rs +++ b/ruma-client-api/src/r0/message/get_message_events.rs @@ -191,7 +191,7 @@ mod tests { }; let request = req - .try_into_http_request( + .try_into_http_request::>( "https://homeserver.tld", SendAccessToken::IfRequired("auth_tok"), ) diff --git a/ruma-client-api/src/r0/session/sso_login_with_provider.rs b/ruma-client-api/src/r0/session/sso_login_with_provider.rs index 135b8295..4a88cc71 100644 --- a/ruma-client-api/src/r0/session/sso_login_with_provider.rs +++ b/ruma-client-api/src/r0/session/sso_login_with_provider.rs @@ -60,7 +60,7 @@ mod tests { #[test] fn serialize_sso_login_with_provider_request_uri() { let req = Request { idp_id: "provider", redirect_url: "https://example.com/sso" } - .try_into_http_request("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>("https://homeserver.tld", SendAccessToken::None) .unwrap(); assert_eq!( diff --git a/ruma-client-api/src/r0/state/get_state_events_for_key.rs b/ruma-client-api/src/r0/state/get_state_events_for_key.rs index f4c341db..c4719b9b 100644 --- a/ruma-client-api/src/r0/state/get_state_events_for_key.rs +++ b/ruma-client-api/src/r0/state/get_state_events_for_key.rs @@ -1,5 +1,6 @@ //! [GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey) +use bytes::BufMut; use ruma_api::{ruma_api, SendAccessToken}; use ruma_events::{AnyStateEventContent, EventType}; use ruma_identifiers::RoomId; @@ -65,14 +66,14 @@ impl<'a> ruma_api::OutgoingRequest for Request<'a> { const METADATA: ruma_api::Metadata = METADATA; - fn try_into_http_request( + fn try_into_http_request( self, base_url: &str, access_token: SendAccessToken<'_>, - ) -> Result>, ruma_api::error::IntoHttpError> { + ) -> Result, ruma_api::error::IntoHttpError> { use std::borrow::Cow; - use http::header::{HeaderValue, AUTHORIZATION, CONTENT_TYPE}; + use http::header::{self, HeaderValue}; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; let mut url = format!( @@ -90,9 +91,9 @@ impl<'a> ruma_api::OutgoingRequest for Request<'a> { http::Request::builder() .method(http::Method::GET) .uri(url) - .header(CONTENT_TYPE, "application/json") + .header(header::CONTENT_TYPE, "application/json") .header( - AUTHORIZATION, + header::AUTHORIZATION, HeaderValue::from_str(&format!( "Bearer {}", access_token @@ -100,7 +101,7 @@ impl<'a> ruma_api::OutgoingRequest for Request<'a> { .ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)?, ))?, ) - .body(Vec::new()) + .body(T::default()) .map_err(Into::into) } } diff --git a/ruma-client-api/src/r0/tag/get_tags.rs b/ruma-client-api/src/r0/tag/get_tags.rs index 283c36cc..2e0d552e 100644 --- a/ruma-client-api/src/r0/tag/get_tags.rs +++ b/ruma-client-api/src/r0/tag/get_tags.rs @@ -62,7 +62,7 @@ mod server_tests { tags.insert("u.user_tag".into(), assign!(TagInfo::new(), { order: Some(0.11) })); let response = Response { tags }; - let http_response = response.try_into_http_response().unwrap(); + let http_response = response.try_into_http_response::>().unwrap(); let json_response: serde_json::Value = serde_json::from_slice(http_response.body()).unwrap(); diff --git a/ruma-client-api/src/r0/uiaa.rs b/ruma-client-api/src/r0/uiaa.rs index f59bab42..f8690e68 100644 --- a/ruma-client-api/src/r0/uiaa.rs +++ b/ruma-client-api/src/r0/uiaa.rs @@ -2,6 +2,7 @@ use std::{collections::BTreeMap, fmt}; +use bytes::BufMut; use ruma_api::{ error::{IntoHttpError, ResponseDeserializationError}, EndpointError, OutgoingResponse, @@ -9,8 +10,7 @@ use ruma_api::{ use ruma_serde::Outgoing; use serde::{Deserialize, Serialize}; use serde_json::{ - from_slice as from_json_slice, to_vec as to_json_vec, value::RawValue as RawJsonValue, - Value as JsonValue, + from_slice as from_json_slice, value::RawValue as RawJsonValue, Value as JsonValue, }; use crate::error::{Error as MatrixError, ErrorBody}; @@ -150,12 +150,14 @@ impl EndpointError for UiaaResponse { impl std::error::Error for UiaaResponse {} impl OutgoingResponse for UiaaResponse { - fn try_into_http_response(self) -> Result>, IntoHttpError> { + fn try_into_http_response( + self, + ) -> Result, IntoHttpError> { match self { UiaaResponse::AuthResponse(authentication_info) => http::Response::builder() .header(http::header::CONTENT_TYPE, "application/json") .status(&http::StatusCode::UNAUTHORIZED) - .body(to_json_vec(&authentication_info)?) + .body(ruma_serde::json_to_buf(&authentication_info)?) .map_err(Into::into), UiaaResponse::MatrixError(error) => error.try_into_http_response(), } @@ -336,7 +338,8 @@ mod tests { session: None, auth_error: None, }; - let uiaa_response = UiaaResponse::AuthResponse(uiaa_info).try_into_http_response().unwrap(); + let uiaa_response = + UiaaResponse::AuthResponse(uiaa_info).try_into_http_response::>().unwrap(); assert_matches!( from_json_slice::(uiaa_response.body()).unwrap(), diff --git a/ruma-client/src/lib.rs b/ruma-client/src/lib.rs index 9299e83a..4ca38554 100644 --- a/ruma-client/src/lib.rs +++ b/ruma-client/src/lib.rs @@ -357,7 +357,10 @@ impl Client { SendAccessToken::None }; - request.try_into_http_request(&client.homeserver_url.to_string(), access_token)? + request.try_into_http_request::>( + &client.homeserver_url.to_string(), + access_token, + )? }; let extra_params = urlencoded::to_string(extra_params).unwrap(); diff --git a/ruma-federation-api/src/membership/create_leave_event/v2.rs b/ruma-federation-api/src/membership/create_leave_event/v2.rs index acdf9c77..8052cd84 100644 --- a/ruma-federation-api/src/membership/create_leave_event/v2.rs +++ b/ruma-federation-api/src/membership/create_leave_event/v2.rs @@ -57,7 +57,7 @@ mod tests { #[test] fn response_body() { - let res = Response::new().try_into_http_response().unwrap(); + let res = Response::new().try_into_http_response::>().unwrap(); assert_eq!(res.body(), b"{}"); } diff --git a/ruma-serde/Cargo.toml b/ruma-serde/Cargo.toml index 0f77d7fd..491fc3be 100644 --- a/ruma-serde/Cargo.toml +++ b/ruma-serde/Cargo.toml @@ -13,6 +13,7 @@ version = "0.3.1" edition = "2018" [dependencies] +bytes = "1.0.1" form_urlencoded = "1.0.0" js_int = { version = "0.2.0", features = ["serde"] } itoa = "0.4.6" diff --git a/ruma-serde/src/buf.rs b/ruma-serde/src/buf.rs new file mode 100644 index 00000000..1c597c6b --- /dev/null +++ b/ruma-serde/src/buf.rs @@ -0,0 +1,16 @@ +use bytes::BufMut; +use serde::Serialize; + +/// Converts a byte slice to a buffer by copying. +pub fn slice_to_buf(s: &[u8]) -> B { + let mut buf = B::default(); + buf.put_slice(s); + buf +} + +/// Creates a buffer and writes a serializable value to it. +pub fn json_to_buf(val: &T) -> serde_json::Result { + let mut buf = B::default().writer(); + serde_json::to_writer(&mut buf, val)?; + Ok(buf.into_inner()) +} diff --git a/ruma-serde/src/lib.rs b/ruma-serde/src/lib.rs index 4c71e7cf..6d137a57 100644 --- a/ruma-serde/src/lib.rs +++ b/ruma-serde/src/lib.rs @@ -2,6 +2,7 @@ #![doc(html_logo_url = "https://www.ruma.io/images/logo.png")] //! (De)serialization helpers for other ruma crates. +mod buf; pub mod can_be_empty; mod canonical_json; mod cow; @@ -15,6 +16,7 @@ pub mod test; pub mod time; pub mod urlencoded; +pub use buf::{json_to_buf, slice_to_buf}; pub use can_be_empty::{is_empty, CanBeEmpty}; pub use canonical_json::{ to_canonical_value, to_string as to_canonical_json_string, try_from_json_map,