diff --git a/.travis.yml b/.travis.yml index 8b42c9c..df6d6c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,9 +52,7 @@ before_install: - cargo install --force cross - rustup component add clippy -script: - - cross build --target=${TARGET} --release - - cross clippy --release -- -D warnings +script: ci/script.sh notifications: email: diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ada746e..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] -### Fixed -- Fix multiplication overflow while calculating battery percentage in Mac OS by [@mindriot101](https://github.com/mindriot101) [#10](https://github.com/svartalf/rust-battery/pull/10) -- Fix wrong units for consumption graph in `battery-cli`, should be `W` instead of `Wh` [#9](https://github.com/svartalf/rust-battery/issues/9) -- Fix non-uniform path import that was breaking compilation for Rust<1.32 [#6](https://github.com/svartalf/rust-battery/issues/6) -- Fix `time_to_empty` and `time_to_full` calculations for Linux when charger is unplugged but driver still reports zero `energy_rate` by [@kerhong](https://github.com/kerhong) [#5](https://github.com/svartalf/rust-battery/pull/5) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 120000 index 0000000..150339b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +battery/CHANGELOG.md \ No newline at end of file diff --git a/README.md b/README.md index 6d29328..20ab32c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Latest Version](https://img.shields.io/crates/v/battery.svg)](https://crates.io/crates/battery) [![Latest Version](https://docs.rs/battery/badge.svg)](https://docs.rs/battery) [![Build Status](https://travis-ci.org/svartalf/rust-battery.svg?branch=master)](https://travis-ci.org/svartalf/rust-battery) -[![dependency status](https://deps.rs/crate/battery/0.6.0/status.svg)](https://deps.rs/crate/battery/0.6.0) +[![dependency status](https://deps.rs/crate/battery/0.6.1/status.svg)](https://deps.rs/crate/battery/0.6.1) ![Apache 2.0 OR MIT licensed](https://img.shields.io/badge/license-Apache2.0%2FMIT-blue.svg) Rust crate providing cross-platform information about batteries. diff --git a/battery-cli/Cargo.toml b/battery-cli/Cargo.toml index dddd9f8..a1f5ccc 100644 --- a/battery-cli/Cargo.toml +++ b/battery-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "battery-cli" -version = "0.1.1" +version = "0.1.2" authors = ["svartalf "] edition = "2018" description = "CLI tool for batteries reports" @@ -25,7 +25,7 @@ test = false cfg-if = "0.1" [target.'cfg(not(windows))'.dependencies] -battery = { path = "../battery" } +battery = "0.6.1" humantime = "1.2.0" tui = "0.4.0" termion = "1.5.1" diff --git a/battery-cli/README.md b/battery-cli/README.md index b562c90..b2e715c 100644 --- a/battery-cli/README.md +++ b/battery-cli/README.md @@ -3,7 +3,7 @@ [![Latest Version](https://img.shields.io/crates/v/battery-cli.svg)](https://crates.io/crates/battery-cli) [![Latest Version](https://docs.rs/battery-cli/badge.svg)](https://docs.rs/battery-cli) [![Build Status](https://travis-ci.org/svartalf/rust-battery.svg?branch=master)](https://travis-ci.org/svartalf/rust-battery) -[![dependency status](https://deps.rs/crate/battery-cli/0.1.0/status.svg)](https://deps.rs/crate/battery-cli/0.1.0) +[![dependency status](https://deps.rs/crate/battery-cli/0.1.2/status.svg)](https://deps.rs/crate/battery-cli/0.1.2) ![Apache 2.0 OR MIT licensed](https://img.shields.io/badge/license-Apache2.0%2FMIT-blue.svg) `battery-cli` is a Proof-Of-Concept binary crate, that provides terminal user interface diff --git a/battery-cli/src/ui/mod.rs b/battery-cli/src/ui/mod.rs index 8b4c087..60a1f57 100644 --- a/battery-cli/src/ui/mod.rs +++ b/battery-cli/src/ui/mod.rs @@ -31,7 +31,7 @@ use tui::layout::*; use tui::style::*; use tui::widgets::*; -use battery::Battery; +use battery::{Battery, State}; use crate::ui::app::BatteryStats; use crate::ui::util::event::{Event, Events}; @@ -208,7 +208,7 @@ fn draw_common_information(f: &mut Frame, area: Rect, battery: &Battery) w Table::new(header.iter(), rows) .header_style(Style::default().fg(Color::DarkGray)) .block(block) - .widths(&[15, 20]) + .widths(&[17, 17]) .render(f, area); } @@ -222,8 +222,13 @@ fn draw_energy_information(f: &mut Frame, area: Rect, battery: &Battery) w let current = &format!("{:.2} Wh", battery.energy() as f32 / 1000.0); let last_full = &format!("{:.2} Wh", battery.energy_full() as f32 / 1000.0); let full_design = &format!("{:.2} Wh", battery.energy_full_design() as f32 / 1000.0); + let consumption_label = match battery.state() { + State::Charging => "Charging with", + State::Discharging => "Discharging with", + _ => "Consumption", + }; let items = vec![ - vec!["Consumption", consumption], + vec![consumption_label, consumption], vec!["Voltage", voltage], vec!["Capacity", capacity], vec!["Current", current], @@ -239,7 +244,7 @@ fn draw_energy_information(f: &mut Frame, area: Rect, battery: &Battery) w Table::new(header.iter(), rows) .header_style(Style::default().fg(Color::DarkGray)) .block(block) - .widths(&[15, 20]) + .widths(&[17, 17]) .render(f, area); } @@ -269,7 +274,7 @@ fn draw_time_information(f: &mut Frame, area: Rect, battery: &Battery) whe Table::new(header.iter(), rows) .header_style(Style::default().fg(Color::DarkGray)) .block(block) - .widths(&[15, 20]) + .widths(&[17, 17]) .render(f, area); } @@ -293,6 +298,6 @@ fn draw_env_information(f: &mut Frame, area: Rect, battery: &Battery) wher Table::new(header.iter(), rows) .header_style(Style::default().fg(Color::DarkGray)) .block(block) - .widths(&[15, 20]) + .widths(&[17, 17]) .render(f, area); } diff --git a/battery-ffi/Cargo.toml b/battery-ffi/Cargo.toml index f2af4a1..1070a1b 100644 --- a/battery-ffi/Cargo.toml +++ b/battery-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "battery-ffi" -version = "0.1.3" +version = "0.1.4" authors = ["svartalf "] edition = "2018" description = "FFI bindings for battery crate" @@ -22,7 +22,7 @@ crate-type = ["cdylib"] default = ["cbindgen"] [dependencies] -battery = { path = "../battery" } +battery = "0.6.1" libc = "0.2.48" [build-dependencies] diff --git a/battery-ffi/README.md b/battery-ffi/README.md index d59f1c7..e114cec 100644 --- a/battery-ffi/README.md +++ b/battery-ffi/README.md @@ -3,7 +3,7 @@ [![Latest Version](https://img.shields.io/crates/v/battery-ffi.svg)](https://crates.io/crates/battery-ffi) [![Latest Version](https://docs.rs/battery-ffi/badge.svg)](https://docs.rs/battery-ffi) [![Build Status](https://travis-ci.org/svartalf/rust-battery.svg?branch=master)](https://travis-ci.org/svartalf/rust-battery) -[![dependency status](https://deps.rs/crate/battery-ffi/0.1.2/status.svg)](https://deps.rs/crate/battery-ffi/0.1.2) +[![dependency status](https://deps.rs/crate/battery-ffi/0.1.4/status.svg)](https://deps.rs/crate/battery-ffi/0.1.4) ![Apache 2.0 OR MIT licensed](https://img.shields.io/badge/license-Apache2.0%2FMIT-blue.svg) This is a FFI bindings for [battery](https://github.com/svartalf/rust-battery/tree/master/battery) diff --git a/battery/CHANGELOG.md b/battery/CHANGELOG.md new file mode 100644 index 0000000..01d097e --- /dev/null +++ b/battery/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.6.1] - 2019-02-27 +### Fixed +- Fix energy and remaining time calculations for MacOS [#8](https://github.com/svartalf/rust-battery/issues/8), [#11](https://github.com/svartalf/rust-battery/pull/11) +- Fix multiplication overflow while calculating battery percentage in Mac OS by [@mindriot101](https://github.com/mindriot101) [#10](https://github.com/svartalf/rust-battery/pull/10) +- Fix wrong units for consumption graph in `battery-cli`, should be `W` instead of `Wh` [#9](https://github.com/svartalf/rust-battery/issues/9) +- Fix non-uniform path import that was breaking compilation for Rust<1.32 [#6](https://github.com/svartalf/rust-battery/issues/6) +- Fix `time_to_empty` and `time_to_full` calculations for Linux when charger is unplugged but driver still reports zero `energy_rate` by [@kerhong](https://github.com/kerhong) [#5](https://github.com/svartalf/rust-battery/pull/5) diff --git a/battery/Cargo.toml b/battery/Cargo.toml index 77f1335..2b82a09 100644 --- a/battery/Cargo.toml +++ b/battery/Cargo.toml @@ -1,15 +1,14 @@ [package] name = "battery" -version = "0.6.0" +version = "0.6.1" authors = ["svartalf "] edition = "2018" description = "Cross-platform information about batteries" repository = "https://github.com/svartalf/rust-battery" readme = "README.md" categories = ["os"] -keywords = ["battery", "linux", "macos", "windows", "freebsd", "dragonflybsd"] +keywords = ["battery", "linux", "macos", "windows", "freebsd"] license = "Apache-2.0 OR MIT" -build = "build.rs" [badges] travis-ci = { repository = "svartalf/rust-battery", branch = "master" } diff --git a/battery/src/platform/macos/device.rs b/battery/src/platform/macos/device.rs index f91282b..3e3d0d2 100644 --- a/battery/src/platform/macos/device.rs +++ b/battery/src/platform/macos/device.rs @@ -1,6 +1,5 @@ // For keys reference see: https://developer.apple.com/documentation/kernel/iopmpowersource?language=objc // Additional keys worth to implement later: -// * "Cycle Count" // * "ChargerData" -> // - ChargingVoltage // - ChargingCurrent @@ -8,142 +7,82 @@ use std::io; use std::str; +use std::boxed::Box; use std::convert::AsRef; use std::time::Duration; use crate::types::{State, Technology}; use crate::platform::traits::BatteryDevice; -use super::iokit; +use super::traits::DataSource; #[derive(Debug)] -pub struct IoKitDevice{ - source: iokit::PowerSource, - - fully_charged: bool, - external_connected: bool, - is_charging: bool, - - voltage: u32, // mV - amperage: u32, // mA - temperature: Option, - cycle_count: Option, - - design_capacity: u32, // mAh - max_capacity: u32, // mAh - current_capacity: u32, // mAh +pub struct IoKitDevice { + source: Box, manufacturer: Option, model: Option, serial_number: Option, - - instant_time_to_empty: Option, - instant_time_to_full: Option, } impl IoKitDevice { - pub fn new() -> io::Result { - let ps = iokit::PowerSource::new()?; - - let fully_charged = ps.get_bool(b"FullyCharged") - .expect("IOKit is not providing required data"); - let external_connected = ps.get_bool(b"ExternalConnected") - .expect("IOKit is not providing required data"); - let is_charging = ps.get_bool(b"IsCharging") - .expect("IOKit is not providing required data"); - - let voltage = ps.get_u32(b"Voltage") - .expect("IOKit is not providing required data"); - let amperage = ps.get_u32(b"Amperage") - .expect("IOKit is not providing required data"); - let design_capacity = ps.get_u32(b"DesignCapacity") - .expect("IOKit is not providing required data"); - let max_capacity = ps.get_u32(b"MaxCapacity") - .expect("IOKit is not providing required data"); - let current_capacity = ps.get_u32(b"CurrentCapacity") - .expect("IOKit is not providing required data"); - let temperature = ps.get_isize(b"Temperature") - .map(|value| value as f32 / 100.0); - let cycle_count = ps.get_u32(b"CycleCount"); - let instant_time_to_empty = ps.get_isize(b"InstantTimeToEmpty") - .and_then(|val| { - if val == 65535 { - None - } else { - Some(val) - } - }); - let instant_time_to_full = ps.get_isize(b"InstantTimeToFull") - .and_then(|val| { - if val == 65535 { - None - } else { - Some(val) - } - }); - - let manufacturer = ps.get_string(b"Manufacturer"); - let model = ps.get_string(b"DeviceName"); - let serial_number = ps.get_string(b"BatterySerialNumber"); - - Ok(IoKitDevice{ - source: ps, - - fully_charged, - external_connected, - is_charging, - voltage, - amperage, - temperature, - cycle_count, - design_capacity, - max_capacity, - current_capacity, - manufacturer, - model, - serial_number, - instant_time_to_empty, - instant_time_to_full, - }) + pub fn new() -> io::Result { + let ds = T::new()?; + + Ok(ds.into()) } } +// Note about `mWh` values calculation, used in `energy`, `energy_full` and `energy_full_design` +// method, which caused https://github.com/svartalf/rust-battery/issues/8 bug +// +// Formula: mWh = mAh * V +// +// But `self.source.voltage()` returns `mV`, not the `V` units. impl BatteryDevice for IoKitDevice { fn energy(&self) -> u32 { - self.current_capacity * self.voltage + let voltage = self.source.voltage() as f32 / 1_000.0; // V units + + (self.source.current_capacity() as f32 * voltage) as u32 } fn energy_full(&self) -> u32 { - self.max_capacity * self.voltage + let voltage = self.source.voltage() as f32 / 1_000.0; // V units + + (self.source.max_capacity() as f32 * voltage) as u32 } fn energy_full_design(&self) -> u32 { - self.design_capacity * self.voltage + let voltage = self.source.voltage() as f32 / 1_000.0; // V units + + (self.source.design_capacity() as f32 * voltage) as u32 } fn energy_rate(&self) -> u32 { - self.amperage * self.voltage + let voltage = self.source.voltage() as f32 / 1_000.0; // V units + + (self.source.amperage().abs() as f32 * voltage) as u32 } fn percentage(&self) -> f32 { - 100.0f32 * ((self.energy() as f32) / (self.energy_full() as f32)) + 100.0 * ((self.energy() as f32) / (self.energy_full() as f32)) } fn state(&self) -> State { match () { - _ if !self.external_connected => State::Discharging, - _ if self.is_charging => State::Charging, - _ if self.current_capacity == 0 => State::Empty, - _ if self.fully_charged => State::Full, + _ if !self.source.external_connected() => State::Discharging, + _ if self.source.is_charging() => State::Charging, + _ if self.source.current_capacity() == 0 => State::Empty, + _ if self.source.fully_charged() => State::Full, _ => State::Unknown, } } fn voltage(&self) -> u32 { - self.voltage + self.source.voltage() } fn temperature(&self) -> Option { - self.temperature + self.source.temperature() } fn vendor(&self) -> Option<&str> { @@ -163,20 +102,38 @@ impl BatteryDevice for IoKitDevice { } fn cycle_count(&self) -> Option { - self.cycle_count + self.source.cycle_count() } fn time_to_full(&self) -> Option { - self.instant_time_to_full.and_then(|value| { - // TODO: Possible `value` invalid cast - Some(Duration::from_secs(value as u64)) - }) + if self.state() == State::Charging { + self.source.time_remaining() + } else { + None + } } fn time_to_empty(&self) -> Option { - self.instant_time_to_empty.and_then(|value| { - // TODO: Possible `value` invalid cast - Some(Duration::from_secs(value as u64)) - }) + if self.state() == State::Discharging { + self.source.time_remaining() + } else { + None + } + } +} + +impl From for IoKitDevice where T: DataSource { + fn from(ds: T) -> IoKitDevice { + let manufacturer = ds.manufacturer(); + let model = ds.device_name(); + let serial_number = ds.serial_number(); + + IoKitDevice { + source: Box::new(ds), + + manufacturer, + model, + serial_number, + } } } diff --git a/battery/src/platform/macos/iokit.rs b/battery/src/platform/macos/iokit.rs index ae17fce..43a0d27 100644 --- a/battery/src/platform/macos/iokit.rs +++ b/battery/src/platform/macos/iokit.rs @@ -1,18 +1,22 @@ use std::io; +use std::i32; use std::mem; +use std::time::Duration; use std::ffi::{CStr, CString}; use CoreFoundation_sys as core; use IOKit_sys as iokit; use mach::{port, kern_return}; +use super::traits::DataSource; + const IOPM_SERVICE_NAME: *const libc::c_char = b"IOPMPowerSource\0".as_ptr() as *const libc::c_char; #[derive(Debug)] pub struct PowerSource(core::dictionary::CFMutableDictionaryRef); impl PowerSource { - pub fn new() -> io::Result { + pub fn get_props() -> io::Result { let mut master_port: port::mach_port_t = port::MACH_PORT_NULL; let res = unsafe { @@ -45,6 +49,7 @@ impl PowerSource { // Uncomment this to see all existing keys in the `props`. // Will write to stderr. Do not use in production. + // // unsafe { core::CFShow(props as *const libc::c_void); } unsafe { @@ -118,6 +123,30 @@ impl PowerSource { } } + pub fn get_i32(&self, key: &[u8]) -> Option { + if let Some(value_ptr) = self.get_dict_value_ptr(key) { + unsafe { + debug_assert!(core::CFGetTypeID(value_ptr) == core::CFNumberGetTypeID()); + } + + let mut value = 0i32; + let res = unsafe { + core::CFNumberGetValue( + value_ptr as core::CFNumberRef, + core::kCFNumberIntType, + &mut value as *mut _ as *mut libc::c_void + ) + }; + if res == 1 { + Some(value) + } else { + None + } + } else { + None + } + } + pub fn get_string(&self, key: &[u8]) -> Option { if let Some(value_ptr) = self.get_dict_value_ptr(key) { unsafe { @@ -174,6 +203,86 @@ impl PowerSource { } +impl DataSource for PowerSource { + fn new() -> io::Result where Self: Sized { + PowerSource::get_props() + } + + fn fully_charged(&self) -> bool { + self.get_bool(b"FullyCharged") + .expect("IOKit is not providing required data") + } + + fn external_connected(&self) -> bool { + self.get_bool(b"ExternalConnected") + .expect("IOKit is not providing required data") + } + + fn is_charging(&self) -> bool { + self.get_bool(b"IsCharging") + .expect("IOKit is not providing required data") + } + + fn voltage(&self) -> u32 { + self.get_u32(b"Voltage") + .expect("IOKit is not providing required data") + } + + fn amperage(&self) -> i32 { + self.get_i32(b"Amperage") + .expect("IOKit is not providing required data") + } + + fn design_capacity(&self) -> u32 { + self.get_u32(b"DesignCapacity") + .expect("IOKit is not providing required data") + } + + fn max_capacity(&self) -> u32 { + self.get_u32(b"MaxCapacity") + .expect("IOKit is not providing required data") + } + + fn current_capacity(&self) -> u32 { + self.get_u32(b"CurrentCapacity") + .expect("IOKit is not providing required data") + } + + fn temperature(&self) -> Option { + self.get_isize(b"Temperature") + .map(|value| value as f32 / 100.0) + } + + fn cycle_count(&self) -> Option { + self.get_u32(b"CycleCount") + } + + fn time_remaining(&self) -> Option { + self.get_i32(b"TimeRemaining") + .and_then(|val| { + if val == i32::MAX { + None + } else { + // TODO: Is it possible to have negative `TimeRemaining`? + let seconds = val.abs() as u64 * 60; + Some(Duration::from_secs(seconds)) + } + }) + } + + fn manufacturer(&self) -> Option { + self.get_string(b"Manufacturer") + } + + fn device_name(&self) -> Option { + self.get_string(b"DeviceName") + } + + fn serial_number(&self) -> Option { + self.get_string(b"BatterySerialNumber") + } +} + impl Drop for PowerSource { fn drop(&mut self) { unsafe { diff --git a/battery/src/platform/macos/mod.rs b/battery/src/platform/macos/mod.rs index 8f1e807..49a0832 100644 --- a/battery/src/platform/macos/mod.rs +++ b/battery/src/platform/macos/mod.rs @@ -7,13 +7,14 @@ pub use self::device::IoKitDevice; mod iokit; mod device; +mod traits; #[derive(Debug, Default)] pub struct IoKitManager; impl IoKitManager { pub fn iter(&self) -> IoKitIterator { - let inner = match device::IoKitDevice::new() { + let inner = match device::IoKitDevice::new::() { Ok(device) => Some(device), Err(_) => None, }; @@ -24,7 +25,7 @@ impl IoKitManager { impl BatteryManager for IoKitManager { fn refresh(&mut self, battery: &mut Battery) -> io::Result<()> { - let inner = device::IoKitDevice::new()?; + let inner = device::IoKitDevice::new::()?; *battery.get_mut_ref() = inner; Ok(()) @@ -46,3 +47,6 @@ impl iter::Iterator for IoKitIterator { } impl BatteryIterator for IoKitIterator {} + +#[cfg(test)] +mod tests; diff --git a/battery/src/platform/macos/tests.rs b/battery/src/platform/macos/tests.rs new file mode 100644 index 0000000..5b8a2c1 --- /dev/null +++ b/battery/src/platform/macos/tests.rs @@ -0,0 +1,116 @@ +use std::io; +use std::time::Duration; + +use crate::platform::traits::BatteryDevice; +use super::device::IoKitDevice; +use super::traits::DataSource; + +#[derive(Debug, Default)] +struct TestDataSource { + fully_charged: bool, + external_connected: bool, + is_charging: bool, + voltage: u32, + amperage: i32, + design_capacity: u32, + max_capacity: u32, + current_capacity: u32, + temperature: Option, + cycle_count: Option, +} + +impl DataSource for TestDataSource { + fn new() -> io::Result where Self: Sized { + Ok(Default::default()) + } + + fn fully_charged(&self) -> bool { + self.fully_charged + } + + fn external_connected(&self) -> bool { + self.external_connected + } + + fn is_charging(&self) -> bool { + self.is_charging + } + + fn voltage(&self) -> u32 { + self.voltage + } + + fn amperage(&self) -> i32 { + self.amperage + } + + fn design_capacity(&self) -> u32 { + self.design_capacity + } + + fn max_capacity(&self) -> u32 { + self.max_capacity + } + + fn current_capacity(&self) -> u32 { + self.current_capacity + } + + fn temperature(&self) -> Option { + self.temperature + } + + fn cycle_count(&self) -> Option { + self.cycle_count + } + + fn time_remaining(&self) -> Option { + None + } + + fn manufacturer(&self) -> Option { + None + } + + fn device_name(&self) -> Option { + None + } + + fn serial_number(&self) -> Option { + None + } +} + +// Based on the https://github.com/svartalf/rust-battery/pull/10 +#[test] +fn test_energy_multiplication_overflow() { + let data = TestDataSource { + current_capacity: 6232, + max_capacity: 6324, + voltage: 12701, + ..Default::default() + }; + let device: IoKitDevice = data.into(); + + assert!(device.percentage() >= 0.0); + assert!(device.percentage() <= 100.0); +} + +// Based on the https://github.com/svartalf/rust-battery/issues/8 +#[test] +fn test_energy_calculation() { + let data = TestDataSource { + current_capacity: 3938, + design_capacity: 4315, + max_capacity: 4119, + voltage: 12818, + amperage: -1037, + ..Default::default() + }; + let device: IoKitDevice = data.into(); + + assert_eq!(device.energy_rate(), 13292); + assert_eq!(device.energy(), 50477); + assert_eq!(device.energy_full(), 52797); + assert_eq!(device.energy_full_design(), 55309); +} diff --git a/battery/src/platform/macos/traits.rs b/battery/src/platform/macos/traits.rs new file mode 100644 index 0000000..9f1311f --- /dev/null +++ b/battery/src/platform/macos/traits.rs @@ -0,0 +1,58 @@ +use std::io; +use std::fmt::Debug; +use std::time::Duration; + +/// Used for IOPMPowerSource wrapper and for tests. +/// +/// Only keys declared at https://developer.apple.com/documentation/kernel/iopmpowersource?language=objc +/// should be used in this trait and trait implementors, otherwise bugs might happen +/// as in [#11](https://github.com/svartalf/rust-battery/pull/11) +pub trait DataSource: Debug + 'static { + fn new() -> io::Result where Self: Sized; + + /// kIOPMFullyChargedKey + /// + /// Does not seems to be declared in the documentation anymore. + fn fully_charged(&self) -> bool; + + /// kIOPMPSExternalConnectedKey + fn external_connected(&self) -> bool; + + /// kIOPMPSIsChargingKey + fn is_charging(&self) -> bool; + + /// kIOPMPSVoltageKey, mV + fn voltage(&self) -> u32; + + /// kIOPMPSAmperageKey, mA + fn amperage(&self) -> i32; + + /// kIOPMPSDesignCapacityKey, mAh + /// + /// Does not seems to be declared in the documentation anymore. + fn design_capacity(&self) -> u32; + + /// kIOPMPSMaxCapacityKey, mAh + fn max_capacity(&self) -> u32; + + /// kIOPMPSCurrentCapacityKey, mAh + fn current_capacity(&self) -> u32; + + /// kIOPMPSBatteryTemperatureKey + fn temperature(&self) -> Option; + + /// kIOPMPSCycleCountKey + fn cycle_count(&self) -> Option; + + /// kIOPMPSTimeRemainingKey, minutes + fn time_remaining(&self) -> Option; + + /// kIOPMPSManufacturerKey + fn manufacturer(&self) -> Option; + + /// kIOPMPSModelKey + fn device_name(&self) -> Option; + + /// kIOPMPSSerialKey + fn serial_number(&self) -> Option; +} diff --git a/battery/src/platform/traits.rs b/battery/src/platform/traits.rs index e827f93..84e8c30 100644 --- a/battery/src/platform/traits.rs +++ b/battery/src/platform/traits.rs @@ -11,7 +11,10 @@ pub trait BatteryIterator: Iterator + Sized {} pub trait BatteryDevice: Sized { fn capacity(&self) -> f32 { - ((self.energy_full() / self.energy_full_design()) * 100) as f32 + let full = self.energy_full() as f32; + let full_design = self.energy_full_design() as f32; + + (full / full_design) * 100.0 } fn energy(&self) -> u32; diff --git a/battery/src/platform/windows/device.rs b/battery/src/platform/windows/device.rs index e0ce32f..df59b70 100644 --- a/battery/src/platform/windows/device.rs +++ b/battery/src/platform/windows/device.rs @@ -105,7 +105,7 @@ impl BatteryDevice for PowerDevice { } fn percentage(&self) -> f32 { - set_bounds(100 * self.energy() / self.energy_full()) as f32 + set_bounds(100 * (self.energy() / self.energy_full())) as f32 } fn state(&self) -> State { diff --git a/ci/script.sh b/ci/script.sh new file mode 100755 index 0000000..c57f970 --- /dev/null +++ b/ci/script.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -ex + +cross build --target=${TARGET} + +if [ "${TARGET}" = "i686-unknown-freebsd" ] || [ "${TARGET}" = "x86_64-unknown-freebsd" ]; then + echo "'cross test' command is not available for '${TARGET}' target" +else + cross test --target=${TARGET} +fi + +cross clippy -- -D warnings