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