(&mut self, command: &str)
| 503 | } |
| 504 | |
| 505 | fn run_command(&mut self, command: &str) -> Result<ImapCommandOutput, EmailServiceError> { |
| 506 | let tag = format!("A{:04}", self.next_tag); |
| 507 | self.next_tag = self.next_tag.saturating_add(1); |
| 508 | |
| 509 | let command_line = format!("{tag} {command}\r\n"); |
| 510 | self.stream |
| 511 | .get_mut() |
| 512 | .write_all(command_line.as_bytes()) |
| 513 | .map_err(|e| EmailServiceError::Api(format!("failed to write IMAP command: {e}")))?; |
| 514 | self.stream |
| 515 | .get_mut() |
| 516 | .flush() |
| 517 | .map_err(|e| EmailServiceError::Api(format!("failed to flush IMAP command: {e}")))?; |
| 518 | |
| 519 | let mut lines = Vec::new(); |
| 520 | let mut literals = Vec::new(); |
| 521 | |
| 522 | loop { |
| 523 | let line = self.read_line()?; |
| 524 | let literal_size = parse_literal_size(&line); |
| 525 | lines.push(line.clone()); |
| 526 | |
| 527 | if let Some(size) = literal_size { |
| 528 | let mut literal = vec![0u8; size]; |
| 529 | self.stream.read_exact(&mut literal).map_err(|e| { |
| 530 | EmailServiceError::Api(format!("failed to read IMAP literal: {e}")) |
| 531 | })?; |
| 532 | literals.push(literal); |
| 533 | } |
| 534 | |
| 535 | if line.starts_with(&tag) { |
| 536 | let upper = line.to_ascii_uppercase(); |
| 537 | if !upper.contains(" OK") { |
| 538 | return Err(EmailServiceError::Api(format!( |
| 539 | "IMAP command '{command}' failed: {line}" |
| 540 | ))); |
| 541 | } |
| 542 | break; |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | Ok(ImapCommandOutput { lines, literals }) |
| 547 | } |
| 548 | |
| 549 | fn read_line(&mut self) -> Result<String, EmailServiceError> { |
| 550 | let mut line = String::new(); |
no test coverage detected