tokio_udev/
lib.rs

1// SPDX-FileCopyrightText: © 2020 Jean-Pierre De Jesus DIAZ <me@jeandudey.tech>
2// SPDX-FileCopyrightText: © 2023 Sjoerd Simons <sjoerd@collabora.com>
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! # tokio-udev
6//!
7//! This library implements an stream of device events from `udev`
8//! asynchronously.
9//!
10//! # Usage
11//!
12//! First put the dependency on your crate's `Cargo.toml`. For example:
13//!
14//! ```toml
15//! [dependencies]
16//! tokio-udev = "0.9"
17//! ```
18
19#![cfg(target_os = "linux")]
20
21pub use udev::{
22    Attributes, Device, Enumerator, Event, EventType, MonitorBuilder,
23    MonitorSocket, Properties,
24};
25
26use futures_core::stream::Stream;
27use std::{convert::TryFrom, io, pin::Pin, sync::Mutex, task::Poll};
28use tokio::io::unix::AsyncFd;
29
30/// Asynchronous stream of device events.
31pub struct AsyncMonitorSocket {
32    inner: Mutex<Inner>,
33}
34
35impl AsyncMonitorSocket {
36    /// Construct a tokio-udev [`AsyncMonitorSocket`] from an existing one.
37    pub fn new(monitor: MonitorSocket) -> io::Result<AsyncMonitorSocket> {
38        Ok(AsyncMonitorSocket {
39            inner: Mutex::new(Inner::new(monitor)?),
40        })
41    }
42}
43
44impl TryFrom<MonitorSocket> for AsyncMonitorSocket {
45    type Error = io::Error;
46
47    fn try_from(
48        monitor: MonitorSocket,
49    ) -> Result<AsyncMonitorSocket, Self::Error> {
50        AsyncMonitorSocket::new(monitor)
51    }
52}
53
54impl Stream for AsyncMonitorSocket {
55    type Item = Result<udev::Event, io::Error>;
56
57    fn poll_next(
58        self: Pin<&mut Self>,
59        ctx: &mut std::task::Context,
60    ) -> Poll<Option<Self::Item>> {
61        self.inner.lock().unwrap().poll_receive(ctx)
62    }
63}
64
65struct Inner {
66    fd: AsyncFd<MonitorSocket>,
67}
68
69impl Inner {
70    fn new(monitor: MonitorSocket) -> io::Result<Inner> {
71        Ok(Inner {
72            fd: AsyncFd::new(monitor)?,
73        })
74    }
75
76    fn poll_receive(
77        &mut self,
78        ctx: &mut std::task::Context,
79    ) -> Poll<Option<Result<Event, io::Error>>> {
80        loop {
81            if let Some(e) = self.fd.get_mut().iter().next() {
82                return Poll::Ready(Some(Ok(e)));
83            }
84            match self.fd.poll_read_ready(ctx) {
85                Poll::Ready(Ok(mut ready_guard)) => {
86                    ready_guard.clear_ready();
87                }
88                Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err))),
89                Poll::Pending => return Poll::Pending,
90            }
91        }
92    }
93}