arminius_lib_roomservice_client/
ruma_request_wrapper.rs1mod tests;
13
14use std::{borrow::Cow, collections::BTreeSet};
15
16use axum::http::Request;
17use itertools::Itertools as _;
18use ruma::api::{
19 IncomingRequest, IncomingRequestExt, MatrixVersion, Metadata, OutgoingRequest,
20 OutgoingRequestExt, SupportedVersions,
21 auth_scheme::{AccessToken, AuthScheme, SendAccessToken},
22 path_builder::{PathBuilder, SinglePath, VersionHistory},
23};
24use serde::{Deserialize, Serialize, de::Error as _, ser::Error as _};
25
26pub trait DefaultPathInput<T: PathBuilder> {
36 fn get_default<'a>() -> T::Input<'a>;
38}
39
40impl DefaultPathInput<VersionHistory> for VersionHistory {
41 fn get_default<'a>() -> <VersionHistory as PathBuilder>::Input<'a> {
42 Cow::Owned(SupportedVersions {
43 versions: BTreeSet::from([MatrixVersion::V1_16]),
44 features: BTreeSet::new(),
45 })
46 }
47}
48
49impl DefaultPathInput<SinglePath> for SinglePath {
50 fn get_default<'a>() -> <SinglePath as PathBuilder>::Input<'a> {}
51}
52
53pub trait DefaultAuthScheme<T: AuthScheme> {
60 fn get_default<'a>() -> T::Input<'a>;
62}
63
64impl DefaultAuthScheme<AccessToken> for AccessToken {
65 fn get_default<'a>() -> <AccessToken as AuthScheme>::Input<'a> {
66 SendAccessToken::Always("")
67 }
68}
69
70#[derive(Debug)]
73pub struct SerializingRumaRequestWrapper<T>(pub T)
74where
75 T: IncomingRequest + OutgoingRequest + Clone,
76 <T as Metadata>::Authentication: DefaultAuthScheme<<T as Metadata>::Authentication>,
77 <T as Metadata>::PathBuilder: DefaultPathInput<<T as Metadata>::PathBuilder>;
78
79#[derive(Debug, Serialize, Deserialize)]
83struct SerializedRumaRequest {
84 pub uri: String,
86 pub request_bytes: Vec<u8>,
88}
89
90impl<T> Serialize for SerializingRumaRequestWrapper<T>
91where
92 T: Clone + IncomingRequest + OutgoingRequest,
93 <T as Metadata>::Authentication: DefaultAuthScheme<<T as Metadata>::Authentication>,
94 <T as Metadata>::PathBuilder: DefaultPathInput<<T as Metadata>::PathBuilder>,
95{
96 fn serialize<'a, S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97 where
98 S: serde::Serializer,
99 T: Clone + IncomingRequest + OutgoingRequest,
100 <T as Metadata>::Authentication: DefaultAuthScheme<<T as Metadata>::Authentication>,
101 <T as Metadata>::PathBuilder: DefaultPathInput<<T as Metadata>::PathBuilder>,
102 {
103 let auth_input: <T::Authentication as AuthScheme>::Input<'a> =
104 <T::Authentication as DefaultAuthScheme<T::Authentication>>::get_default::<'a>();
105 let path_input: <T::PathBuilder as PathBuilder>::Input<'a> =
106 <T::PathBuilder as DefaultPathInput<T::PathBuilder>>::get_default::<'a>();
107 let request: Request<Vec<u8>> =
108 self.0.clone().try_into_http_request("", auth_input, path_input).map_err(|err| {
109 S::Error::custom(format!("Failed serializing into HTTP request: {err}"))
110 })?;
111 let (parts, body) = request.into_parts();
112 let serializer_struct =
113 SerializedRumaRequest { uri: parts.uri.to_string(), request_bytes: body };
114 serializer_struct.serialize(serializer)
115 }
116}
117
118impl<'de, T> Deserialize<'de> for SerializingRumaRequestWrapper<T>
119where
120 T: IncomingRequestExt + OutgoingRequestExt + Clone,
121 <T as Metadata>::Authentication: DefaultAuthScheme<<T as Metadata>::Authentication>,
122 <T as Metadata>::PathBuilder: DefaultPathInput<<T as Metadata>::PathBuilder>,
123{
124 fn deserialize<'a, D>(deserializer: D) -> Result<Self, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 <T as Metadata>::Authentication: DefaultAuthScheme<<T as Metadata>::Authentication>,
128 <T as Metadata>::PathBuilder: DefaultPathInput<<T as Metadata>::PathBuilder>,
129 {
130 let serializer_struct = SerializedRumaRequest::deserialize(deserializer)?;
131 let mut request = Request::new(&serializer_struct.request_bytes[..]);
132 *request.uri_mut() = serializer_struct
133 .uri
134 .clone()
135 .try_into()
136 .map_err(|err| D::Error::custom(format!("Failed parsing serialized URI: {err}")))?;
137 *request.method_mut() = <T as Metadata>::METHOD;
138
139 let path_segments: Vec<&str> =
143 serializer_struct.uri.split('/').filter(|segment| !segment.is_empty()).collect();
144
145 let path_input: <T::PathBuilder as PathBuilder>::Input<'a> =
146 <T::PathBuilder as DefaultPathInput<T::PathBuilder>>::get_default::<'a>();
147 let endpoint_path = T::PathBuilder::select_path(&<T as Metadata>::PATH_BUILDER, path_input)
148 .map_err(|_| {
149 D::Error::custom("Failed to select a path when deserializing request".to_owned())
150 })?;
151 let path_with_placeholders = endpoint_path.split("/").skip(1);
152 let path_args = path_segments
153 .iter()
154 .zip_longest(path_with_placeholders)
155 .map(|segment| {
156 let (path_segment, template_segment) = match segment {
157 itertools::EitherOrBoth::Both(path, template) => Ok((*path, template)),
158 itertools::EitherOrBoth::Left(_) => {
159 Err(D::Error::custom("Too many path segments in serialized request"))
160 }
161 itertools::EitherOrBoth::Right(template) => Ok(("", template)),
162 }?;
163
164 if template_segment.starts_with(':')
165 || (template_segment.starts_with('{') && template_segment.ends_with('}'))
166 {
167 Ok(Some(path_segment))
168 } else {
169 Ok(None)
170 }
171 })
172 .collect::<Result<Vec<Option<&str>>, _>>()?
173 .into_iter()
174 .flatten()
175 .collect::<Vec<&str>>();
176
177 Ok(SerializingRumaRequestWrapper(T::try_from_http_request(request, &path_args).map_err(
178 |err| D::Error::custom(format!("Failed deserializing HTTP request: {err}")),
179 )?))
180 }
181}