├── battery ├── LICENSE-MIT ├── README.md ├── LICENSE-APACHE ├── src │ ├── platform │ │ ├── darwin │ │ │ ├── iokit │ │ │ │ ├── mod.rs │ │ │ │ ├── errors.rs │ │ │ │ ├── sys.rs │ │ │ │ ├── wrappers.rs │ │ │ │ └── power_source.rs │ │ │ ├── mod.rs │ │ │ ├── manager.rs │ │ │ ├── iterator.rs │ │ │ ├── tests.rs │ │ │ ├── device.rs │ │ │ └── traits.rs │ │ ├── freebsd │ │ │ ├── mod.rs │ │ │ ├── manager.rs │ │ │ ├── iterator.rs │ │ │ ├── device.rs │ │ │ └── acpi.rs │ │ ├── linux │ │ │ ├── mod.rs │ │ │ ├── tests │ │ │ │ ├── mod.rs │ │ │ │ ├── issue_28.rs │ │ │ │ └── issue_40.rs │ │ │ ├── manager.rs │ │ │ ├── sysfs │ │ │ │ ├── mod.rs │ │ │ │ └── fs.rs │ │ │ ├── iterator.rs │ │ │ └── device.rs │ │ ├── windows │ │ │ ├── mod.rs │ │ │ ├── manager.rs │ │ │ ├── ffi │ │ │ │ ├── ioctl │ │ │ │ │ ├── wait_status.rs │ │ │ │ │ ├── query_info.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── info.rs │ │ │ │ │ └── status.rs │ │ │ │ ├── wide_string.rs │ │ │ │ ├── wrappers.rs │ │ │ │ └── mod.rs │ │ │ ├── iterator.rs │ │ │ └── device.rs │ │ ├── mod.rs │ │ └── traits.rs │ ├── types │ │ ├── mod.rs │ │ ├── iterator.rs │ │ ├── manager.rs │ │ ├── state.rs │ │ ├── technology.rs │ │ └── battery.rs │ ├── lib.rs │ ├── errors.rs │ └── units.rs ├── build.rs ├── examples │ └── simple.rs └── Cargo.toml ├── battery-ffi ├── LICENSE-MIT ├── LICENSE-APACHE ├── cbindgen.toml ├── build.rs ├── src │ ├── state.rs │ ├── iterator.rs │ ├── technology.rs │ ├── errors.rs │ ├── manager.rs │ ├── lib.rs │ └── battery.rs ├── Cargo.toml ├── README.md └── examples │ ├── ffi.c │ └── ffi.py ├── Cargo.toml ├── Cross.toml ├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── rustfmt.toml ├── .editorconfig ├── .cirrus.yml ├── LICENSE-MIT ├── CHANGELOG.md ├── README.md └── LICENSE-APACHE /battery/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /battery/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /battery-ffi/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /battery-ffi/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /battery/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "battery", 4 | "battery-ffi", 5 | ] 6 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = [ 3 | "RUST_BACKTRACE", 4 | "RUST_LOG", 5 | ] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/target 3 | **/*.rs.bk 4 | Cargo.lock 5 | 6 | *.orig 7 | local_build.sh 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: rust-battery 2 | liberapay: svartalf 3 | patreon: svartalf 4 | custom: ["https://svartalf.info/donate/", "https://www.buymeacoffee.com/svartalf"] 5 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/iokit/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod errors; 3 | mod power_source; 4 | mod sys; 5 | mod wrappers; 6 | 7 | pub use self::power_source::PowerSource; 8 | pub use self::wrappers::*; 9 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | version = "Two" 3 | wrap_comments = true 4 | comment_width = 120 5 | max_width = 120 6 | merge_imports = false 7 | newline_style = "Unix" 8 | struct_lit_single_line = false 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | 10 | [*.yml] 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /battery/src/platform/freebsd/mod.rs: -------------------------------------------------------------------------------- 1 | // DragonflyBSD also! 2 | 3 | mod acpi; 4 | mod device; 5 | mod iterator; 6 | mod manager; 7 | 8 | pub use self::device::IoCtlDevice; 9 | pub use self::iterator::IoCtlIterator; 10 | pub use self::manager::IoCtlManager; 11 | -------------------------------------------------------------------------------- /battery/src/platform/linux/mod.rs: -------------------------------------------------------------------------------- 1 | mod device; 2 | mod iterator; 3 | mod manager; 4 | mod sysfs; 5 | 6 | pub use self::device::SysFsDevice; 7 | pub use self::iterator::SysFsIterator; 8 | pub use self::manager::SysFsManager; 9 | 10 | #[cfg(test)] 11 | mod tests; 12 | -------------------------------------------------------------------------------- /battery-ffi/cbindgen.toml: -------------------------------------------------------------------------------- 1 | include_guard = "battery_ffi_h" 2 | autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" 3 | include_version = true 4 | language = "C" 5 | 6 | [parse] 7 | parse_deps = true 8 | include = ["battery"] 9 | -------------------------------------------------------------------------------- /battery/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|x| &**x) { 5 | Ok("macos") | Ok("ios") => { 6 | println!("cargo:rustc-link-lib=framework=IOKit"); 7 | } 8 | _ => {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/mod.rs: -------------------------------------------------------------------------------- 1 | mod device; 2 | mod iokit; 3 | mod iterator; 4 | mod manager; 5 | mod traits; 6 | 7 | pub use self::device::IoKitDevice; 8 | pub use self::iterator::IoKitIterator; 9 | pub use self::manager::IoKitManager; 10 | 11 | #[cfg(test)] 12 | mod tests; 13 | -------------------------------------------------------------------------------- /battery/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod battery; 2 | mod iterator; 3 | mod manager; 4 | mod state; 5 | mod technology; 6 | 7 | pub use self::battery::Battery; 8 | pub use self::iterator::Batteries; 9 | pub use self::manager::Manager; 10 | pub use self::state::State; 11 | pub use self::technology::Technology; 12 | -------------------------------------------------------------------------------- /battery/src/platform/windows/mod.rs: -------------------------------------------------------------------------------- 1 | // https://docs.microsoft.com/en-us/windows/desktop/power/power-management-portal 2 | 3 | mod device; 4 | mod ffi; 5 | mod iterator; 6 | mod manager; 7 | 8 | pub use self::device::PowerDevice; 9 | pub use self::iterator::PowerIterator; 10 | pub use self::manager::PowerManager; 11 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | task: 2 | name: FreeBSD 12.1 3 | freebsd_instance: 4 | image_family: freebsd-12-1 5 | 6 | setup_script: 7 | - pkg install -y curl 8 | - curl https://sh.rustup.rs -sSf --output rustup.sh 9 | - sh rustup.sh -y --profile=minimal 10 | - . $HOME/.cargo/env 11 | - rustup default stable 12 | 13 | build_script: 14 | - . $HOME/.cargo/env 15 | - cargo build 16 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/iokit/errors.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! r#kern_try { 3 | ($expr:expr) => { 4 | match $expr { 5 | mach::kern_return::KERN_SUCCESS => (), 6 | err_code => return ::std::result::Result::Err(::std::io::Error::from_raw_os_error(err_code).into()), 7 | } 8 | }; 9 | ($expr:expr,) => { 10 | r#kern_try!($expr) 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /battery-ffi/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "cbindgen")] 2 | fn build_header() { 3 | use std::env; 4 | use std::path::PathBuf; 5 | 6 | let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var is not defined"); 7 | let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR env var is not defined")); 8 | 9 | let config = cbindgen::Config::from_file("cbindgen.toml").expect("Unable to find cbindgen.toml configuration file"); 10 | 11 | cbindgen::generate_with_config(&crate_dir, config) 12 | .unwrap() 13 | .write_to_file(out_dir.join("battery_ffi.h")); 14 | } 15 | 16 | fn main() { 17 | #[cfg(feature = "cbindgen")] 18 | build_header() 19 | } 20 | -------------------------------------------------------------------------------- /battery/src/platform/linux/tests/mod.rs: -------------------------------------------------------------------------------- 1 | /// This macro generates the bunch of files representing the 2 | /// `/sys/class/power_supply/{name}/*` directory contents. 3 | macro_rules! sysfs_test_suite { 4 | ( $( $name:expr => $value:expr ),* ) => {{ 5 | use ::std::io::Write; 6 | 7 | let root = tempfile::tempdir().unwrap(); 8 | 9 | $( 10 | let mut file = ::std::fs::OpenOptions::new() 11 | .write(true) 12 | .create_new(true) 13 | .open(root.path().join($name)) 14 | .unwrap(); 15 | file.write_fmt(format_args!("{}\n", $value)).unwrap(); 16 | )* 17 | 18 | root 19 | }}; 20 | } 21 | 22 | mod issue_28; 23 | mod issue_40; 24 | -------------------------------------------------------------------------------- /battery/examples/simple.rs: -------------------------------------------------------------------------------- 1 | extern crate battery; 2 | 3 | use std::io; 4 | use std::thread; 5 | use std::time::Duration; 6 | 7 | fn main() -> battery::Result<()> { 8 | let manager = battery::Manager::new()?; 9 | let mut battery = match manager.batteries()?.next() { 10 | Some(Ok(battery)) => battery, 11 | Some(Err(e)) => { 12 | eprintln!("Unable to access battery information"); 13 | return Err(e); 14 | } 15 | None => { 16 | eprintln!("Unable to find any batteries"); 17 | return Err(io::Error::from(io::ErrorKind::NotFound).into()); 18 | } 19 | }; 20 | 21 | loop { 22 | println!("{:?}", battery); 23 | thread::sleep(Duration::from_secs(1)); 24 | manager.refresh(&mut battery)?; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /battery/src/platform/linux/manager.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use super::device::SysFsDevice; 4 | use super::iterator::SysFsIterator; 5 | use crate::platform::traits::*; 6 | use crate::Result; 7 | 8 | #[allow(clippy::redundant_static_lifetimes)] 9 | static SYSFS_ROOT: &'static str = "/sys/class/power_supply"; 10 | 11 | #[derive(Debug)] 12 | pub struct SysFsManager { 13 | root: PathBuf, 14 | } 15 | 16 | impl SysFsManager { 17 | pub fn path(&self) -> &Path { 18 | self.root.as_path() 19 | } 20 | } 21 | 22 | impl BatteryManager for SysFsManager { 23 | type Iterator = SysFsIterator; 24 | 25 | fn new() -> Result { 26 | Ok(Self { 27 | root: PathBuf::from(SYSFS_ROOT), 28 | }) 29 | } 30 | 31 | fn refresh(&self, device: &mut SysFsDevice) -> Result<()> { 32 | device.refresh() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /battery-ffi/src/state.rs: -------------------------------------------------------------------------------- 1 | use battery::State as RawState; 2 | 3 | /// Possible battery states. 4 | /// 5 | /// Enum members are prefixed here in order to not have "redeclaration of enumerator" error in C. 6 | #[repr(u8)] 7 | pub enum State { 8 | // DO NOT RE-ORDER VALUES IN THIS ENUM, IT WILL AFFECT FFI USERS! 9 | StateUnknown = 0, 10 | StateCharging = 1, 11 | StateDischarging = 2, 12 | StateEmpty = 3, 13 | StateFull = 4, 14 | } 15 | 16 | impl From for State { 17 | fn from(s: RawState) -> Self { 18 | match s { 19 | RawState::Unknown => State::StateUnknown, 20 | RawState::Charging => State::StateCharging, 21 | RawState::Discharging => State::StateDischarging, 22 | RawState::Empty => State::StateEmpty, 23 | RawState::Full => State::StateFull, 24 | _ => State::StateUnknown, 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /battery/src/platform/windows/manager.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use super::{ffi, PowerDevice, PowerIterator}; 4 | use crate::platform::traits::BatteryManager; 5 | use crate::Result; 6 | 7 | #[derive(Default)] 8 | pub struct PowerManager; 9 | 10 | impl BatteryManager for PowerManager { 11 | type Iterator = PowerIterator; 12 | 13 | fn new() -> Result { 14 | Ok(PowerManager {}) 15 | } 16 | 17 | fn refresh(&self, device: &mut PowerDevice) -> Result<()> { 18 | let battery_tag = device.tag().clone(); 19 | let di = ffi::DeviceIterator::new()?; 20 | let handle = di.prepare_handle()?; 21 | let device_handle = ffi::DeviceHandle { 22 | handle, 23 | tag: battery_tag, 24 | }; 25 | device.refresh(device_handle)?; 26 | 27 | Ok(()) 28 | } 29 | } 30 | 31 | impl fmt::Debug for PowerManager { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | f.debug_struct("WindowsManager").finish() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/manager.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::Deref; 3 | 4 | use super::{iokit, IoKitIterator}; 5 | use crate::platform::traits::{BatteryIterator, BatteryManager}; 6 | use crate::Result; 7 | 8 | pub struct IoKitManager(iokit::IoMasterPort); 9 | 10 | impl BatteryManager for IoKitManager { 11 | type Iterator = IoKitIterator; 12 | 13 | fn new() -> Result { 14 | let port = iokit::IoMasterPort::new()?; 15 | 16 | Ok(Self(port)) 17 | } 18 | 19 | fn refresh(&self, device: &mut ::Device) -> Result<()> { 20 | device.refresh() 21 | } 22 | } 23 | 24 | impl Deref for IoKitManager { 25 | type Target = iokit::IoMasterPort; 26 | 27 | fn deref(&self) -> &iokit::IoMasterPort { 28 | &self.0 29 | } 30 | } 31 | 32 | impl fmt::Debug for IoKitManager { 33 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 34 | f.debug_struct("MacOSManager").field("io_master_port", &self.0).finish() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /battery-ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "battery-ffi" 3 | version = "0.7.5" # remember to update html_root_url 4 | authors = ["svartalf "] 5 | edition = "2018" 6 | description = "FFI bindings for battery crate" 7 | repository = "https://github.com/svartalf/rust-battery" 8 | readme = "README.md" 9 | categories = ["os"] 10 | keywords = ["battery", "ffi", "linux", "macos", "windows"] 11 | license = "Apache-2.0 OR MIT" 12 | 13 | [badges] 14 | travis-ci = { repository = "svartalf/rust-battery", branch = "master" } 15 | maintenance = { status = "actively-developed" } 16 | is-it-maintained-issue-resolution = { repository = "svartalf/rust-battery" } 17 | is-it-maintained-open-issues = { repository = "svartalf/rust-battery" } 18 | 19 | [lib] 20 | name = "battery_ffi" 21 | crate-type = ["cdylib"] 22 | 23 | [features] 24 | default = ["cbindgen"] 25 | 26 | [dependencies.battery] 27 | version = "^0.7" 28 | path = "../battery" 29 | 30 | [dependencies] 31 | libc = "^0.2" 32 | 33 | [build-dependencies] 34 | cbindgen = { version = "^0.13", optional = true } 35 | 36 | [package.metadata.docs.rs] 37 | no-default-features = true 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 svartalf 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /battery/src/platform/mod.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(target_os = "linux")] { 3 | mod linux; 4 | 5 | pub type Manager = linux::SysFsManager; 6 | pub type Iterator = linux::SysFsIterator; 7 | pub type Device = linux::SysFsDevice; 8 | } else if #[cfg(any(target_os = "macos", target_os = "ios"))] { 9 | mod darwin; 10 | 11 | pub type Manager = darwin::IoKitManager; 12 | pub type Iterator = darwin::IoKitIterator; 13 | pub type Device = darwin::IoKitDevice; 14 | } else if #[cfg(target_os = "windows")] { 15 | mod windows; 16 | 17 | pub type Manager = windows::PowerManager; 18 | pub type Iterator = windows::PowerIterator; 19 | pub type Device = windows::PowerDevice; 20 | } else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] { 21 | mod freebsd; 22 | 23 | pub type Manager = freebsd::IoCtlManager; 24 | pub type Iterator = freebsd::IoCtlIterator; 25 | pub type Device = freebsd::IoCtlDevice; 26 | } else { 27 | compile_error!("Support for this target OS is not implemented yet!\n \ 28 | You may want to create an issue: https://github.com/svartalf/rust-battery/issues/new"); 29 | } 30 | } 31 | 32 | pub mod traits; 33 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/ioctl/wait_status.rs: -------------------------------------------------------------------------------- 1 | //! https://docs.microsoft.com/en-us/windows/desktop/power/battery-status-str 2 | 3 | #![allow(non_snake_case)] 4 | 5 | use std::default::Default; 6 | use std::mem; 7 | use std::ops; 8 | 9 | use winapi::shared::ntdef; 10 | 11 | STRUCT! {#[cfg_attr(target_arch = "x86", repr(packed))] #[derive(Debug)] struct BATTERY_WAIT_STATUS { 12 | BatteryTag: ntdef::ULONG, 13 | Timeout: ntdef::ULONG, 14 | PowerState: ntdef::ULONG, 15 | LowCapacity: ntdef::ULONG, 16 | HighCapacity: ntdef::ULONG, 17 | }} 18 | 19 | impl Default for BATTERY_WAIT_STATUS { 20 | #[inline] 21 | fn default() -> Self { 22 | unsafe { mem::zeroed() } 23 | } 24 | } 25 | 26 | #[derive(Debug)] 27 | pub struct BatteryWaitStatus(BATTERY_WAIT_STATUS); 28 | 29 | impl Default for BatteryWaitStatus { 30 | fn default() -> Self { 31 | BatteryWaitStatus(BATTERY_WAIT_STATUS::default()) 32 | } 33 | } 34 | 35 | impl ops::Deref for BatteryWaitStatus { 36 | type Target = BATTERY_WAIT_STATUS; 37 | 38 | fn deref(&self) -> &Self::Target { 39 | &self.0 40 | } 41 | } 42 | impl ops::DerefMut for BatteryWaitStatus { 43 | fn deref_mut(&mut self) -> &mut Self::Target { 44 | &mut self.0 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /battery/src/types/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::platform::Iterator as PlatformIterator; 4 | use crate::{Battery, Result}; 5 | 6 | /// An iterator that yields [batteries] available in system. 7 | /// 8 | /// This struct is created by the [Manager::batteries](struct.Manager.html#method.batteries) method. 9 | /// See its documentation for more. 10 | /// 11 | /// [batteries]: struct.Battery.html 12 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 13 | pub struct Batteries(PlatformIterator); 14 | 15 | impl Iterator for Batteries { 16 | type Item = Result; 17 | 18 | fn next(&mut self) -> Option { 19 | match self.0.next() { 20 | Some(Ok(device)) => Some(Ok(device.into())), 21 | Some(Err(e)) => Some(Err(e)), 22 | None => None, 23 | } 24 | } 25 | 26 | fn size_hint(&self) -> (usize, Option) { 27 | self.0.size_hint() 28 | } 29 | } 30 | 31 | impl From for Batteries { 32 | fn from(inner: PlatformIterator) -> Batteries { 33 | Batteries(inner) 34 | } 35 | } 36 | 37 | impl fmt::Debug for Batteries { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | f.debug_struct("Batteries").field("impl", &self.0).finish() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /battery/src/platform/freebsd/manager.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::Deref; 3 | use std::os::unix::io::AsRawFd; 4 | 5 | use super::{acpi, IoCtlIterator}; 6 | use crate::platform::traits::{BatteryIterator, BatteryManager}; 7 | use crate::{Error, Result}; 8 | 9 | pub struct IoCtlManager(acpi::AcpiDevice); 10 | 11 | impl BatteryManager for IoCtlManager { 12 | type Iterator = IoCtlIterator; 13 | 14 | fn new() -> Result { 15 | Ok(Self(acpi::AcpiDevice::new()?)) 16 | } 17 | 18 | fn refresh(&self, device: &mut ::Device) -> Result<()> { 19 | let bif = self.0.bif(device.unit())?; 20 | let bst = self.0.bst(device.unit())?; 21 | 22 | match (bif, bst) { 23 | (Some(bif), Some(bst)) => device.refresh(bif, bst), 24 | (None, _) => Err(Error::invalid_data("Returned bif struct is invalid")), 25 | (_, None) => Err(Error::invalid_data("Returned bst struct is invalid")), 26 | } 27 | } 28 | } 29 | 30 | impl Deref for IoCtlManager { 31 | type Target = acpi::AcpiDevice; 32 | 33 | fn deref(&self) -> &acpi::AcpiDevice { 34 | &self.0 35 | } 36 | } 37 | 38 | impl fmt::Debug for IoCtlManager { 39 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 40 | f.debug_struct("FreeBSD").field("fd", &self.0.as_raw_fd()).finish() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/wide_string.rs: -------------------------------------------------------------------------------- 1 | use std::ffi; 2 | use std::ops; 3 | use std::os::windows::ffi::OsStringExt; 4 | 5 | #[derive(Debug)] 6 | pub struct WideString(Vec); 7 | 8 | impl Default for WideString { 9 | fn default() -> Self { 10 | let buf = vec![0u16; 128]; 11 | Self(buf) 12 | } 13 | } 14 | 15 | impl WideString { 16 | pub fn with_capacity(capacity: usize) -> WideString { 17 | let buf = vec![0u16; capacity]; 18 | Self(buf) 19 | } 20 | 21 | // Note: those a u8, not a u16 received! 22 | #[inline] 23 | pub fn truncate(&mut self, bytes: usize) { 24 | // `-1` is for dropping the traling `\0` 25 | // which we will always have in our cases 26 | if let Some(new_size) = (bytes / 2).checked_sub(1) { 27 | self.0.truncate(new_size); 28 | } 29 | } 30 | 31 | #[inline] 32 | pub fn len(&self) -> usize { 33 | self.0.len() 34 | } 35 | } 36 | 37 | impl From for String { 38 | fn from(b: WideString) -> Self { 39 | let raw = ffi::OsString::from_wide(&b.0); 40 | raw.to_string_lossy().to_string() 41 | } 42 | } 43 | 44 | impl ops::Deref for WideString { 45 | type Target = Vec; 46 | 47 | fn deref(&self) -> &Self::Target { 48 | &self.0 49 | } 50 | } 51 | 52 | impl ops::DerefMut for WideString { 53 | fn deref_mut(&mut self) -> &mut Self::Target { 54 | &mut self.0 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/ioctl/query_info.rs: -------------------------------------------------------------------------------- 1 | //! https://docs.microsoft.com/en-us/windows/desktop/power/battery-query-information-str 2 | 3 | #![allow(non_snake_case)] 4 | 5 | use std::default::Default; 6 | use std::mem; 7 | use std::ops; 8 | 9 | use winapi::shared::ntdef; 10 | 11 | use super::info_level; 12 | 13 | STRUCT! {#[cfg_attr(target_arch = "x86", repr(packed))] #[derive(Debug)] struct BATTERY_QUERY_INFORMATION { 14 | BatteryTag: ntdef::ULONG, 15 | InformationLevel: info_level::BATTERY_QUERY_INFORMATION_LEVEL, 16 | AtRate: ntdef::LONG, 17 | }} 18 | 19 | impl Default for BATTERY_QUERY_INFORMATION { 20 | #[inline] 21 | fn default() -> Self { 22 | unsafe { mem::zeroed() } 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct BatteryQueryInformation(BATTERY_QUERY_INFORMATION); 28 | 29 | impl BatteryQueryInformation { 30 | pub fn battery_tag(&self) -> ntdef::ULONG { 31 | self.0.BatteryTag 32 | } 33 | } 34 | 35 | impl Default for BatteryQueryInformation { 36 | fn default() -> Self { 37 | BatteryQueryInformation(BATTERY_QUERY_INFORMATION::default()) 38 | } 39 | } 40 | 41 | impl ops::Deref for BatteryQueryInformation { 42 | type Target = BATTERY_QUERY_INFORMATION; 43 | 44 | fn deref(&self) -> &Self::Target { 45 | &self.0 46 | } 47 | } 48 | impl ops::DerefMut for BatteryQueryInformation { 49 | fn deref_mut(&mut self) -> &mut Self::Target { 50 | &mut self.0 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /battery/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides cross-platform information about batteries. 2 | //! 3 | //! Gives access to a system independent battery state, capacity, charge and voltage values 4 | //! recalculated as necessary to be returned in [SI measurement units](https://www.bipm.org/en/measurement-units/). 5 | //! 6 | //! ## Supported platforms 7 | //! 8 | //! * Linux 2.6.39+ 9 | //! * MacOS 10.10+ 10 | //! * Windows 7+ 11 | //! * FreeBSD 12 | //! * DragonFlyBSD 13 | //! 14 | //! ## Examples 15 | //! 16 | //! For a quick example see the [Manager](struct.Manager.html) type documentation 17 | //! or [`simple.rs`](https://github.com/svartalf/rust-battery/blob/master/battery/examples/simple.rs) 18 | //! file in the `examples/` folder. 19 | //! 20 | //! [battop](https://crates.io/crates/battop) crate is using this library as a knowledge source, 21 | //! so check it out too for a real-life example. 22 | 23 | #![deny(unused)] 24 | #![deny(unstable_features)] 25 | #![deny(bare_trait_objects)] 26 | #![allow(clippy::manual_non_exhaustive)] // MSRV is 1.36 27 | #![doc(html_root_url = "https://docs.rs/battery/0.7.8")] 28 | 29 | #[macro_use] 30 | extern crate cfg_if; 31 | 32 | #[cfg(target_os = "windows")] 33 | #[macro_use] 34 | extern crate winapi; 35 | 36 | #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] 37 | #[macro_use] 38 | extern crate nix; 39 | 40 | mod types; 41 | #[macro_use] 42 | pub mod units; 43 | pub mod errors; 44 | mod platform; 45 | 46 | pub use self::errors::{Error, Result}; 47 | pub use self::types::{Batteries, Battery, Manager, State, Technology}; 48 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::rc::Rc; 3 | 4 | use super::{iokit, IoKitDevice, IoKitManager}; 5 | use crate::platform::traits::BatteryIterator; 6 | use crate::Result; 7 | 8 | pub struct IoKitIterator { 9 | #[allow(dead_code)] 10 | manager: Rc, 11 | inner: iokit::IoIterator, 12 | } 13 | 14 | impl Iterator for IoKitIterator { 15 | type Item = Result; 16 | 17 | fn next(&mut self) -> Option { 18 | match self.inner.next() { 19 | None => None, 20 | Some(io_obj) => match iokit::PowerSource::try_from(io_obj) { 21 | Ok(source) => Some(Ok(source.into())), 22 | Err(e) => Some(Err(e)), 23 | }, 24 | } 25 | } 26 | 27 | fn size_hint(&self) -> (usize, Option) { 28 | self.inner.size_hint() 29 | } 30 | } 31 | 32 | impl BatteryIterator for IoKitIterator { 33 | type Manager = IoKitManager; 34 | type Device = IoKitDevice; 35 | 36 | fn new(manager: Rc) -> Result { 37 | let services = manager.get_services()?; 38 | 39 | Ok(Self { 40 | manager, 41 | inner: services, 42 | }) 43 | } 44 | } 45 | 46 | impl fmt::Debug for IoKitIterator { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | let (start, end) = self.size_hint(); 49 | f.debug_struct("MacOSIterator") 50 | .field("start", &start) 51 | .field("end", &end) 52 | .finish() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /battery/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "battery" 3 | version = "0.7.8" # remember to update html_root_url 4 | authors = ["svartalf "] 5 | edition = "2018" 6 | description = "Cross-platform information about the notebook batteries" 7 | repository = "https://github.com/svartalf/rust-battery" 8 | readme = "README.md" 9 | categories = ["os"] 10 | keywords = ["battery", "linux", "macos", "windows", "freebsd"] 11 | license = "Apache-2.0 OR MIT" 12 | build = "build.rs" 13 | 14 | [badges] 15 | travis-ci = { repository = "svartalf/rust-battery", branch = "master" } 16 | maintenance = { status = "actively-developed" } 17 | is-it-maintained-issue-resolution = { repository = "svartalf/rust-battery" } 18 | is-it-maintained-open-issues = { repository = "svartalf/rust-battery" } 19 | 20 | [dependencies] 21 | cfg-if = "1.0" 22 | num-traits = { version = "0.2", default_features = false } 23 | uom = { version = "0.30", features = ["autoconvert", "f32", "si"] } 24 | 25 | [target.'cfg(target_os = "linux")'.dependencies] 26 | lazycell = "~1.3" 27 | 28 | [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] 29 | libc = "^0.2" 30 | mach = "^0.3" 31 | core-foundation = "~0.7" 32 | 33 | [target.'cfg(target_os = "windows")'.dependencies] 34 | winapi = { version ="~0.3", features = ["impl-default", "devguid", "winbase", "ioapiset", "ntdef", "setupapi", "handleapi", "errhandlingapi", "winerror"] } 35 | 36 | [target.'cfg(any(target_os = "dragonfly", target_os = "freebsd"))'.dependencies] 37 | libc = "~0.2" 38 | nix = "~0.19" 39 | 40 | [dev-dependencies] 41 | tempfile = "^3.0" 42 | approx = "0.3.2" 43 | -------------------------------------------------------------------------------- /battery-ffi/src/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::{Batteries, Battery}; 4 | 5 | /// Gets next iteration over batteries iterator. 6 | /// 7 | /// Caller is required to call [battery_free](fn.battery_free.html) in order 8 | /// to properly free memory for the returned battery instance. 9 | /// 10 | /// # Panics 11 | /// 12 | /// This function will panic if passed pointer is `NULL`. 13 | /// 14 | /// # Returns 15 | /// 16 | /// Returns pointer to next battery. 17 | /// 18 | /// If there is no batteries left to iterate or some error happened, this function will return `NULL`. 19 | /// 20 | /// Caller is required to differentiate between these two cases and should check 21 | /// if there was any error with [battery_have_last_error](fn.battery_have_last_error.html). 22 | /// 23 | /// If there is no batteries left, `battery_have_last_error` will return `0`. 24 | #[no_mangle] 25 | pub unsafe extern "C" fn battery_iterator_next(ptr: *mut Batteries) -> *mut Battery { 26 | assert!(!ptr.is_null()); 27 | let iterator = &mut *ptr; 28 | 29 | match iterator.next() { 30 | None => { 31 | crate::errors::clear_last_error(); 32 | ptr::null_mut() 33 | } 34 | Some(Ok(battery)) => Box::into_raw(Box::new(battery)), 35 | Some(Err(e)) => { 36 | crate::errors::set_last_error(e); 37 | ptr::null_mut() 38 | } 39 | } 40 | } 41 | 42 | /// Frees previously created batteries iterator. 43 | #[no_mangle] 44 | pub unsafe extern "C" fn battery_iterator_free(ptr: *mut Batteries) { 45 | if ptr.is_null() { 46 | return; 47 | } 48 | 49 | let _ = Box::from_raw(ptr); 50 | } 51 | -------------------------------------------------------------------------------- /battery-ffi/src/technology.rs: -------------------------------------------------------------------------------- 1 | use battery::Technology as RawTech; 2 | 3 | /// Possible battery technologies. 4 | /// 5 | /// New members might be added to this enum in the next versions, 6 | /// so users are required to properly handle that case. 7 | /// 8 | /// Enum members are prefixed here in order to not have "redeclaration of enumerator" error in C. 9 | #[repr(u8)] 10 | pub enum Technology { 11 | // DO NOT RE-ORDER VALUES IN THIS ENUM, IT WILL AFFECT FFI USERS 12 | TechnologyUnknown = 0, 13 | TechnologyLithiumIon = 1, 14 | TechnologyLeadAcid = 2, 15 | TechnologyLithiumPolymer = 3, 16 | TechnologyNickelMetalHydride = 4, 17 | TechnologyNickelCadmium = 5, 18 | TechnologyNickelZinc = 6, 19 | TechnologyLithiumIronPhosphate = 7, 20 | TechnologyRechargeableAlkalineManganese = 8, 21 | } 22 | 23 | impl From for Technology { 24 | fn from(s: RawTech) -> Self { 25 | match s { 26 | RawTech::Unknown => Technology::TechnologyUnknown, 27 | RawTech::LithiumIon => Technology::TechnologyLithiumIon, 28 | RawTech::LeadAcid => Technology::TechnologyLeadAcid, 29 | RawTech::LithiumPolymer => Technology::TechnologyLithiumPolymer, 30 | RawTech::NickelMetalHydride => Technology::TechnologyNickelMetalHydride, 31 | RawTech::NickelCadmium => Technology::TechnologyNickelCadmium, 32 | RawTech::NickelZinc => Technology::TechnologyNickelZinc, 33 | RawTech::LithiumIronPhosphate => Technology::TechnologyLithiumIronPhosphate, 34 | RawTech::RechargeableAlkalineManganese => Technology::TechnologyRechargeableAlkalineManganese, 35 | _ => Technology::TechnologyUnknown, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /battery/src/platform/windows/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::rc::Rc; 3 | 4 | use super::{ffi, PowerDevice, PowerManager}; 5 | use crate::platform::traits::BatteryIterator; 6 | use crate::Result; 7 | 8 | pub struct PowerIterator { 9 | #[allow(dead_code)] 10 | manager: Rc, 11 | inner: ffi::DeviceIterator, 12 | } 13 | 14 | impl Iterator for PowerIterator { 15 | type Item = Result; 16 | 17 | fn next(&mut self) -> Option { 18 | loop { 19 | match self.inner.next() { 20 | None => return None, 21 | Some(handle) => { 22 | match PowerDevice::try_from(handle) { 23 | Ok(Some(device)) => return Some(Ok(device)), 24 | Ok(None) => continue, 25 | Err(e) => return Some(Err(e)), 26 | }; 27 | } 28 | } 29 | } 30 | } 31 | 32 | fn size_hint(&self) -> (usize, Option) { 33 | self.inner.size_hint() 34 | } 35 | } 36 | 37 | impl BatteryIterator for PowerIterator { 38 | type Manager = PowerManager; 39 | type Device = PowerDevice; 40 | 41 | fn new(manager: Rc) -> Result { 42 | let inner = ffi::DeviceIterator::new()?; 43 | Ok(Self { 44 | manager, 45 | inner, 46 | }) 47 | } 48 | } 49 | 50 | impl fmt::Debug for PowerIterator { 51 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 52 | let (start, end) = self.size_hint(); 53 | f.debug_struct("WindowsIterator") 54 | .field("start", &start) 55 | .field("end", &end) 56 | .finish() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /battery/src/platform/linux/sysfs/mod.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::str::FromStr; 3 | 4 | pub mod fs; 5 | mod source; 6 | 7 | pub use self::source::{DataBuilder, InstantData}; 8 | 9 | #[allow(dead_code)] 10 | #[derive(Debug, Eq, PartialEq)] 11 | pub enum Type { 12 | Battery, 13 | Mains, 14 | Ups, 15 | Usb, 16 | Unknown, 17 | __Nonexhaustive, 18 | } 19 | 20 | impl FromStr for Type { 21 | type Err = io::Error; 22 | 23 | fn from_str(s: &str) -> Result { 24 | let value = match () { 25 | _ if s.eq_ignore_ascii_case("Battery") => Type::Battery, 26 | _ if s.eq_ignore_ascii_case("Mains") => Type::Mains, 27 | _ if s.eq_ignore_ascii_case("Ups") => Type::Ups, 28 | _ if s.eq_ignore_ascii_case("Usb") => Type::Usb, 29 | _ => Type::Unknown, 30 | }; 31 | Ok(value) 32 | } 33 | } 34 | 35 | /// A power supply which doesn't have a "scope" attribute should be assumed to 36 | /// have "System" scope. 37 | #[derive(Debug, Eq, PartialEq)] 38 | pub enum Scope { 39 | /// Powers a specific device, or tree of devices 40 | Device, 41 | /// Powers the whole system 42 | System, 43 | /// Unknown power topology 44 | Unknown, 45 | __Nonexhaustive, 46 | } 47 | 48 | impl FromStr for Scope { 49 | type Err = io::Error; 50 | 51 | fn from_str(s: &str) -> Result { 52 | let value = match s { 53 | _ if s.eq_ignore_ascii_case("Device") => Scope::Device, 54 | _ if s.eq_ignore_ascii_case("System") => Scope::System, 55 | _ if s.eq_ignore_ascii_case("Unknown") => Scope::Unknown, 56 | _ => Scope::Unknown, 57 | }; 58 | 59 | Ok(value) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/wrappers.rs: -------------------------------------------------------------------------------- 1 | // Wrappers around the FFI things that should be freed later. 2 | // It is better to Drop than free them manually. 3 | 4 | use std::ops; 5 | 6 | use winapi::ctypes::c_void; 7 | use winapi::shared::ntdef; 8 | use winapi::um::{handleapi, setupapi, winbase}; 9 | 10 | #[derive(Debug)] 11 | pub struct InterfaceDetailData(setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W); 12 | 13 | impl From for InterfaceDetailData { 14 | fn from(p: setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W) -> Self { 15 | Self(p) 16 | } 17 | } 18 | 19 | impl ops::Deref for InterfaceDetailData { 20 | type Target = setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W; 21 | 22 | fn deref(&self) -> &Self::Target { 23 | &self.0 24 | } 25 | } 26 | 27 | impl Drop for InterfaceDetailData { 28 | fn drop(&mut self) { 29 | let res = unsafe { winbase::LocalFree(self.0 as *mut c_void) }; 30 | debug_assert_eq!(res, ntdef::NULL, "Unable to free device interface detail data"); 31 | } 32 | } 33 | 34 | #[derive(Debug)] 35 | pub struct Handle(ntdef::HANDLE); 36 | 37 | impl From for Handle { 38 | fn from(handle: ntdef::HANDLE) -> Self { 39 | Self(handle) 40 | } 41 | } 42 | 43 | impl ops::Deref for Handle { 44 | type Target = ntdef::HANDLE; 45 | 46 | fn deref(&self) -> &Self::Target { 47 | &self.0 48 | } 49 | } 50 | 51 | impl ops::DerefMut for Handle { 52 | fn deref_mut(&mut self) -> &mut Self::Target { 53 | &mut self.0 54 | } 55 | } 56 | 57 | impl Drop for Handle { 58 | fn drop(&mut self) { 59 | let res = unsafe { handleapi::CloseHandle(self.0) }; 60 | debug_assert_ne!(res, 0, "Unable to close device handle"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/ioctl/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unreadable_literal)] 2 | 3 | // Each sub-module represents a C-level struct to respective IOCTL request 4 | // and idiomatic Rust struct around it. 5 | 6 | use winapi::shared::minwindef; 7 | 8 | mod info; 9 | mod query_info; 10 | mod status; 11 | mod wait_status; 12 | 13 | pub use self::info::BatteryInformation; 14 | pub use self::query_info::BatteryQueryInformation; 15 | pub use self::status::BatteryStatus; 16 | pub use self::wait_status::BatteryWaitStatus; 17 | 18 | // Following values are based on the https://www.ioctls.net data 19 | pub const IOCTL_BATTERY_QUERY_TAG: minwindef::DWORD = 0x294040; 20 | pub const IOCTL_BATTERY_QUERY_INFORMATION: minwindef::DWORD = 0x294044; 21 | pub const IOCTL_BATTERY_QUERY_STATUS: minwindef::DWORD = 0x29404c; 22 | 23 | pub mod info_level { 24 | #![allow(non_camel_case_types, non_upper_case_globals)] 25 | 26 | /// For some reasons, "winapi==0.3.6" `ENUM!` macro fails to compile with 27 | /// error: no rules expected the token `@` 28 | /// so defining `BATTERY_QUERY_INFORMATION_LEVEL` "enum" manually. 29 | 30 | pub type BATTERY_QUERY_INFORMATION_LEVEL = u32; 31 | 32 | // pub const BatteryInformation: BATTERY_QUERY_INFORMATION_LEVEL = 0; 33 | // pub const BatteryGranularityInformation: BATTERY_QUERY_INFORMATION_LEVEL = 1; 34 | pub const BatteryTemperature: BATTERY_QUERY_INFORMATION_LEVEL = 2; 35 | // pub const BatteryEstimatedTime: BATTERY_QUERY_INFORMATION_LEVEL = 3; 36 | pub const BatteryDeviceName: BATTERY_QUERY_INFORMATION_LEVEL = 4; 37 | // pub const BatteryManufactureDate: BATTERY_QUERY_INFORMATION_LEVEL = 5; 38 | pub const BatteryManufactureName: BATTERY_QUERY_INFORMATION_LEVEL = 6; 39 | // pub const BatteryUniqueID: BATTERY_QUERY_INFORMATION_LEVEL = 7; 40 | pub const BatterySerialNumber: BATTERY_QUERY_INFORMATION_LEVEL = 8; 41 | } 42 | -------------------------------------------------------------------------------- /battery/src/types/manager.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::rc::Rc; 3 | 4 | use crate::platform::traits::*; 5 | use crate::platform::Iterator as PlatformIterator; 6 | use crate::platform::Manager as PlatformManager; 7 | use crate::{Batteries, Battery, Result}; 8 | 9 | /// Manager for batteries available in system. 10 | /// 11 | /// Allows fetching and updating [batteries] information. 12 | /// 13 | /// # Example 14 | /// 15 | /// ```edition2018 16 | /// # use battery::{Result, Manager}; 17 | /// # fn main() -> Result<()> { 18 | /// for battery in Manager::new()?.batteries()? { 19 | /// println!("{:#?}", battery?); 20 | /// } 21 | /// # Ok(()) 22 | /// # } 23 | /// ``` 24 | /// 25 | /// [batteries]: struct.Battery.html 26 | pub struct Manager { 27 | inner: Rc, 28 | } 29 | 30 | impl Manager { 31 | /// Creates new manager value. 32 | pub fn new() -> Result { 33 | let inner = PlatformManager::new()?; 34 | 35 | Ok(Manager { 36 | inner: Rc::new(inner), 37 | }) 38 | } 39 | 40 | /// Returns an iterator over available batteries. 41 | /// 42 | /// There are no guarantees provided for [batteries] ordering, 43 | /// multiple calls to this method might result in any particular order 44 | /// depending on underline OS implementation. 45 | /// 46 | /// [batteries]: struct.Battery.html 47 | pub fn batteries(&self) -> Result { 48 | let inner = PlatformIterator::new(self.inner.clone())?; 49 | 50 | Ok(Batteries::from(inner)) 51 | } 52 | 53 | /// Refresh battery information in-place. 54 | pub fn refresh(&self, battery: &mut Battery) -> Result<()> { 55 | self.inner.refresh(battery) 56 | } 57 | } 58 | 59 | impl fmt::Debug for Manager { 60 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 61 | f.debug_struct("Manager").field("impl", &self.inner).finish() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /battery/src/platform/linux/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fs::{self, ReadDir}; 3 | use std::rc::Rc; 4 | 5 | use super::{SysFsDevice, SysFsManager}; 6 | use crate::platform::traits::*; 7 | use crate::Result; 8 | 9 | pub struct SysFsIterator { 10 | #[allow(dead_code)] 11 | manager: Rc, 12 | entries: ReadDir, 13 | } 14 | 15 | impl BatteryIterator for SysFsIterator { 16 | type Manager = SysFsManager; 17 | type Device = SysFsDevice; 18 | 19 | fn new(manager: Rc) -> Result { 20 | let entries = fs::read_dir(manager.path())?; 21 | 22 | Ok(SysFsIterator { 23 | manager, 24 | entries, 25 | }) 26 | } 27 | } 28 | 29 | impl Iterator for SysFsIterator { 30 | type Item = Result<::Device>; 31 | 32 | fn next(&mut self) -> Option { 33 | loop { 34 | return match self.entries.next() { 35 | None => None, 36 | // Unable to access sysfs for some reasons 37 | Some(Err(e)) => Some(Err(e.into())), 38 | Some(Ok(entry)) => { 39 | let path = entry.path(); 40 | match SysFsDevice::is_system_battery(&path) { 41 | Ok(true) => Some(SysFsDevice::try_from(path)), 42 | Ok(false) => continue, 43 | Err(e) => Some(Err(e)), 44 | } 45 | } 46 | }; 47 | } 48 | } 49 | 50 | fn size_hint(&self) -> (usize, Option) { 51 | self.entries.size_hint() 52 | } 53 | } 54 | 55 | impl fmt::Debug for SysFsIterator { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | let (start, end) = self.size_hint(); 58 | f.debug_struct("LinuxIterator") 59 | .field("start", &start) 60 | .field("end", &end) 61 | .finish() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /battery/src/types/state.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::str; 4 | 5 | /// Possible battery state values. 6 | /// 7 | /// Unknown can mean either controller returned unknown, 8 | /// or not able to retrieve state due to some error. 9 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] 10 | pub enum State { 11 | Unknown, 12 | Charging, 13 | Discharging, 14 | Empty, 15 | Full, 16 | 17 | // Awaiting for https://github.com/rust-lang/rust/issues/44109 18 | #[doc(hidden)] 19 | __Nonexhaustive, 20 | } 21 | 22 | impl str::FromStr for State { 23 | type Err = io::Error; 24 | 25 | fn from_str(s: &str) -> Result { 26 | // TODO: Support strings that starts with `\0` 27 | // TODO: Support `not charging` value 28 | // Ref: `up_device_supply_get_state` function at 29 | //https://gitlab.freedesktop.org/upower/upower/blob/master/src/linux/up-device-supply.c#L452 30 | match s { 31 | _ if s.eq_ignore_ascii_case("Unknown") => Ok(State::Unknown), 32 | _ if s.eq_ignore_ascii_case("Empty") => Ok(State::Empty), 33 | _ if s.eq_ignore_ascii_case("Full") => Ok(State::Full), 34 | _ if s.eq_ignore_ascii_case("Charging") => Ok(State::Charging), 35 | _ if s.eq_ignore_ascii_case("Discharging") => Ok(State::Discharging), 36 | _ => Err(io::Error::from(io::ErrorKind::InvalidData)), 37 | } 38 | } 39 | } 40 | 41 | impl fmt::Display for State { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | let display = match self { 44 | State::Unknown => "unknown", 45 | State::Charging => "charging", 46 | State::Discharging => "discharging", 47 | State::Empty => "empty", 48 | State::Full => "full", 49 | _ => "unknown", 50 | }; 51 | 52 | write!(f, "{}", display) 53 | } 54 | } 55 | 56 | impl Default for State { 57 | fn default() -> Self { 58 | State::Unknown 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /battery/src/platform/freebsd/iterator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::Range; 3 | use std::rc::Rc; 4 | 5 | use super::{IoCtlDevice, IoCtlManager}; 6 | use crate::platform::traits::BatteryIterator; 7 | use crate::Result; 8 | 9 | pub struct IoCtlIterator { 10 | manager: Rc, 11 | range: Range, 12 | } 13 | 14 | impl Iterator for IoCtlIterator { 15 | type Item = Result; 16 | 17 | fn next(&mut self) -> Option { 18 | loop { 19 | match self.range.next() { 20 | None => return None, 21 | Some(idx) => { 22 | let bif = self.manager.bif(idx); 23 | let bst = self.manager.bst(idx); 24 | 25 | match (bif, bst) { 26 | (Ok(Some(bif)), Ok(Some(bst))) => { 27 | return Some(Ok(IoCtlDevice::new(idx, bif, bst))); 28 | } 29 | (Err(e), _) => return Some(Err(e)), 30 | (_, Err(e)) => return Some(Err(e)), 31 | // If bif or bst is invalid (`Ok(None)` here), 32 | // silently skipping it, same as FreeBSD does 33 | _ => continue, 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | fn size_hint(&self) -> (usize, Option) { 41 | (0, Some((self.range.end - self.range.start) as usize)) 42 | } 43 | } 44 | 45 | impl BatteryIterator for IoCtlIterator { 46 | type Manager = IoCtlManager; 47 | type Device = IoCtlDevice; 48 | 49 | fn new(manager: Rc) -> Result { 50 | let batteries = manager.count()?; 51 | 52 | Ok(Self { 53 | manager, 54 | range: (0..batteries), 55 | }) 56 | } 57 | } 58 | 59 | impl fmt::Debug for IoCtlIterator { 60 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 61 | f.debug_struct("FreeBSDIterator") 62 | .field("start", &self.range.start) 63 | .field("end", &self.range.end) 64 | .finish() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /battery/src/platform/linux/tests/issue_28.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use approx::assert_abs_diff_eq; 4 | 5 | use super::super::SysFsDevice; 6 | use crate::platform::traits::BatteryDevice; 7 | use crate::{State, Technology}; 8 | 9 | // https://github.com/svartalf/rust-battery/issues/28 10 | // 11 | // This test is not actually covers the `ENODEV` case, 12 | // but it would be nice to have some test cases, 13 | // especially when people are so gracious to provide 14 | // the test data. 15 | #[test] 16 | fn test_issue_28() { 17 | let root = sysfs_test_suite!( 18 | "charge_full_design" => 3600000, 19 | "serial_number" => "41167", 20 | "technology" => "Li-ion", 21 | "charge_now" => 725000, 22 | "present" => 1, 23 | "manufacturer" => "Hewlett-Packard", 24 | "type" => "Battery", 25 | "charge_full" => 3424000, 26 | "capacity" => 21, 27 | "cycle_count" => 0, 28 | "voltage_now" => 10663000, 29 | "status" => "Discharging", 30 | "alarm" => 340000, 31 | "model_name" => "PABAS0241231", 32 | "voltage_min_design" => 11400000, 33 | "capacity_level" => "Normal" 34 | ); 35 | 36 | let path = root.into_path(); 37 | let device = SysFsDevice::try_from(path.clone()); 38 | 39 | assert!(device.is_ok()); 40 | let device = device.unwrap(); 41 | 42 | assert_eq!(device.state(), State::Discharging); 43 | assert_eq!(device.technology(), Technology::LithiumIon); 44 | assert!(device.temperature().is_none()); 45 | assert_eq!(device.cycle_count(), None); 46 | assert_eq!(device.vendor(), Some("Hewlett-Packard")); 47 | assert_eq!(device.model(), Some("PABAS0241231")); 48 | assert_eq!(device.serial_number(), Some("41167")); 49 | assert_abs_diff_eq!(device.state_of_health().value, 0.9511111); 50 | assert_abs_diff_eq!(device.state_of_charge().value, 0.21); 51 | assert_abs_diff_eq!(device.energy().value, 29753.998); 52 | assert_abs_diff_eq!(device.energy_full().value, 140520.95); 53 | assert_abs_diff_eq!(device.energy_full_design().value, 147744.0); 54 | assert_abs_diff_eq!(device.energy_rate().value, 0.0); 55 | assert_abs_diff_eq!(device.voltage().value, 10.663); 56 | 57 | fs::remove_dir_all(path).unwrap(); 58 | } 59 | -------------------------------------------------------------------------------- /battery/src/platform/linux/tests/issue_40.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use approx::assert_abs_diff_eq; 4 | 5 | use super::super::SysFsDevice; 6 | use crate::platform::traits::BatteryDevice; 7 | use crate::{State, Technology}; 8 | 9 | // https://github.com/svartalf/rust-battery/issues/40 10 | // 11 | // Both missing `energy_full_design` and `charge_full_design` 12 | // should be handled correctly. 13 | // 14 | // See https://github.com/starship/starship/issues/613#issuecomment-548873632 15 | #[test] 16 | fn test_issue_40() { 17 | let root = sysfs_test_suite!( 18 | "capacity" => 83, 19 | "charge_counter" => 2584, 20 | "current_now" => 898, 21 | "health" => "Good", 22 | "present" => 1, 23 | "status" => "Discharging", 24 | "technology" => "Li-ion", 25 | "temp" => 258, 26 | "type" => "Battery", 27 | "voltage_now" => 11829000 28 | ); 29 | 30 | let path = root.into_path(); 31 | let device = SysFsDevice::try_from(path.clone()); 32 | 33 | assert!(device.is_ok()); 34 | let device = device.unwrap(); 35 | 36 | assert_eq!(device.state(), State::Discharging); 37 | assert_eq!(device.technology(), Technology::LithiumIon); 38 | assert_eq!(device.cycle_count(), None); 39 | assert!(device.vendor().is_none()); 40 | assert!(device.model().is_none()); 41 | assert!(device.serial_number().is_none()); 42 | assert!(device.temperature().is_some()); 43 | assert_abs_diff_eq!(device.temperature().unwrap().value, 298.94998); // Kelvins 44 | 45 | assert_abs_diff_eq!(device.state_of_health().value, 1.0); 46 | assert_abs_diff_eq!(device.state_of_charge().value, 0.83); 47 | assert_abs_diff_eq!(device.energy().value, 0.0); 48 | assert_abs_diff_eq!(device.energy_full().value, 0.0); 49 | assert_abs_diff_eq!(device.energy_full_design().value, 0.0); 50 | assert_abs_diff_eq!(device.energy_rate().value, 0.00089799997); 51 | assert_abs_diff_eq!(device.voltage().value, 11.8289995); 52 | 53 | // &device.source = InstantData { 54 | // state_of_health: 1.0, 55 | // state_of_charge: 0.83, 56 | // energy: 0.0 m^2 kg^1 s^-2, 57 | // energy_full: 0.0 m^2 kg^1 s^-2, 58 | // energy_full_design: 0.0 m^2 kg^1 s^-2, 59 | // energy_rate: 0.00089799997 m^2 kg^1 s^-3, 60 | // voltage: 11.8289995 m^2 kg^1 s^-3 A^-1, 61 | // state: Discharging, 62 | // temperature: Some( 63 | // 298.94998 K^1, 64 | // ), 65 | // cycle_count: None, 66 | //} 67 | 68 | fs::remove_dir_all(path).unwrap(); 69 | } 70 | -------------------------------------------------------------------------------- /battery-ffi/src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::error::Error; 3 | use std::ptr; 4 | use std::slice; 5 | 6 | thread_local! { 7 | static LAST_ERROR: RefCell>> = RefCell::new(None); 8 | } 9 | 10 | pub fn set_last_error(err: E) { 11 | LAST_ERROR.with(|prev| { 12 | *prev.borrow_mut() = Some(Box::new(err)); 13 | }); 14 | } 15 | 16 | pub fn take_last_error() -> Option> { 17 | LAST_ERROR.with(|prev| prev.borrow_mut().take()) 18 | } 19 | 20 | pub fn clear_last_error() { 21 | let _ = take_last_error(); 22 | } 23 | 24 | /// Checks if there was an error before. 25 | /// 26 | /// # Returns 27 | /// 28 | /// `0` if there was no error, `1` if error had occured. 29 | #[no_mangle] 30 | pub extern "C" fn battery_have_last_error() -> libc::c_int { 31 | LAST_ERROR.with(|prev| match *prev.borrow() { 32 | Some(_) => 1, 33 | None => 0, 34 | }) 35 | } 36 | 37 | /// Gets error message length if any error had occurred. 38 | /// 39 | /// # Returns 40 | /// 41 | /// If there was no error before, returns `0`, 42 | /// otherwise returns message length including trailing `\0`. 43 | #[no_mangle] 44 | pub extern "C" fn battery_last_error_length() -> libc::c_int { 45 | // TODO: Support Windows UTF-16 strings 46 | LAST_ERROR.with(|prev| match *prev.borrow() { 47 | Some(ref err) => err.to_string().len() as libc::c_int + 1, 48 | None => 0, 49 | }) 50 | } 51 | 52 | /// Fills passed buffer with an error message. 53 | /// 54 | /// Buffer length can be get with [battery_last_error_length](fn.battery_last_error_length.html) function. 55 | /// 56 | /// # Returns 57 | /// 58 | /// Returns `-1` is passed buffer is `NULL` or too small for error message. 59 | /// Returns `0` if there was no error previously. 60 | /// 61 | /// In all other cases returns error message length. 62 | #[no_mangle] 63 | pub unsafe extern "C" fn battery_last_error_message(buffer: *mut libc::c_char, length: libc::c_int) -> libc::c_int { 64 | if buffer.is_null() { 65 | return -1; 66 | } 67 | 68 | let last_error = match take_last_error() { 69 | Some(err) => err, 70 | None => return 0, 71 | }; 72 | 73 | let error_message = last_error.to_string(); 74 | 75 | let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize); 76 | 77 | if error_message.len() >= buffer.len() { 78 | return -1; 79 | } 80 | 81 | ptr::copy_nonoverlapping(error_message.as_ptr(), buffer.as_mut_ptr(), error_message.len()); 82 | 83 | buffer[error_message.len()] = b'\0'; 84 | 85 | error_message.len() as libc::c_int 86 | } 87 | -------------------------------------------------------------------------------- /battery-ffi/src/manager.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::{Batteries, Battery, Manager}; 4 | 5 | /// Creates new batteries manager instance. 6 | /// 7 | /// # Returns 8 | /// 9 | /// Returns opaque pointer to manager instance. 10 | /// Caller is required to call [battery_manager_free](fn.battery_manager_free.html) 11 | /// to properly free memory. 12 | /// 13 | /// `NULL` pointer might be returned if manager creation had failed. 14 | /// Caller can check [battery_last_error_message](fn.battery_last_error_message.html) 15 | /// for error details. 16 | #[no_mangle] 17 | pub extern "C" fn battery_manager_new() -> *mut Manager { 18 | match Manager::new() { 19 | Ok(manager) => Box::into_raw(Box::new(manager)), 20 | Err(e) => { 21 | crate::errors::set_last_error(e); 22 | ptr::null_mut() 23 | } 24 | } 25 | } 26 | 27 | /// Creates an iterator over batteries from manager instance. 28 | /// 29 | /// See [iterator_next](fn.battery_iterator_next.html) function for iterating over batteries. 30 | /// 31 | /// # Panics 32 | /// 33 | /// This function will panic if passed pointer is `NULL` 34 | /// 35 | /// # Returns 36 | /// 37 | /// `NULL` pointer will be returned if iterator creation had failed. 38 | /// Caller can check [battery_last_error_message](fn.battery_last_error_message.html) 39 | /// for error details. 40 | #[no_mangle] 41 | pub unsafe extern "C" fn battery_manager_iter(ptr: *mut Manager) -> *mut Batteries { 42 | assert!(!ptr.is_null()); 43 | let manager = &*ptr; 44 | 45 | match manager.batteries() { 46 | Ok(iterator) => Box::into_raw(Box::new(iterator)), 47 | Err(e) => { 48 | crate::errors::set_last_error(e); 49 | ptr::null_mut() 50 | } 51 | } 52 | } 53 | 54 | /// Refreshes battery information. 55 | /// 56 | /// # Panics 57 | /// 58 | /// This function will panic if any passed pointer is `NULL` 59 | /// 60 | /// # Returns 61 | /// 62 | /// `0` if everything is okay, `-1` if refresh failed and `battery_ptr` contains stale information. 63 | pub unsafe extern "C" fn battery_manager_refresh(manager_ptr: *mut Manager, battery_ptr: *mut Battery) -> libc::c_int { 64 | assert!(!manager_ptr.is_null()); 65 | let manager = &mut *manager_ptr; 66 | 67 | assert!(!battery_ptr.is_null()); 68 | let mut battery = &mut *battery_ptr; 69 | 70 | match manager.refresh(&mut battery) { 71 | Ok(_) => 0, 72 | Err(e) => { 73 | crate::errors::set_last_error(e); 74 | -1 75 | } 76 | } 77 | } 78 | 79 | /// Frees manager instance. 80 | #[no_mangle] 81 | pub unsafe extern "C" fn battery_manager_free(ptr: *mut Manager) { 82 | if ptr.is_null() { 83 | return; 84 | } 85 | 86 | Box::from_raw(ptr); 87 | } 88 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/iokit/sys.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types, dead_code, unused)] 2 | 3 | // Functions here are copied from the `IOKit-sys` (https://crates.io/crates/iokit-sys) crate 4 | // and rewritten to use `core_foundation` types. 5 | 6 | use core_foundation::base::{mach_port_t, CFAllocatorRef}; 7 | use core_foundation::dictionary::{CFDictionaryRef, CFMutableDictionaryRef}; 8 | use libc::c_char; 9 | use mach::{boolean, kern_return}; 10 | 11 | pub type io_object_t = mach_port_t; 12 | pub type io_registry_entry_t = io_object_t; 13 | pub type io_service_t = io_object_t; 14 | pub type io_iterator_t = io_object_t; 15 | 16 | pub type IOOptionBits = u32; 17 | 18 | pub const IOPM_SERVICE_NAME: *const c_char = b"IOPMPowerSource\0".as_ptr() as *const c_char; 19 | 20 | extern "C" { 21 | // https://developer.apple.com/documentation/iokit/kiomasterportdefault 22 | pub static kIOMasterPortDefault: mach_port_t; 23 | 24 | // https://developer.apple.com/documentation/iokit/1514652-iomasterport 25 | // Should be deallocated with `mach_port_deallocate(mach_task_self(), masterPort)` 26 | pub fn IOMasterPort(bootstrapPort: mach_port_t, masterPort: *mut mach_port_t) -> kern_return::kern_return_t; 27 | 28 | // https://developer.apple.com/documentation/iokit/1514687-ioservicematching 29 | // The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification 30 | // which will consume a reference, otherwise it should be released with CFRelease by the caller. 31 | pub fn IOServiceMatching(name: *const c_char) -> CFMutableDictionaryRef; 32 | 33 | // https://developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices?language=objc 34 | // An `existing` iterator handle is returned on success, and should be released by the caller 35 | // when the iteration is finished. 36 | pub fn IOServiceGetMatchingServices( 37 | masterPort: mach_port_t, 38 | matching: CFDictionaryRef, 39 | existing: *mut io_iterator_t, 40 | ) -> kern_return::kern_return_t; 41 | 42 | // https://developer.apple.com/documentation/iokit/1514310-ioregistryentrycreatecfpropertie 43 | // The caller should release `properties` with CFRelease. 44 | pub fn IORegistryEntryCreateCFProperties( 45 | entry: io_registry_entry_t, 46 | properties: *mut CFMutableDictionaryRef, 47 | allocator: CFAllocatorRef, 48 | options: IOOptionBits, 49 | ) -> kern_return::kern_return_t; 50 | 51 | // https://developer.apple.com/documentation/iokit/1514741-ioiteratornext 52 | // The element should be released by the caller when it is finished. 53 | pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t; 54 | 55 | pub fn IOIteratorIsValid(iterator: io_iterator_t) -> boolean::boolean_t; 56 | 57 | pub fn IOObjectRelease(object: io_object_t) -> kern_return::kern_return_t; 58 | } 59 | -------------------------------------------------------------------------------- /battery/src/types/technology.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::str; 3 | 4 | use crate::Error; 5 | 6 | /// Possible battery technologies. 7 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] 8 | pub enum Technology { 9 | Unknown, 10 | LithiumIon, 11 | LeadAcid, 12 | LithiumPolymer, 13 | NickelMetalHydride, 14 | NickelCadmium, 15 | NickelZinc, 16 | LithiumIronPhosphate, 17 | RechargeableAlkalineManganese, 18 | 19 | // Awaiting for https://github.com/rust-lang/rust/issues/44109 20 | #[doc(hidden)] 21 | __Nonexhaustive, 22 | } 23 | 24 | impl str::FromStr for Technology { 25 | type Err = Error; 26 | 27 | fn from_str(s: &str) -> Result { 28 | let tech = match s { 29 | _ if s.eq_ignore_ascii_case("li-i") => Technology::LithiumIon, 30 | _ if s.eq_ignore_ascii_case("li-ion") => Technology::LithiumIon, 31 | _ if s.eq_ignore_ascii_case("lion") => Technology::LithiumIon, 32 | _ if s.eq_ignore_ascii_case("pb") => Technology::LeadAcid, 33 | _ if s.eq_ignore_ascii_case("pbac") => Technology::LeadAcid, 34 | _ if s.eq_ignore_ascii_case("lip") => Technology::LithiumPolymer, 35 | _ if s.eq_ignore_ascii_case("lipo") => Technology::LithiumPolymer, 36 | _ if s.eq_ignore_ascii_case("li-poly") => Technology::LithiumPolymer, 37 | _ if s.eq_ignore_ascii_case("nimh") => Technology::NickelMetalHydride, 38 | _ if s.eq_ignore_ascii_case("nicd") => Technology::NickelCadmium, 39 | _ if s.eq_ignore_ascii_case("nizn") => Technology::NickelZinc, 40 | _ if s.eq_ignore_ascii_case("life") => Technology::LithiumIronPhosphate, 41 | _ if s.eq_ignore_ascii_case("ram") => Technology::RechargeableAlkalineManganese, 42 | // TODO: warn! 43 | _ => Technology::Unknown, 44 | }; 45 | 46 | Ok(tech) 47 | } 48 | } 49 | 50 | impl fmt::Display for Technology { 51 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 52 | let display = match self { 53 | Technology::Unknown => "unknown", 54 | Technology::LithiumIon => "lithium-ion", 55 | Technology::LeadAcid => "lead-acid", 56 | Technology::LithiumPolymer => "lithium-polymer", 57 | Technology::NickelMetalHydride => "nickel-metal-hydride", 58 | Technology::NickelCadmium => "nickel-cadmium", 59 | Technology::NickelZinc => "nickel-zinc", 60 | Technology::LithiumIronPhosphate => "lithium-iron-phosphate", 61 | Technology::RechargeableAlkalineManganese => "rechargeable-alkaline-manganese", 62 | _ => "unknown", 63 | }; 64 | 65 | write!(f, "{}", display) 66 | } 67 | } 68 | 69 | impl Default for Technology { 70 | fn default() -> Self { 71 | Technology::Unknown 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /battery/src/platform/windows/ffi/ioctl/info.rs: -------------------------------------------------------------------------------- 1 | //! https://docs.microsoft.com/en-us/windows/desktop/power/battery-information-str 2 | 3 | #![allow(non_snake_case, clippy::unreadable_literal)] 4 | 5 | use std::default::Default; 6 | use std::mem; 7 | use std::ops; 8 | use std::str::{self, FromStr}; 9 | 10 | use crate::Technology; 11 | use winapi::shared::ntdef; 12 | 13 | pub const BATTERY_CAPACITY_RELATIVE: ntdef::ULONG = 0x40000000; 14 | pub const BATTERY_SYSTEM_BATTERY: ntdef::ULONG = 0x80000000; 15 | 16 | STRUCT! {#[cfg_attr(target_arch = "x86", repr(packed))] #[derive(Debug)] struct BATTERY_INFORMATION { 17 | Capabilities: ntdef::ULONG, 18 | Technology: ntdef::UCHAR, 19 | Reserved: [ntdef::UCHAR; 3], 20 | Chemistry: [ntdef::UCHAR; 4], 21 | DesignedCapacity: ntdef::ULONG, // mWh 22 | FullChargedCapacity: ntdef::ULONG, // mWh 23 | DefaultAlert1: ntdef::ULONG, 24 | DefaultAlert2: ntdef::ULONG, 25 | CriticalBias: ntdef::ULONG, 26 | CycleCount: ntdef::ULONG, 27 | }} 28 | 29 | impl Default for BATTERY_INFORMATION { 30 | #[inline] 31 | fn default() -> Self { 32 | unsafe { mem::zeroed() } 33 | } 34 | } 35 | 36 | #[derive(Debug)] 37 | pub struct BatteryInformation(BATTERY_INFORMATION); 38 | 39 | impl Default for BatteryInformation { 40 | fn default() -> Self { 41 | BatteryInformation(BATTERY_INFORMATION::default()) 42 | } 43 | } 44 | 45 | impl ops::Deref for BatteryInformation { 46 | type Target = BATTERY_INFORMATION; 47 | 48 | fn deref(&self) -> &Self::Target { 49 | &self.0 50 | } 51 | } 52 | impl ops::DerefMut for BatteryInformation { 53 | fn deref_mut(&mut self) -> &mut Self::Target { 54 | &mut self.0 55 | } 56 | } 57 | 58 | impl BatteryInformation { 59 | #[inline] 60 | pub fn is_system_battery(&self) -> bool { 61 | (self.0.Capabilities & BATTERY_SYSTEM_BATTERY) != 0 62 | } 63 | 64 | #[inline] 65 | pub fn is_relative(&self) -> bool { 66 | (self.0.Capabilities & BATTERY_CAPACITY_RELATIVE) != 0 67 | } 68 | 69 | pub fn technology(&self) -> Technology { 70 | let raw = unsafe { str::from_utf8_unchecked(&self.0.Chemistry) }; 71 | match Technology::from_str(raw) { 72 | Ok(tech) => tech, 73 | Err(_) => Technology::Unknown, 74 | } 75 | } 76 | 77 | // Originally `mWh`, matches `Battery::energy_full_design` result 78 | #[inline] 79 | pub fn designed_capacity(&self) -> u32 { 80 | self.0.DesignedCapacity 81 | } 82 | 83 | // Originally `mWh`, matches `Battery::energy_full` result 84 | #[inline] 85 | pub fn full_charged_capacity(&self) -> u32 { 86 | self.0.FullChargedCapacity 87 | } 88 | 89 | pub fn cycle_count(&self) -> Option { 90 | if self.0.CycleCount == 0 { 91 | None 92 | } else { 93 | Some(self.0.CycleCount) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /battery-ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides C ABI interface for [battery](https://crates.io/crate/battery) crate. 2 | //! 3 | //! # Bindings generation 4 | //! 5 | //! Among library creation this crate generates `battery_ffi.h` file, enabled by default by `cbindgen` feature, 6 | //! which might be useful for automatic bindings generation or just with plain `C`/`C++` development. 7 | //! 8 | //! After build it will be located somewhere at `target/*/build/battery-ffi-*/out/`, 9 | //! depending on build profile (`debug`/`release`) and build hash. 10 | //! 11 | //! Disabling `cbindgen` feature might speed up compilation a little bit, 12 | //! especially if you don't need the header file. 13 | //! 14 | //! # Examples 15 | //! 16 | //! ```c 17 | //! #include "battery_ffi.h" 18 | //! 19 | //! void main() { 20 | //! Manager *manager = battery_manager_new(); 21 | //! // .. handle `manager == NULL` here .. 22 | //! Batteries *iterator = battery_manager_iter(manager); 23 | //! // .. handle `iterator == NULL` here .. 24 | //! while (true) { 25 | //! Battery *battery = battery_iterator_next(iterator); 26 | //! // .. handle possible error here .. 27 | //! if (battery == NULL) { 28 | //! break; 29 | //! } 30 | //! 31 | //! // Use some `battery_get_*` functions here 32 | //! 33 | //! battery_free(battery); 34 | //! } 35 | //! 36 | //! battery_iterator_free(iterator); 37 | //! battery_manager_free(manager); 38 | //! } 39 | //! ``` 40 | //! 41 | //! Also, check the `examples/` directory in the repository for examples with C and Python. 42 | 43 | #![doc(html_root_url = "https://docs.rs/battery-ffi/0.7.5")] 44 | #![allow(clippy::missing_safety_doc)] 45 | 46 | // cbindgen==0.8.0 fails to export typedefs for opaque pointers 47 | // from the battery crate, if this line is missing 48 | extern crate battery as battery_lib; 49 | 50 | mod battery; 51 | mod errors; 52 | mod iterator; 53 | mod manager; 54 | mod state; 55 | mod technology; 56 | 57 | /// Opaque struct representing battery manager. 58 | /// 59 | /// End users should consider it as a some memory somewhere in the heap, 60 | /// and work with it only via library methods. 61 | pub type Manager = battery_lib::Manager; 62 | 63 | /// Opaque struct representing batteries iterator. 64 | /// 65 | /// End users should consider it as a some memory somewhere in the heap, 66 | /// and work with it only via library methods. 67 | pub type Batteries = battery_lib::Batteries; 68 | 69 | /// Opaque struct representing battery. 70 | /// 71 | /// End users should consider it as a some memory somewhere in the heap, 72 | /// and work with it only via library methods. 73 | pub type Battery = battery_lib::Battery; 74 | 75 | pub use self::battery::*; 76 | pub use self::errors::{battery_have_last_error, battery_last_error_length, battery_last_error_message}; 77 | pub use self::iterator::*; 78 | pub use self::manager::*; 79 | pub use self::state::*; 80 | pub use self::technology::*; 81 | -------------------------------------------------------------------------------- /battery/src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Errors handling 2 | 3 | use std::borrow::Cow; 4 | use std::error::Error as StdError; 5 | use std::fmt; 6 | use std::io; 7 | use std::result; 8 | 9 | pub type Result = result::Result; 10 | 11 | /// Battery routines error. 12 | /// 13 | /// Since all operations are basically I/O of some kind, 14 | /// this is a thin wrapper around `::std::io::Error` with option 15 | /// to store custom description for debugging purposes. 16 | #[derive(Debug)] 17 | pub struct Error { 18 | source: io::Error, 19 | description: Option>, 20 | } 21 | 22 | impl Error { 23 | #[allow(unused)] 24 | pub(crate) fn new(e: io::Error, description: T) -> Error 25 | where 26 | T: Into>, 27 | { 28 | Error { 29 | source: e, 30 | description: Some(description.into()), 31 | } 32 | } 33 | 34 | #[allow(unused)] 35 | pub(crate) fn not_found(description: T) -> Error 36 | where 37 | T: Into>, 38 | { 39 | Error { 40 | source: io::Error::from(io::ErrorKind::NotFound), 41 | description: Some(description.into()), 42 | } 43 | } 44 | 45 | #[allow(unused)] 46 | pub(crate) fn invalid_data(description: T) -> Error 47 | where 48 | T: Into>, 49 | { 50 | Error { 51 | source: io::Error::from(io::ErrorKind::InvalidData), 52 | description: Some(description.into()), 53 | } 54 | } 55 | } 56 | 57 | impl StdError for Error { 58 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 59 | Some(&self.source) 60 | } 61 | } 62 | 63 | impl fmt::Display for Error { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | match &self.description { 66 | Some(desc) => write!(f, "{}", desc), 67 | None => self.source.fmt(f), 68 | } 69 | } 70 | } 71 | 72 | impl From for Error { 73 | fn from(e: io::Error) -> Self { 74 | Error { 75 | source: e, 76 | description: None, 77 | } 78 | } 79 | } 80 | 81 | #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] 82 | mod nix_impl { 83 | use std::io; 84 | 85 | use super::Error; 86 | 87 | impl From for Error { 88 | fn from(e: nix::Error) -> Self { 89 | match e { 90 | nix::Error::Sys(errno) => Error { 91 | source: io::Error::from_raw_os_error(errno as i32), 92 | description: Some(errno.desc().into()), 93 | }, 94 | nix::Error::InvalidPath => Error { 95 | source: io::Error::new(io::ErrorKind::InvalidInput, e), 96 | description: Some("Invalid path".into()), 97 | }, 98 | nix::Error::InvalidUtf8 => Error { 99 | source: io::Error::new(io::ErrorKind::InvalidData, e), 100 | description: Some("Invalid UTF-8 string".into()), 101 | }, 102 | nix::Error::UnsupportedOperation => Error { 103 | source: io::Error::new(io::ErrorKind::Other, e), 104 | description: Some("Unsupported operation".into()), 105 | }, 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /battery/src/platform/darwin/tests.rs: -------------------------------------------------------------------------------- 1 | use super::device::IoKitDevice; 2 | use super::traits::DataSource; 3 | use crate::platform::traits::BatteryDevice; 4 | use crate::units::energy::watt_hour; 5 | use crate::units::power::milliwatt; 6 | use crate::units::{ElectricCharge, ElectricCurrent, ElectricPotential, ThermodynamicTemperature, Time}; 7 | use crate::Result; 8 | 9 | /// This data source is not using uom types, because it is easier to create test suites 10 | /// from the `ioreg` tool output that way (which values are in mV, mA, mAh and mWh). 11 | #[derive(Debug, Default)] 12 | struct TestDataSource { 13 | fully_charged: bool, 14 | external_connected: bool, 15 | is_charging: bool, 16 | voltage: u32, 17 | amperage: i32, 18 | design_capacity: u32, 19 | max_capacity: u32, 20 | current_capacity: u32, 21 | temperature: Option, 22 | cycle_count: Option, 23 | } 24 | 25 | impl DataSource for TestDataSource { 26 | fn refresh(&mut self) -> Result<()> { 27 | Ok(()) 28 | } 29 | 30 | fn fully_charged(&self) -> bool { 31 | self.fully_charged 32 | } 33 | 34 | fn external_connected(&self) -> bool { 35 | self.external_connected 36 | } 37 | 38 | fn is_charging(&self) -> bool { 39 | self.is_charging 40 | } 41 | 42 | fn voltage(&self) -> ElectricPotential { 43 | millivolt!(self.voltage) 44 | } 45 | 46 | fn amperage(&self) -> ElectricCurrent { 47 | milliampere!(self.amperage.abs()) 48 | } 49 | 50 | fn design_capacity(&self) -> ElectricCharge { 51 | milliampere_hour!(self.design_capacity) 52 | } 53 | 54 | fn max_capacity(&self) -> ElectricCharge { 55 | milliampere_hour!(self.max_capacity) 56 | } 57 | 58 | fn current_capacity(&self) -> ElectricCharge { 59 | milliampere_hour!(self.current_capacity) 60 | } 61 | 62 | fn temperature(&self) -> Option { 63 | self.temperature.map(|value| celsius!(value)) 64 | } 65 | 66 | fn cycle_count(&self) -> Option { 67 | self.cycle_count 68 | } 69 | 70 | fn time_remaining(&self) -> Option