Skip to main content

imsg_map/client/
control.rs

1//! Folder listing, notification registration, and session teardown.
2
3use bytes::Bytes;
4use formats::xml::FolderListing;
5use futures::{SinkExt, StreamExt};
6use obex_core::{client::ObexClient, headers::Header};
7use tokio::io::{AsyncRead, AsyncWrite};
8
9use super::MapClient;
10use crate::{params::set_notification_registration_params, MapError};
11
12impl<T: AsyncRead + AsyncWrite + Unpin> MapClient<T> {
13    /// Returns the MAP folder listing for the current object store level via `GetFolderListing`
14    /// GET with Type `x-obex/folder-listing`. Folders are in device-reported document order.
15    ///
16    /// # Errors
17    ///
18    /// Returns [`MapError::ServerError`] if the remote returns a non-OK response.
19    /// Returns [`MapError::FolderListing`] if the response body is malformed XML.
20    /// Returns [`MapError::Obex`] or [`MapError::Transport`] on lower-layer failure.
21    pub async fn get_folder_listing(&mut self) -> Result<FolderListing, MapError> {
22        let req = self.obex.get_request(b"x-obex/folder-listing\x00", None, None)?;
23        self.transport.send(req).await?;
24        let body = self.collect_body().await?;
25        Ok(FolderListing::parse(&body)?)
26    }
27
28    /// When `enable` is `true`, the phone will connect to the MNS channel and push event reports.
29    /// When `false`, it stops. The caller must keep the MAP session alive while notifications are active.
30    ///
31    /// # Errors
32    ///
33    /// Returns [`MapError::ServerError`] if the remote returns a non-OK response.
34    /// Returns [`MapError::Obex`] or [`MapError::Transport`] on lower-layer failure.
35    pub async fn set_notification_registration(&mut self, enable: bool) -> Result<(), MapError> {
36        let req = self.obex.put_final_request(
37            b"x-bt/MAP-NotificationRegistration\x00",
38            vec![
39                Header::AppParams(set_notification_registration_params(enable)),
40                Header::EndOfBody(Bytes::new()),
41            ],
42        )?;
43        self.transport.send(req).await?;
44        let rsp_bytes = Self::recv(&mut self.transport).await?;
45        let rsp = ObexClient::parse_response(&rsp_bytes)?;
46        if !rsp.opcode.is_ok() {
47            return Err(MapError::ServerError(rsp.opcode.to_byte()));
48        }
49        Ok(())
50    }
51
52    /// Reads from the transport until the remote closes the stream, discarding all received
53    /// packets. Returns `Ok(())` on clean close. Does not send OBEX DISCONNECT and does not
54    /// parse received packet opcodes.
55    ///
56    /// # Errors
57    ///
58    /// Returns [`MapError::Transport`] on a framing error from the underlying codec.
59    pub async fn hold(&mut self) -> Result<(), MapError> {
60        loop {
61            match self.transport.next().await {
62                None => return Ok(()),
63                Some(Ok(_)) => {}
64                Some(Err(e)) => return Err(MapError::Transport(e)),
65            }
66        }
67    }
68
69    /// Sends OBEX DISCONNECT and awaits the server acknowledgement.
70    ///
71    /// Consumes `self` — the session is unusable after this call regardless of the outcome.
72    /// Does not close the underlying stream; the stream is dropped when `self` is consumed.
73    ///
74    /// # Errors
75    ///
76    /// Returns [`MapError`] if the request cannot be encoded, the transport fails, or the
77    /// server returns a non-OK response.
78    pub async fn disconnect(mut self) -> Result<(), MapError> {
79        let req = self.obex.disconnect_request()?;
80        self.transport.send(req).await?;
81        let rsp_bytes = Self::recv(&mut self.transport).await?;
82        let rsp = ObexClient::parse_response(&rsp_bytes)?;
83        if !rsp.opcode.is_ok() {
84            return Err(MapError::ServerError(rsp.opcode.to_byte()));
85        }
86        Ok(())
87    }
88}