-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use custom {de,}serialization for operation/response envelopes #5
Changes from 4 commits
b5d79c0
4635133
4ef6083
30be0ca
90c7a21
5f65e7d
c1dbd49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
use std::marker::PhantomData; | ||
|
||
use serde::{de::Visitor, Deserialize, Deserializer}; | ||
|
||
use crate::OperationResponse; | ||
|
||
#[derive(Deserialize)] | ||
#[serde(rename_all = "PascalCase")] | ||
pub(super) struct DummyEnvelope<T> | ||
babolivier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
where | ||
T: OperationResponse, | ||
{ | ||
#[serde(deserialize_with = "deserialize_body")] | ||
pub body: T, | ||
} | ||
|
||
fn deserialize_body<'de, D, T>(body: D) -> Result<T, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
T: OperationResponse, | ||
{ | ||
body.deserialize_map(BodyVisitor::<T>(PhantomData)) | ||
} | ||
|
||
/// A visitor for custom name-based deserialization of operation responses. | ||
struct BodyVisitor<T>(PhantomData<T>); | ||
|
||
impl<'de, T> Visitor<'de> for BodyVisitor<T> | ||
where | ||
T: OperationResponse, | ||
{ | ||
type Value = T; | ||
|
||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
formatter.write_str("EWS operation response body") | ||
} | ||
|
||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> | ||
where | ||
A: serde::de::MapAccess<'de>, | ||
{ | ||
match map.next_key::<String>()? { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I know, we should never be receiving more than one element in the body of a response. That said, there's no documented guarantee of this that I'm aware of, so this code could potentially be made more robust by iterating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understood the the documentation the inner body response is considered a collection? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A quick scan through the WSDL suggests that the contents of the |
||
Some(name) => { | ||
// We expect the body of the response to contain a single | ||
// element with the name of the expected operation response. | ||
let expected = T::name(); | ||
if name.as_str() != expected { | ||
return Err(serde::de::Error::custom(format_args!( | ||
"unknown field `{}`, expected {}", | ||
name, expected | ||
))); | ||
} | ||
|
||
let value = map.next_value()?; | ||
|
||
// To satisfy quick-xml's serde impl, we need to consume the | ||
// final `None` key value in order to successfully complete. | ||
match map.next_key::<String>()? { | ||
Some(name) => { | ||
// The response body contained more than one element, | ||
// which violates our expectations. | ||
Err(serde::de::Error::custom(format_args!( | ||
"unexpected field `{}`", | ||
babolivier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
name | ||
))) | ||
} | ||
None => Ok(value), | ||
} | ||
} | ||
None => Err(serde::de::Error::invalid_type( | ||
serde::de::Unexpected::Map, | ||
&self, | ||
)), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deriving this would be pretty trivial and we probably want to do that soon. I just wanted to get it working.
For derivation, we could even potentially make it so that
Operation
andOperationResponse
are the traits used for the macro(s) and gettingEnvelopeBodyContents
is a "hidden" bonus. It would just require that we add something like a#[response(GetFolderResponse)]
attribute to theOperation
derive. Thoughts on this are welcomed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps something more specific to the project such as
#[ews:node(GetFolderResponse)]
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the helper attribute needs to be a valid identifier, but that's something we can figure out later if we do want to add this derive functionality in that form.