2
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
4
5
- use serde:: Deserialize ;
5
+ use std:: ops:: { Deref , DerefMut } ;
6
+
7
+ use serde:: { Deserialize , Deserializer } ;
6
8
use time:: format_description:: well_known:: Iso8601 ;
7
9
use xml_struct:: XmlSerialize ;
8
10
@@ -278,7 +280,7 @@ pub enum BaseItemId {
278
280
/// The unique identifier of an item.
279
281
///
280
282
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid>
281
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
283
+ #[ derive( Clone , Debug , Deserialize , XmlSerialize , PartialEq ) ]
282
284
pub struct ItemId {
283
285
#[ xml_struct( attribute) ]
284
286
#[ serde( rename = "@Id" ) ]
@@ -359,11 +361,18 @@ pub enum Folder {
359
361
} ,
360
362
}
361
363
364
+ /// An array of items.
365
+ #[ derive( Debug , Deserialize ) ]
366
+ pub struct Items {
367
+ #[ serde( rename = "$value" , default ) ]
368
+ pub inner : Vec < RealItem > ,
369
+ }
370
+
362
371
/// An item which may appear as the result of a request to read or modify an
363
372
/// Exchange item.
364
373
///
365
374
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/items>
366
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
375
+ #[ derive( Debug , Deserialize ) ]
367
376
pub enum RealItem {
368
377
Message ( Message ) ,
369
378
}
@@ -372,7 +381,8 @@ pub enum RealItem {
372
381
///
373
382
/// See [`Attachment::ItemAttachment`] for details.
374
383
// N.B.: Commented-out variants are not yet implemented.
375
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
384
+ #[ non_exhaustive]
385
+ #[ derive( Debug , Deserialize ) ]
376
386
pub enum AttachmentItem {
377
387
// Item(Item),
378
388
Message ( Message ) ,
@@ -413,14 +423,13 @@ impl XmlSerialize for DateTime {
413
423
/// An email message.
414
424
///
415
425
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref>
416
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
426
+ #[ derive( Debug , Deserialize ) ]
417
427
#[ serde( rename_all = "PascalCase" ) ]
418
428
pub struct Message {
419
429
/// The MIME content of the item.
420
430
pub mime_content : Option < MimeContent > ,
421
-
422
431
/// The item's Exchange identifier.
423
- pub item_id : Option < ItemId > ,
432
+ pub item_id : ItemId ,
424
433
425
434
/// The identifier for the containing folder.
426
435
///
@@ -436,7 +445,6 @@ pub struct Message {
436
445
///
437
446
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/subject>
438
447
pub subject : Option < String > ,
439
-
440
448
pub sensitivity : Option < Sensitivity > ,
441
449
pub body : Option < Body > ,
442
450
pub attachments : Option < Attachments > ,
@@ -447,7 +455,6 @@ pub struct Message {
447
455
///
448
456
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/categories-ex15websvcsotherref>
449
457
pub categories : Option < Vec < StringElement > > ,
450
-
451
458
pub importance : Option < Importance > ,
452
459
pub in_reply_to : Option < String > ,
453
460
pub is_submitted : Option < bool > ,
@@ -465,21 +472,21 @@ pub struct Message {
465
472
pub display_to : Option < String > ,
466
473
pub has_attachments : Option < bool > ,
467
474
pub culture : Option < String > ,
468
- pub sender : Option < SingleRecipient > ,
475
+ pub sender : Option < Recipient > ,
469
476
pub to_recipients : Option < ArrayOfRecipients > ,
470
477
pub cc_recipients : Option < ArrayOfRecipients > ,
471
478
pub bcc_recipients : Option < ArrayOfRecipients > ,
472
479
pub is_read_receipt_requested : Option < bool > ,
473
480
pub is_delivery_receipt_requested : Option < bool > ,
474
481
pub conversation_index : Option < String > ,
475
482
pub conversation_topic : Option < String > ,
476
- pub from : Option < SingleRecipient > ,
483
+ pub from : Option < Recipient > ,
477
484
pub internet_message_id : Option < String > ,
478
485
pub is_read : Option < bool > ,
479
486
pub is_response_requested : Option < bool > ,
480
- pub reply_to : Option < SingleRecipient > ,
481
- pub received_by : Option < SingleRecipient > ,
482
- pub received_representing : Option < SingleRecipient > ,
487
+ pub reply_to : Option < Recipient > ,
488
+ pub received_by : Option < Recipient > ,
489
+ pub received_representing : Option < Recipient > ,
483
490
pub last_modified_name : Option < String > ,
484
491
pub last_modified_time : Option < DateTime > ,
485
492
pub is_associated : Option < bool > ,
@@ -496,18 +503,61 @@ pub struct Attachments {
496
503
pub inner : Vec < Attachment > ,
497
504
}
498
505
506
+ /// A newtype around a vector of `Recipient`s, that is deserialized using
507
+ /// `deserialize_recipients`.
508
+ #[ derive( Debug , Default , Deserialize , XmlSerialize ) ]
509
+ pub struct ArrayOfRecipients (
510
+ #[ serde( deserialize_with = "deserialize_recipients" ) ] pub Vec < Recipient > ,
511
+ ) ;
512
+
513
+ impl Deref for ArrayOfRecipients {
514
+ type Target = Vec < Recipient > ;
515
+
516
+ fn deref ( & self ) -> & Self :: Target {
517
+ & self . 0
518
+ }
519
+ }
520
+
521
+ impl DerefMut for ArrayOfRecipients {
522
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
523
+ & mut self . 0
524
+ }
525
+ }
526
+
499
527
/// A single mailbox.
500
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
528
+ #[ derive( Debug , Deserialize , XmlSerialize , PartialEq ) ]
501
529
#[ serde( rename_all = "PascalCase" ) ]
502
- pub struct SingleRecipient {
530
+ pub struct Recipient {
531
+ #[ xml_struct( ns_prefix = "t" ) ]
503
532
pub mailbox : Mailbox ,
504
533
}
505
534
506
- /// A list of mailboxes.
507
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
508
- #[ serde( rename_all = "PascalCase" ) ]
509
- pub struct ArrayOfRecipients {
510
- pub mailbox : Vec < Mailbox > ,
535
+ /// Deserializes a list of recipients.
536
+ ///
537
+ /// `quick-xml`'s `serde` implementation requires the presence of an
538
+ /// intermediate type when dealing with lists, and this is not compatible with
539
+ /// our model for serialization.
540
+ ///
541
+ /// We could directly deserialize into a `Vec<Mailbox>`, which would also
542
+ /// simplify this function a bit, but this would mean using different models
543
+ /// to represent single vs. multiple recipient(s).
544
+ fn deserialize_recipients < ' de , D > ( deserializer : D ) -> Result < Vec < Recipient > , D :: Error >
545
+ where
546
+ D : Deserializer < ' de > ,
547
+ {
548
+ #[ derive( Debug , Deserialize ) ]
549
+ #[ serde( rename_all = "PascalCase" ) ]
550
+ struct MailboxSequence {
551
+ mailbox : Vec < Mailbox > ,
552
+ }
553
+
554
+ let seq = MailboxSequence :: deserialize ( deserializer) ?;
555
+
556
+ Ok ( seq
557
+ . mailbox
558
+ . into_iter ( )
559
+ . map ( |mailbox| Recipient { mailbox } )
560
+ . collect ( ) )
511
561
}
512
562
513
563
/// A list of Internet Message Format headers.
@@ -520,13 +570,15 @@ pub struct InternetMessageHeaders {
520
570
/// A reference to a user or address which can send or receive mail.
521
571
///
522
572
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailbox>
523
- #[ derive( Debug , Deserialize , XmlSerialize ) ]
573
+ #[ derive( Clone , Debug , Deserialize , XmlSerialize , PartialEq ) ]
524
574
#[ serde( rename_all = "PascalCase" ) ]
525
575
pub struct Mailbox {
526
576
/// The name of this mailbox's user.
577
+ #[ xml_struct( ns_prefix = "t" ) ]
527
578
pub name : Option < String > ,
528
579
529
580
/// The email address for this mailbox.
581
+ #[ xml_struct( ns_prefix = "t" ) ]
530
582
pub email_address : String ,
531
583
532
584
/// The protocol used in routing to this mailbox.
@@ -547,7 +599,7 @@ pub struct Mailbox {
547
599
/// A protocol used in routing mail.
548
600
///
549
601
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/routingtype-emailaddress>
550
- #[ derive( Clone , Copy , Debug , Default , Deserialize , XmlSerialize ) ]
602
+ #[ derive( Clone , Copy , Debug , Default , Deserialize , XmlSerialize , PartialEq ) ]
551
603
#[ xml_struct( text) ]
552
604
pub enum RoutingType {
553
605
#[ default]
@@ -558,7 +610,7 @@ pub enum RoutingType {
558
610
/// The type of sender or recipient a mailbox represents.
559
611
///
560
612
/// See <https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/mailboxtype>
561
- #[ derive( Clone , Copy , Debug , Deserialize , XmlSerialize ) ]
613
+ #[ derive( Clone , Copy , Debug , Deserialize , XmlSerialize , PartialEq ) ]
562
614
#[ xml_struct( text) ]
563
615
pub enum MailboxType {
564
616
Mailbox ,
@@ -829,3 +881,105 @@ pub struct MessageXml {
829
881
/// if the server is throttling operations.
830
882
pub back_off_milliseconds : Option < usize > ,
831
883
}
884
+
885
+ #[ cfg( test) ]
886
+ mod tests {
887
+ use quick_xml:: Writer ;
888
+
889
+ use super :: * ;
890
+ use crate :: Error ;
891
+
892
+ /// Tests that an [`ArrayOfRecipients`] correctly serializes into XML. It
893
+ /// should serialize as multiple `<t:Mailbox>` elements, one per [`Recipient`].
894
+ #[ test]
895
+ fn serialize_array_of_recipients ( ) -> Result < ( ) , Error > {
896
+ // Define the recipients to serialize.
897
+ let alice = Recipient {
898
+ mailbox : Mailbox {
899
+ name : Some ( "Alice Test" . into ( ) ) ,
900
+ email_address : "alice@test.com" . into ( ) ,
901
+ routing_type : None ,
902
+ mailbox_type : None ,
903
+ item_id : None ,
904
+ } ,
905
+ } ;
906
+
907
+ let bob = Recipient {
908
+ mailbox : Mailbox {
909
+ name : Some ( "Bob Test" . into ( ) ) ,
910
+ email_address : "bob@test.com" . into ( ) ,
911
+ routing_type : None ,
912
+ mailbox_type : None ,
913
+ item_id : None ,
914
+ } ,
915
+ } ;
916
+
917
+ let recipients = ArrayOfRecipients ( vec ! [ alice, bob] ) ;
918
+
919
+ // Serialize into XML.
920
+ let mut writer = {
921
+ let inner: Vec < u8 > = Default :: default ( ) ;
922
+ Writer :: new ( inner)
923
+ } ;
924
+ recipients. serialize_as_element ( & mut writer, "Recipients" ) ?;
925
+
926
+ // Read the contents of the `Writer`'s buffer.
927
+ let buf = writer. into_inner ( ) ;
928
+ let actual = std:: str:: from_utf8 ( buf. as_slice ( ) )
929
+ . map_err ( |e| Error :: UnexpectedResponse ( e. to_string ( ) . into_bytes ( ) ) ) ?;
930
+
931
+ // Ensure the structure of the XML document is correct.
932
+ let expected = "<Recipients><t:Mailbox><t:Name>Alice Test</t:Name><t:EmailAddress>alice@test.com</t:EmailAddress></t:Mailbox><t:Mailbox><t:Name>Bob Test</t:Name><t:EmailAddress>bob@test.com</t:EmailAddress></t:Mailbox></Recipients>" ;
933
+ assert_eq ! ( expected, actual) ;
934
+
935
+ Ok ( ( ) )
936
+ }
937
+
938
+ /// Tests that deserializing a sequence of `<t:Mailbox>` XML elements
939
+ /// results in an [`ArrayOfRecipients`] with one [`Recipient`] per
940
+ /// `<t:Mailbox>` element.
941
+ #[ test]
942
+ fn deserialize_array_of_recipients ( ) -> Result < ( ) , Error > {
943
+ // The raw XML to deserialize.
944
+ let xml = "<Recipients><t:Mailbox><t:Name>Alice Test</t:Name><t:EmailAddress>alice@test.com</t:EmailAddress></t:Mailbox><t:Mailbox><t:Name>Bob Test</t:Name><t:EmailAddress>bob@test.com</t:EmailAddress></t:Mailbox></Recipients>" ;
945
+
946
+ // Deserialize the raw XML, with `serde_path_to_error` to help
947
+ // troubleshoot any issue.
948
+ let mut de = quick_xml:: de:: Deserializer :: from_reader ( xml. as_bytes ( ) ) ;
949
+ let recipients: ArrayOfRecipients = serde_path_to_error:: deserialize ( & mut de) ?;
950
+
951
+ // Ensure we have the right number of recipients in the resulting
952
+ // `ArrayOfRecipients`.
953
+ assert_eq ! ( recipients. 0 . len( ) , 2 ) ;
954
+
955
+ // Ensure the first recipient correctly has a name and address.
956
+ assert_eq ! (
957
+ recipients. get( 0 ) . expect( "no recipient at index 0" ) ,
958
+ & Recipient {
959
+ mailbox: Mailbox {
960
+ name: Some ( "Alice Test" . into( ) ) ,
961
+ email_address: "alice@test.com" . into( ) ,
962
+ routing_type: None ,
963
+ mailbox_type: None ,
964
+ item_id: None ,
965
+ } ,
966
+ }
967
+ ) ;
968
+
969
+ // Ensure the second recipient correctly has a name and address.
970
+ assert_eq ! (
971
+ recipients. get( 1 ) . expect( "no recipient at index 1" ) ,
972
+ & Recipient {
973
+ mailbox: Mailbox {
974
+ name: Some ( "Bob Test" . into( ) ) ,
975
+ email_address: "bob@test.com" . into( ) ,
976
+ routing_type: None ,
977
+ mailbox_type: None ,
978
+ item_id: None ,
979
+ } ,
980
+ }
981
+ ) ;
982
+
983
+ Ok ( ( ) )
984
+ }
985
+ }
0 commit comments