rust_lcd/
lib.rs

1//! Iteration over backlight devices.
2//!
3//! # Usage
4//!
5//! The default directory for backlight devices is [`BACKLIGHT_PATH`],
6//! whose value is `"/sys/class/backlight"`.
7//! The `DeviceIter` iterates over...
8//!
9//! [`BACKLIGHT_PATH`]: constant.BACKLIGHT_PATH.html
10//!
11//! # Examples
12//!
13//! ```
14//! # use rust_lcd::*;
15//! use std::path::Path;
16//! for device in DeviceIter::default() {
17//!      assert_eq!(
18//!         Path::new(BACKLIGHT_PATH),
19//!         device.path().parent().unwrap());
20//! }
21//! ```
22
23#![deny(missing_docs)]
24
25use std::fs;
26use std::io;
27use std::path::{Path, PathBuf};
28
29/// The default directory where to look for devices.
30///
31/// The value under Linux is `"/sys/class/backlight"`.
32pub const BACKLIGHT_PATH: &str = "/sys/class/backlight";
33
34/// The default name of the file controlling the power of the device.
35///
36/// The value under Linux is `"bl_power"`.
37pub const BL_POWER: &str = "bl_power";
38
39/// A single backlight device that can be toggled ON and OFF.
40///
41/// # Examples
42///
43/// ```
44/// # use rust_lcd::{BL_POWER, Device};
45/// use std::path::Path;
46/// let path = Path::new("/sys/class/backlight/intel_backlight");
47/// let dev = Device::new(path);
48/// assert_eq!(dev.bl_power(), path.join(BL_POWER));
49/// assert!(dev.toggle().is_err()); // we don't have permission
50/// ```
51#[derive(Debug)]
52pub struct Device {
53    path: PathBuf,
54    bl_power: PathBuf,
55}
56
57impl Device {
58    /// Creates a new device located at `path`.
59    pub fn new<P: AsRef<Path>>(path: P) -> Self {
60        let path: &Path = path.as_ref();
61        Self {
62            path: path.to_path_buf(),
63            bl_power: path.join(BL_POWER),
64        }
65    }
66
67    // This is not exposed in the public API at the moment.
68    #[allow(unused)]
69    fn custom<P: AsRef<Path>, Q: AsRef<Path>>(path: P, bl_power: Option<Q>) -> Self {
70        let path: &Path = path.as_ref();
71        match bl_power {
72            Some(q) => Device {
73                path: path.to_path_buf(),
74                bl_power: path.join(q),
75            },
76            None => Device {
77                path: path.to_path_buf(),
78                bl_power: path.join(BL_POWER),
79            },
80        }
81    }
82
83    /// Returns the path of the device.
84    pub fn path(&self) -> &Path {
85        &self.path
86    }
87
88    /// Returns the path of the device power controller.
89    pub fn bl_power(&self) -> &Path {
90        &self.bl_power
91    }
92
93    /// Toggles the state of the device ON and OFF.
94    ///
95    /// The return value is either a [`std::io::Error`] or the new state of the device.
96    ///
97    /// [`std::io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
98    pub fn toggle(&self) -> io::Result<i32> {
99        let old_value = read_i32(&self.bl_power)?;
100        let new_value = if old_value == 0 { 1 } else { 0 };
101        write_i32(&self.bl_power, new_value)?;
102        Ok(new_value)
103    }
104}
105
106/// An iterator over the devices found in a given folder.
107pub struct DeviceIter {
108    readdir: Option<fs::ReadDir>,
109}
110
111impl DeviceIter {
112    /// Create a new iterator over the devices found in `path`.
113    pub fn new<P: AsRef<Path>>(path: P) -> Self {
114        let readdir = match fs::read_dir(&path) {
115            Ok(iter) => Some(iter),
116            Err(_) => None,
117        };
118        Self { readdir }
119    }
120}
121
122impl Default for DeviceIter {
123    fn default() -> Self {
124        Self::new(Path::new(BACKLIGHT_PATH))
125    }
126}
127
128impl Iterator for DeviceIter {
129    type Item = Device;
130
131    fn next(&mut self) -> Option<Self::Item> {
132        match self.readdir {
133            Some(ref mut diriter) => diriter
134                .filter(|entry| entry.is_ok())
135                .map(|entry| entry.unwrap().path())
136                .find(|path| bl_power(path).is_file())
137                .map(|p| Device::new(&p)),
138            _ => None,
139        }
140    }
141}
142
143/// Iterate over devices in `dir`.
144///
145/// If successful, it returns an iterator over [`Device`]s.
146///
147/// The function can fail, returning `std::io::Error`,
148/// if `std::fs::read_dir` cannot open the directory.
149///
150/// [`Device`]: struct.Device.html
151pub fn iterate_devices<P: AsRef<Path>>(dir: P) -> io::Result<impl Iterator<Item = Device>> {
152    let diriter = fs::read_dir(dir)?;
153    Ok(diriter
154        .filter(|entry| entry.is_ok())
155        .map(|entry| entry.unwrap().path())
156        .filter(|path| bl_power(path).is_file())
157        .map(|p| Device::new(&p)))
158}
159
160fn bl_power(path: &Path) -> PathBuf {
161    path.join(BL_POWER)
162}
163
164fn read_i32(path: &Path) -> io::Result<i32> {
165    fs::read_to_string(path)?
166        .trim()
167        .parse::<i32>()
168        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "cannot parse i32"))
169}
170
171fn write_i32(path: &Path, value: i32) -> io::Result<()> {
172    fs::write(path, value.to_string())
173}