| // 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() |
| }) |
| } |
| } |