| // Copyright 2023 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| //! L2CAP | |
| use crate::wrapper::{ClosureCallback, PyObjectExt}; | |
| use pyo3::{intern, PyObject, PyResult, Python}; | |
| /// L2CAP connection-oriented channel | |
| pub struct LeConnectionOrientedChannel(PyObject); | |
| impl LeConnectionOrientedChannel { | |
| /// Create a LeConnectionOrientedChannel that wraps the provided obj. | |
| pub(crate) fn from(obj: PyObject) -> Self { | |
| Self(obj) | |
| } | |
| /// Queues data to be automatically sent across this channel. | |
| pub fn write(&mut self, data: &[u8]) -> PyResult<()> { | |
| Python::with_gil(|py| self.0.call_method1(py, intern!(py, "write"), (data,))).map(|_| ()) | |
| } | |
| /// Wait for queued data to be sent on this channel. | |
| pub async fn drain(&mut self) -> PyResult<()> { | |
| Python::with_gil(|py| { | |
| self.0 | |
| .call_method0(py, intern!(py, "drain")) | |
| .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py))) | |
| })? | |
| .await | |
| .map(|_| ()) | |
| } | |
| /// Register a callback to be called when the channel is closed. | |
| pub fn on_close( | |
| &mut self, | |
| callback: impl Fn(Python) -> PyResult<()> + Send + 'static, | |
| ) -> PyResult<()> { | |
| let boxed = ClosureCallback::new(move |py, _args, _kwargs| callback(py)); | |
| Python::with_gil(|py| { | |
| self.0 | |
| .call_method1(py, intern!(py, "add_listener"), ("close", boxed)) | |
| }) | |
| .map(|_| ()) | |
| } | |
| /// Register a callback to be called when the channel receives data. | |
| pub fn set_sink( | |
| &mut self, | |
| callback: impl Fn(Python, &[u8]) -> PyResult<()> + Send + 'static, | |
| ) -> PyResult<()> { | |
| let boxed = ClosureCallback::new(move |py, args, _kwargs| { | |
| callback(py, args.get_item(0)?.extract()?) | |
| }); | |
| Python::with_gil(|py| self.0.setattr(py, intern!(py, "sink"), boxed)).map(|_| ()) | |
| } | |
| /// Disconnect the l2cap channel. | |
| /// Must be called from a thread with a Python event loop, which should be true on | |
| /// `tokio::main` and `async_std::main`. | |
| /// | |
| /// For more info, see <https://awestlake87.github.io/pyo3-asyncio/master/doc/pyo3_asyncio/#event-loop-references-and-contextvars>. | |
| pub async fn disconnect(&mut self) -> PyResult<()> { | |
| Python::with_gil(|py| { | |
| self.0 | |
| .call_method0(py, intern!(py, "disconnect")) | |
| .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py))) | |
| })? | |
| .await | |
| .map(|_| ()) | |
| } | |
| /// Returns some information about the channel as a [String]. | |
| pub fn debug_string(&self) -> PyResult<String> { | |
| Python::with_gil(|py| { | |
| let str_obj = self.0.call_method0(py, intern!(py, "__str__"))?; | |
| str_obj.gil_ref(py).extract() | |
| }) | |
| } | |
| } |