1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct EmailConfiguration {
8 #[serde(rename = "compartmentId")]
10 pub compartment_id: String,
11
12 #[serde(rename = "httpSubmitEndpoint")]
14 pub http_submit_endpoint: String,
15
16 #[serde(rename = "smtpSubmitEndpoint")]
18 pub smtp_submit_endpoint: String,
19
20 #[serde(rename = "emailDeliveryConfigId")]
22 pub email_delivery_config_id: Option<String>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Email {
28 #[serde(rename = "messageId", skip_serializing_if = "Option::is_none")]
30 pub message_id: Option<String>,
31
32 pub sender: Sender,
34
35 pub recipients: Recipients,
37
38 pub subject: String,
40
41 #[serde(rename = "bodyHtml", skip_serializing_if = "Option::is_none")]
43 pub body_html: Option<String>,
44
45 #[serde(rename = "bodyText", skip_serializing_if = "Option::is_none")]
47 pub body_text: Option<String>,
48
49 #[serde(rename = "replyTo", skip_serializing_if = "Option::is_none")]
51 pub reply_to: Option<Vec<EmailAddress>>,
52
53 #[serde(rename = "headerFields", skip_serializing_if = "Option::is_none")]
55 pub headers: Option<std::collections::HashMap<String, String>>,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60pub struct Sender {
61 #[serde(rename = "senderAddress")]
63 pub sender_address: EmailAddress,
64
65 #[serde(rename = "compartmentId")]
67 pub compartment_id: String,
68}
69
70impl Sender {
71 pub fn new(email: impl Into<String>) -> Self {
73 Self {
74 sender_address: EmailAddress::new(email),
75 compartment_id: String::new(), }
77 }
78
79 pub fn with_name(email: impl Into<String>, name: impl Into<String>) -> Self {
81 Self {
82 sender_address: EmailAddress::with_name(email, name),
83 compartment_id: String::new(), }
85 }
86
87 pub(crate) fn set_compartment_id(&mut self, compartment_id: impl Into<String>) {
89 self.compartment_id = compartment_id.into();
90 }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct EmailAddress {
96 pub email: String,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub name: Option<String>,
102}
103
104impl PartialEq for EmailAddress {
106 fn eq(&self, other: &Self) -> bool {
107 self.email == other.email
108 }
109}
110
111impl Eq for EmailAddress {}
112
113impl std::hash::Hash for EmailAddress {
115 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
116 self.email.hash(state);
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub struct Recipients {
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub to: Option<Vec<EmailAddress>>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub cc: Option<Vec<EmailAddress>>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub bcc: Option<Vec<EmailAddress>>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct SubmitEmailResponse {
139 #[serde(rename = "messageId")]
141 pub message_id: String,
142
143 #[serde(rename = "envelopeId")]
145 pub envelope_id: String,
146
147 #[serde(
149 rename = "suppressedRecipients",
150 skip_serializing_if = "Option::is_none"
151 )]
152 pub suppressed_recipients: Option<Vec<EmailAddress>>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct SenderSummary {
158 pub id: String,
160
161 #[serde(rename = "emailAddress")]
163 pub email_address: String,
164
165 #[serde(rename = "lifecycleState")]
167 pub lifecycle_state: SenderLifecycleState,
168
169 #[serde(rename = "timeCreated")]
171 pub time_created: String,
172
173 #[serde(rename = "isSpf", skip_serializing_if = "Option::is_none")]
175 pub is_spf: Option<bool>,
176
177 #[serde(rename = "compartmentId", skip_serializing_if = "Option::is_none")]
179 pub compartment_id: Option<String>,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
185pub enum SenderLifecycleState {
186 Creating,
188 Active,
190 NeedsAttention,
192 Inactive,
194 Failed,
196 Deleting,
198 Deleted,
200}
201
202impl EmailAddress {
203 pub fn new(email: impl Into<String>) -> Self {
205 Self {
206 email: email.into(),
207 name: None,
208 }
209 }
210
211 pub fn with_name(email: impl Into<String>, name: impl Into<String>) -> Self {
213 Self {
214 email: email.into(),
215 name: Some(name.into()),
216 }
217 }
218}
219
220impl Recipients {
221 fn deduplicate(addresses: Vec<EmailAddress>) -> Vec<EmailAddress> {
223 use std::collections::HashSet;
224 let mut seen = HashSet::new();
225 addresses
226 .into_iter()
227 .filter(|addr| seen.insert(addr.clone()))
228 .collect()
229 }
230
231 pub fn new(addresses: Vec<EmailAddress>) -> Self {
233 Self::to(addresses)
234 }
235
236 pub fn to(addresses: Vec<EmailAddress>) -> Self {
238 Self {
239 to: Some(Self::deduplicate(addresses)),
240 cc: None,
241 bcc: None,
242 }
243 }
244
245 pub fn cc(addresses: Vec<EmailAddress>) -> Self {
247 Self {
248 to: None,
249 cc: Some(Self::deduplicate(addresses)),
250 bcc: None,
251 }
252 }
253
254 pub fn bcc(addresses: Vec<EmailAddress>) -> Self {
256 Self {
257 to: None,
258 cc: None,
259 bcc: Some(Self::deduplicate(addresses)),
260 }
261 }
262
263 pub fn add_to(mut self, mut addresses: Vec<EmailAddress>) -> Self {
265 if let Some(ref mut to) = self.to {
266 to.append(&mut addresses);
267 *to = Self::deduplicate(to.clone());
268 } else {
269 self.to = Some(Self::deduplicate(addresses));
270 }
271 self
272 }
273
274 pub fn add_cc(mut self, mut addresses: Vec<EmailAddress>) -> Self {
276 if let Some(ref mut cc) = self.cc {
277 cc.append(&mut addresses);
278 *cc = Self::deduplicate(cc.clone());
279 } else {
280 self.cc = Some(Self::deduplicate(addresses));
281 }
282 self
283 }
284
285 pub fn add_bcc(mut self, mut addresses: Vec<EmailAddress>) -> Self {
287 if let Some(ref mut bcc) = self.bcc {
288 bcc.append(&mut addresses);
289 *bcc = Self::deduplicate(bcc.clone());
290 } else {
291 self.bcc = Some(Self::deduplicate(addresses));
292 }
293 self
294 }
295
296 pub fn builder() -> RecipientsBuilder {
298 RecipientsBuilder::default()
299 }
300}
301
302#[derive(Debug, Default)]
304pub struct RecipientsBuilder {
305 to: Option<Vec<EmailAddress>>,
306 cc: Option<Vec<EmailAddress>>,
307 bcc: Option<Vec<EmailAddress>>,
308}
309
310impl RecipientsBuilder {
311 pub fn to(mut self, addresses: Vec<EmailAddress>) -> Self {
313 self.to = Some(Recipients::deduplicate(addresses));
314 self
315 }
316
317 pub fn cc(mut self, addresses: Vec<EmailAddress>) -> Self {
319 self.cc = Some(Recipients::deduplicate(addresses));
320 self
321 }
322
323 pub fn bcc(mut self, addresses: Vec<EmailAddress>) -> Self {
325 self.bcc = Some(Recipients::deduplicate(addresses));
326 self
327 }
328
329 pub fn build(self) -> Recipients {
331 Recipients {
332 to: self.to,
333 cc: self.cc,
334 bcc: self.bcc,
335 }
336 }
337}
338
339impl Email {
340 pub fn builder() -> EmailBuilder {
342 EmailBuilder::default()
343 }
344}
345
346#[derive(Debug, Default)]
348pub struct EmailBuilder {
349 message_id: Option<String>,
350 sender: Option<EmailAddress>,
351 recipients: Option<Recipients>,
352 subject: Option<String>,
353 body_html: Option<String>,
354 body_text: Option<String>,
355 reply_to: Option<Vec<EmailAddress>>,
356 headers: Option<std::collections::HashMap<String, String>>,
357}
358
359impl EmailBuilder {
360 pub fn message_id(mut self, message_id: impl Into<String>) -> Self {
362 self.message_id = Some(message_id.into());
363 self
364 }
365
366 pub fn sender(mut self, sender: EmailAddress) -> Self {
368 self.sender = Some(sender);
369 self
370 }
371
372 pub fn recipients(mut self, recipients: Recipients) -> Self {
374 self.recipients = Some(recipients);
375 self
376 }
377
378 pub fn subject(mut self, subject: impl Into<String>) -> Self {
380 self.subject = Some(subject.into());
381 self
382 }
383
384 pub fn body_html(mut self, body_html: impl Into<String>) -> Self {
386 self.body_html = Some(body_html.into());
387 self
388 }
389
390 pub fn body_text(mut self, body_text: impl Into<String>) -> Self {
392 self.body_text = Some(body_text.into());
393 self
394 }
395
396 pub fn reply_to(mut self, reply_to: Vec<EmailAddress>) -> Self {
398 self.reply_to = Some(reply_to);
399 self
400 }
401
402 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
404 self.headers = Some(headers);
405 self
406 }
407
408 pub fn build(self) -> crate::error::Result<Email> {
412 let sender_address = self
413 .sender
414 .ok_or_else(|| crate::error::Error::ConfigError("Sender is required".to_string()))?;
415
416 let sender = Sender {
418 sender_address,
419 compartment_id: String::new(),
420 };
421
422 let recipients = self.recipients.ok_or_else(|| {
423 crate::error::Error::ConfigError("Recipients are required".to_string())
424 })?;
425
426 let subject = self.subject.ok_or_else(|| {
427 crate::error::Error::ConfigError("Subject is required".to_string())
428 })?;
429
430 if self.body_html.is_none() && self.body_text.is_none() {
432 return Err(crate::error::Error::ConfigError(
433 "At least one of body_html or body_text is required".to_string(),
434 ));
435 }
436
437 Ok(Email {
438 message_id: self.message_id,
439 sender,
440 recipients,
441 subject,
442 body_html: self.body_html,
443 body_text: self.body_text,
444 reply_to: self.reply_to,
445 headers: self.headers,
446 })
447 }
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453
454 #[test]
455 fn test_email_address_new() {
456 let addr = EmailAddress::new("test@example.com");
457 assert_eq!(addr.email, "test@example.com");
458 assert_eq!(addr.name, None);
459 }
460
461 #[test]
462 fn test_email_address_with_name() {
463 let addr = EmailAddress::with_name("test@example.com", "Test User");
464 assert_eq!(addr.email, "test@example.com");
465 assert_eq!(addr.name, Some("Test User".to_string()));
466 }
467
468 #[test]
469 fn test_recipients_to() {
470 let recipients = Recipients::to(vec![
471 EmailAddress::new("user1@example.com"),
472 EmailAddress::new("user2@example.com"),
473 ]);
474 assert_eq!(recipients.to.as_ref().unwrap().len(), 2);
475 assert_eq!(recipients.cc, None);
476 assert_eq!(recipients.bcc, None);
477 }
478
479 #[test]
480 fn test_recipients_builder() {
481 let recipients = Recipients::builder()
482 .to(vec![EmailAddress::new("to@example.com")])
483 .cc(vec![EmailAddress::new("cc@example.com")])
484 .bcc(vec![EmailAddress::new("bcc@example.com")])
485 .build();
486
487 assert_eq!(recipients.to.as_ref().unwrap().len(), 1);
488 assert_eq!(recipients.cc.as_ref().unwrap().len(), 1);
489 assert_eq!(recipients.bcc.as_ref().unwrap().len(), 1);
490 }
491
492 #[test]
493 fn test_submit_email_request_serialization() {
494 let mut request = Email {
495 message_id: Some("test-123".to_string()),
496 sender: Sender::with_name("sender@example.com", "Sender"),
497 recipients: Recipients::to(vec![EmailAddress::new("recipient@example.com")]),
498 subject: "Test Subject".to_string(),
499 body_html: Some("<html><body>Test</body></html>".to_string()),
500 body_text: Some("Test".to_string()),
501 reply_to: None,
502 headers: None,
503 };
504 request.sender.set_compartment_id("ocid1.compartment.test");
506
507 let json = serde_json::to_string(&request).unwrap();
508 assert!(json.contains("\"sender\""));
509 assert!(json.contains("\"recipients\""));
510 assert!(json.contains("\"subject\""));
511 assert!(json.contains("\"messageId\""));
512 }
513
514 #[test]
515 fn test_submit_email_request_builder() {
516 let mut request = Email::builder()
517 .sender(EmailAddress::new("sender@example.com"))
518 .recipients(
519 Recipients::builder()
520 .to(vec![EmailAddress::new("recipient@example.com")])
521 .build(),
522 )
523 .subject("Test Subject")
524 .body_text("Test body")
525 .build()
526 .unwrap();
527 request.sender.set_compartment_id("ocid1.compartment.test");
529
530 assert_eq!(request.subject, "Test Subject");
531 assert_eq!(request.body_text.as_ref().unwrap(), "Test body");
532 assert!(request.recipients.to.is_some());
533 }
534
535 #[test]
536 fn test_submit_email_request_builder_missing_required_fields() {
537 let result = Email::builder()
539 .recipients(Recipients::to(vec![EmailAddress::new("to@example.com")]))
540 .subject("Test")
541 .build();
542 assert!(result.is_err());
543
544 let result = Email::builder()
546 .sender(EmailAddress::new("sender@example.com"))
547 .subject("Test")
548 .build();
549 assert!(result.is_err());
550
551 let result = Email::builder()
553 .sender(EmailAddress::new("sender@example.com"))
554 .recipients(Recipients::to(vec![EmailAddress::new("to@example.com")]))
555 .build();
556 assert!(result.is_err());
557 }
558
559 #[test]
560 fn test_email_configuration_deserialization() {
561 let json = r#"{
562 "compartmentId": "ocid1.compartment.test",
563 "httpSubmitEndpoint": "https://email.ap-seoul-1.oci.oraclecloud.com",
564 "smtpSubmitEndpoint": "smtp.email.ap-seoul-1.oci.oraclecloud.com"
565 }"#;
566
567 let config: EmailConfiguration = serde_json::from_str(json).unwrap();
568 assert_eq!(config.compartment_id, "ocid1.compartment.test");
569 assert_eq!(
570 config.http_submit_endpoint,
571 "https://email.ap-seoul-1.oci.oraclecloud.com"
572 );
573 assert_eq!(
574 config.smtp_submit_endpoint,
575 "smtp.email.ap-seoul-1.oci.oraclecloud.com"
576 );
577 }
578
579 #[test]
580 fn test_submit_email_response_deserialization() {
581 let json = r#"{
582 "messageId": "msg-123",
583 "envelopeId": "env-456"
584 }"#;
585
586 let response: SubmitEmailResponse = serde_json::from_str(json).unwrap();
587 assert_eq!(response.message_id, "msg-123");
588 assert_eq!(response.envelope_id, "env-456");
589 }
590
591 #[test]
592 fn test_complete_email_request_with_all_fields() {
593 use std::collections::HashMap;
594
595 let mut headers = HashMap::new();
596 headers.insert("X-Test".to_string(), "test-value".to_string());
597
598 let mut request = Email {
599 message_id: Some("msg-001".to_string()),
600 sender: Sender::with_name("sender@example.com", "Sender Name"),
601 recipients: Recipients {
602 to: Some(vec![EmailAddress::new("to@example.com")]),
603 cc: Some(vec![EmailAddress::new("cc@example.com")]),
604 bcc: Some(vec![EmailAddress::new("bcc@example.com")]),
605 },
606 subject: "Complete Test".to_string(),
607 body_html: Some("<p>HTML body</p>".to_string()),
608 body_text: Some("Text body".to_string()),
609 reply_to: Some(vec![EmailAddress::new("replyto@example.com")]),
610 headers: Some(headers),
611 };
612 request.sender.set_compartment_id("ocid1.compartment.test");
613
614 let json = serde_json::to_string(&request).unwrap();
615 let deserialized: Email = serde_json::from_str(&json).unwrap();
616
617 assert_eq!(deserialized.message_id, Some("msg-001".to_string()));
618 assert_eq!(deserialized.subject, "Complete Test");
619 assert!(deserialized.recipients.to.is_some());
620 assert!(deserialized.recipients.cc.is_some());
621 assert!(deserialized.recipients.bcc.is_some());
622 assert!(deserialized.reply_to.is_some());
623 assert!(deserialized.headers.is_some());
624 }
625
626 #[test]
627 fn test_complete_email_request_with_builder() {
628 use std::collections::HashMap;
629
630 let mut headers = HashMap::new();
631 headers.insert("X-Test".to_string(), "test-value".to_string());
632
633 let mut request = Email::builder()
634 .message_id("msg-001")
635 .sender(EmailAddress::with_name("sender@example.com", "Sender Name"))
636 .recipients(
637 Recipients::builder()
638 .to(vec![EmailAddress::new("to@example.com")])
639 .cc(vec![EmailAddress::new("cc@example.com")])
640 .bcc(vec![EmailAddress::new("bcc@example.com")])
641 .build(),
642 )
643 .subject("Complete Test")
644 .body_html("<p>HTML body</p>")
645 .body_text("Text body")
646 .reply_to(vec![EmailAddress::new("replyto@example.com")])
647 .headers(headers)
648 .build()
649 .unwrap();
650 request.sender.set_compartment_id("ocid1.compartment.test");
651
652 assert_eq!(request.message_id, Some("msg-001".to_string()));
653 assert_eq!(request.subject, "Complete Test");
654 assert!(request.recipients.to.is_some());
655 assert!(request.recipients.cc.is_some());
656 assert!(request.recipients.bcc.is_some());
657 assert!(request.reply_to.is_some());
658 assert!(request.headers.is_some());
659 }
660
661 #[test]
662 fn test_recipients_constructors() {
663 let recipients = Recipients::new(vec![EmailAddress::new("to@example.com")]);
665 assert!(recipients.to.is_some());
666 assert!(recipients.cc.is_none());
667 assert!(recipients.bcc.is_none());
668
669 let recipients = Recipients::to(vec![EmailAddress::new("to@example.com")]);
671 assert!(recipients.to.is_some());
672 assert!(recipients.cc.is_none());
673 assert!(recipients.bcc.is_none());
674
675 let recipients = Recipients::cc(vec![EmailAddress::new("cc@example.com")]);
677 assert!(recipients.to.is_none());
678 assert!(recipients.cc.is_some());
679 assert!(recipients.bcc.is_none());
680
681 let recipients = Recipients::bcc(vec![EmailAddress::new("bcc@example.com")]);
683 assert!(recipients.to.is_none());
684 assert!(recipients.cc.is_none());
685 assert!(recipients.bcc.is_some());
686 }
687
688 #[test]
689 fn test_recipients_add_methods() {
690 let recipients = Recipients::to(vec![EmailAddress::new("to1@example.com")])
692 .add_to(vec![EmailAddress::new("to2@example.com")])
693 .add_cc(vec![EmailAddress::new("cc@example.com")])
694 .add_bcc(vec![EmailAddress::new("bcc@example.com")]);
695
696 assert_eq!(recipients.to.as_ref().unwrap().len(), 2);
697 assert_eq!(recipients.cc.as_ref().unwrap().len(), 1);
698 assert_eq!(recipients.bcc.as_ref().unwrap().len(), 1);
699
700 let recipients = Recipients::cc(vec![EmailAddress::new("cc1@example.com")]).add_cc(vec![
702 EmailAddress::new("cc2@example.com"),
703 EmailAddress::new("cc3@example.com"),
704 ]);
705
706 assert_eq!(recipients.cc.as_ref().unwrap().len(), 3);
707
708 let recipients = Recipients::to(vec![EmailAddress::new("to@example.com")])
710 .add_bcc(vec![EmailAddress::new("bcc@example.com")]);
711
712 assert!(recipients.to.is_some());
713 assert!(recipients.bcc.is_some());
714 }
715
716 #[test]
717 fn test_build_missing_body() {
718 let result = Email::builder()
720 .sender(EmailAddress::new("sender@example.com"))
721 .recipients(Recipients::to(vec![EmailAddress::new("to@example.com")]))
722 .subject("Test")
723 .build();
724
725 assert!(result.is_err());
726 if let Err(crate::error::Error::ConfigError(msg)) = result {
727 assert!(msg.contains("body"));
728 } else {
729 panic!("Expected ConfigError about body");
730 }
731 }
732
733 #[test]
734 fn test_build_with_only_html_body() {
735 let result = Email::builder()
737 .sender(EmailAddress::new("sender@example.com"))
738 .recipients(Recipients::to(vec![EmailAddress::new("to@example.com")]))
739 .subject("Test")
740 .body_html("<p>HTML content</p>")
741 .build();
742
743 assert!(result.is_ok());
744 let request = result.unwrap();
745 assert!(request.body_html.is_some());
746 assert!(request.body_text.is_none());
747 }
748
749 #[test]
750 fn test_build_with_only_text_body() {
751 let result = Email::builder()
753 .sender(EmailAddress::new("sender@example.com"))
754 .recipients(Recipients::to(vec![EmailAddress::new("to@example.com")]))
755 .subject("Test")
756 .body_text("Text content")
757 .build();
758
759 assert!(result.is_ok());
760 let request = result.unwrap();
761 assert!(request.body_html.is_none());
762 assert!(request.body_text.is_some());
763 }
764
765 #[test]
766 fn test_recipients_deduplication() {
767 let recipients = Recipients::to(vec![
769 EmailAddress::new("user@example.com"),
770 EmailAddress::new("user@example.com"), EmailAddress::new("other@example.com"),
772 ]);
773 assert_eq!(recipients.to.as_ref().unwrap().len(), 2);
774
775 let recipients = Recipients::cc(vec![
777 EmailAddress::new("cc1@example.com"),
778 EmailAddress::new("cc1@example.com"), ]);
780 assert_eq!(recipients.cc.as_ref().unwrap().len(), 1);
781
782 let recipients = Recipients::bcc(vec![
784 EmailAddress::new("bcc@example.com"),
785 EmailAddress::new("bcc@example.com"), EmailAddress::new("bcc@example.com"), ]);
788 assert_eq!(recipients.bcc.as_ref().unwrap().len(), 1);
789 }
790
791 #[test]
792 fn test_recipients_add_methods_deduplication() {
793 let recipients = Recipients::to(vec![EmailAddress::new("to@example.com")]).add_to(vec![
795 EmailAddress::new("to@example.com"), EmailAddress::new("to2@example.com"),
797 ]);
798 assert_eq!(recipients.to.as_ref().unwrap().len(), 2);
799
800 let recipients = Recipients::to(vec![EmailAddress::new("user1@example.com")])
802 .add_to(vec![EmailAddress::new("user2@example.com")])
803 .add_to(vec![
804 EmailAddress::new("user1@example.com"), EmailAddress::new("user3@example.com"),
806 ]);
807 assert_eq!(recipients.to.as_ref().unwrap().len(), 3);
808 }
809
810 #[test]
811 fn test_recipients_builder_deduplication() {
812 let recipients = Recipients::builder()
814 .to(vec![
815 EmailAddress::new("to@example.com"),
816 EmailAddress::new("to@example.com"), ])
818 .cc(vec![
819 EmailAddress::new("cc@example.com"),
820 EmailAddress::new("cc@example.com"), ])
822 .build();
823
824 assert_eq!(recipients.to.as_ref().unwrap().len(), 1);
825 assert_eq!(recipients.cc.as_ref().unwrap().len(), 1);
826 }
827
828 #[test]
829 fn test_email_address_with_name_deduplication() {
830 let recipients = Recipients::to(vec![
832 EmailAddress::new("user@example.com"),
833 EmailAddress::with_name("user@example.com", "User Name"),
834 ]);
835
836 assert_eq!(recipients.to.as_ref().unwrap().len(), 1);
838 }
839}