1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! L2CAP
16 
17 use crate::wrapper::{ClosureCallback, PyObjectExt};
18 use pyo3::{intern, PyObject, PyResult, Python};
19 
20 /// L2CAP connection-oriented channel
21 pub struct LeConnectionOrientedChannel(PyObject);
22 
23 impl LeConnectionOrientedChannel {
24     /// Create a LeConnectionOrientedChannel that wraps the provided obj.
from(obj: PyObject) -> Self25     pub(crate) fn from(obj: PyObject) -> Self {
26         Self(obj)
27     }
28 
29     /// Queues data to be automatically sent across this channel.
write(&mut self, data: &[u8]) -> PyResult<()>30     pub fn write(&mut self, data: &[u8]) -> PyResult<()> {
31         Python::with_gil(|py| self.0.call_method1(py, intern!(py, "write"), (data,))).map(|_| ())
32     }
33 
34     /// Wait for queued data to be sent on this channel.
drain(&mut self) -> PyResult<()>35     pub async fn drain(&mut self) -> PyResult<()> {
36         Python::with_gil(|py| {
37             self.0
38                 .call_method0(py, intern!(py, "drain"))
39                 .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
40         })?
41         .await
42         .map(|_| ())
43     }
44 
45     /// Register a callback to be called when the channel is closed.
on_close( &mut self, callback: impl Fn(Python) -> PyResult<()> + Send + 'static, ) -> PyResult<()>46     pub fn on_close(
47         &mut self,
48         callback: impl Fn(Python) -> PyResult<()> + Send + 'static,
49     ) -> PyResult<()> {
50         let boxed = ClosureCallback::new(move |py, _args, _kwargs| callback(py));
51 
52         Python::with_gil(|py| {
53             self.0
54                 .call_method1(py, intern!(py, "add_listener"), ("close", boxed))
55         })
56         .map(|_| ())
57     }
58 
59     /// Register a callback to be called when the channel receives data.
set_sink( &mut self, callback: impl Fn(Python, &[u8]) -> PyResult<()> + Send + 'static, ) -> PyResult<()>60     pub fn set_sink(
61         &mut self,
62         callback: impl Fn(Python, &[u8]) -> PyResult<()> + Send + 'static,
63     ) -> PyResult<()> {
64         let boxed = ClosureCallback::new(move |py, args, _kwargs| {
65             callback(py, args.get_item(0)?.extract()?)
66         });
67         Python::with_gil(|py| self.0.setattr(py, intern!(py, "sink"), boxed)).map(|_| ())
68     }
69 
70     /// Disconnect the l2cap channel.
71     /// Must be called from a thread with a Python event loop, which should be true on
72     /// `tokio::main` and `async_std::main`.
73     ///
74     /// For more info, see <https://awestlake87.github.io/pyo3-asyncio/master/doc/pyo3_asyncio/#event-loop-references-and-contextvars>.
disconnect(&mut self) -> PyResult<()>75     pub async fn disconnect(&mut self) -> PyResult<()> {
76         Python::with_gil(|py| {
77             self.0
78                 .call_method0(py, intern!(py, "disconnect"))
79                 .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
80         })?
81         .await
82         .map(|_| ())
83     }
84 
85     /// Returns some information about the channel as a [String].
debug_string(&self) -> PyResult<String>86     pub fn debug_string(&self) -> PyResult<String> {
87         Python::with_gil(|py| {
88             let str_obj = self.0.call_method0(py, intern!(py, "__str__"))?;
89             str_obj.gil_ref(py).extract()
90         })
91     }
92 }
93