bluez 0.4.0

Control Bluetooth on Linux.
Documentation
//! This example allows you to chat over L2CAP with another bluetooth device.
//!
//! Copyright (c) 2022 Ibiyemi Abiodun

extern crate bluez;

use std::io::BufRead;

use anyhow::Context;
use bluez::communication::stream::BluetoothStream;

use bluez::Address;
use bluez::AddressType;
use bluez::Protocol;
use clap::Parser;
use tokio::io::BufReader;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
use tokio::spawn;

#[derive(Parser, Debug)]
struct Args {
    address: Address,
    port: u16,
}

#[tokio::main(worker_threads = 2)]
pub async fn main() -> Result<(), anyhow::Error> {
    let (input_tx, mut input_rx) = tokio::sync::mpsc::channel(16);

    std::thread::spawn(move || -> anyhow::Result<()> {
        let stdin = std::io::stdin();
        let mut stdin = stdin.lock();

        loop {
            let mut line = String::new();
            stdin.read_line(&mut line)?;
            input_tx.blocking_send(line)?;
        }
    });

    let args = Args::parse();

    let stream =
        BluetoothStream::connect(Protocol::L2CAP, args.address, AddressType::BREDR, args.port)
            .await?;

    println!(
        "l2cap client connected to {} on port {}",
        args.address, args.port
    );

    // note: using tokio::io::split for this use-case does not work, because that method
    // uses a lock internally, whereas this one does not
    let (reader, mut writer) = stream.into_split();

    let read_task = spawn(async move {
        let mut line = String::new();
        let mut reader = BufReader::new(reader);

        while reader.read_line(&mut line).await.unwrap() > 0 {
            println!("> {}", line);
            line.clear();
        }
    });

    let write_task = spawn(async move {
        loop {
            let line = input_rx.recv().await.context("stdin ended").unwrap();
            writer.write_all(line.as_bytes()).await.unwrap();
            println!("< {}", line);
        }
    });

    read_task.await?;
    write_task.abort();

    Ok(())
}