// 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 . 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 { Python::with_gil(|py| { let str_obj = self.0.call_method0(py, intern!(py, "__str__"))?; str_obj.gil_ref(py).extract() }) } }