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 //! Controller components
16 use crate::wrapper::{
17     common::{TransportSink, TransportSource},
18     hci::Address,
19     link::Link,
20     wrap_python_async, PyDictExt,
21 };
22 use pyo3::{
23     intern,
24     types::{PyDict, PyModule},
25     PyObject, PyResult, Python,
26 };
27 use pyo3_asyncio::tokio::into_future;
28 
29 /// A controller that can send and receive HCI frames via some link
30 #[derive(Clone)]
31 pub struct Controller(pub(crate) PyObject);
32 
33 impl Controller {
34     /// Creates a new [Controller] object. When optional arguments are not specified, the Python
35     /// module specifies the defaults. Must be called from a thread with a Python event loop, which
36     /// should be true on `tokio::main` and `async_std::main`.
37     ///
38     /// For more info, see <https://awestlake87.github.io/pyo3-asyncio/master/doc/pyo3_asyncio/#event-loop-references-and-contextvars>.
new( name: &str, host_source: Option<TransportSource>, host_sink: Option<TransportSink>, link: Option<Link>, public_address: Option<Address>, ) -> PyResult<Self>39     pub async fn new(
40         name: &str,
41         host_source: Option<TransportSource>,
42         host_sink: Option<TransportSink>,
43         link: Option<Link>,
44         public_address: Option<Address>,
45     ) -> PyResult<Self> {
46         Python::with_gil(|py| {
47             let controller_ctr = PyModule::import(py, intern!(py, "bumble.controller"))?
48                 .getattr(intern!(py, "Controller"))?;
49 
50             let kwargs = PyDict::new(py);
51             kwargs.set_item("name", name)?;
52             kwargs.set_opt_item("host_source", host_source)?;
53             kwargs.set_opt_item("host_sink", host_sink)?;
54             kwargs.set_opt_item("link", link)?;
55             kwargs.set_opt_item("public_address", public_address)?;
56 
57             // Controller constructor (`__init__`) is not (and can't be) marked async, but calls
58             // `get_running_loop`, and thus needs wrapped in an async function.
59             wrap_python_async(py, controller_ctr)?
60                 .call((), Some(kwargs))
61                 .and_then(into_future)
62         })?
63         .await
64         .map(Self)
65     }
66 }
67