Implement own error

This commit is contained in:
Wojciech Kozlowski 2018-12-31 00:03:36 +08:00
parent 5b27106699
commit 341ba8ceec
2 changed files with 83 additions and 63 deletions

View File

@ -13,36 +13,21 @@ extern crate chrono;
extern crate chrono_tz; extern crate chrono_tz;
extern crate libc; extern crate libc;
// std module imports // std imports
use std::io; use std::fs::read_to_string;
use std::path::{Path, PathBuf};
// std type imports // External imports
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
// Other external imports
use chrono::prelude::*; use chrono::prelude::*;
/// Read the contents of the file base/filename and return as a String.
fn read_file(base: &PathBuf, filename: &str) -> io::Result<String> {
let mut file = File::open(base.join(filename))?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
/// Return temperature read from the provided monitor. /// Return temperature read from the provided monitor.
pub fn get_temp(hwmon: &PathBuf) -> Result<String, Box<Error>> { pub fn get_temp(hwmon: &PathBuf) -> Result<String> {
let contents = read_file(hwmon, "temp1_input")?; let val: i64 = read_to_string(hwmon.join("temp1_input"))?.trim().parse()?;
let val: f64 = contents.trim().parse()?; Ok(format!("{:02}°C", val / 1000))
Ok(format!("{:02.0}°C", val / 1000.0))
} }
/// Return the three load average values. /// Return the three load average values.
pub fn get_load_avgs() -> Result<String, Box<Error>> { pub fn get_load_avgs() -> Result<String> {
let mut avgs: [libc::c_double; 3] = [0.0; 3]; let mut avgs: [libc::c_double; 3] = [0.0; 3];
let rc: libc::c_int; let rc: libc::c_int;
@ -51,36 +36,29 @@ pub fn get_load_avgs() -> Result<String, Box<Error>> {
} }
if rc < 0 { if rc < 0 {
return Err(From::from( return Err(StatusError::System(rc));
format!("libc::getloadavg failed with rc {}", rc),
));
} }
Ok(format!("{:.2} {:.2} {:.2}", avgs[0], avgs[1], avgs[2])) Ok(format!("{:.2} {:.2} {:.2}", avgs[0], avgs[1], avgs[2]))
} }
/// Return battery status for the battery at the provided path. /// Return battery status for the battery at the provided path.
pub fn get_batt(batt: &PathBuf) -> Result<String, Box<Error>> { pub fn get_batt(batt: &PathBuf) -> Result<String> {
let contents = read_file(&batt, "present")?; if !read_to_string(batt.join("present"))?.starts_with('1') {
if !contents.starts_with('1') { return Err(StatusError::NotPresent(batt.to_str().unwrap().to_string()));
return Err(From::from("not present"));
} }
let contents = match read_file(&batt, "charge_full_design") { let desired_capacity: u64 = read_to_string(batt.join("charge_full_design"))
Ok(contents) => contents, .or_else(|_| read_to_string(batt.join("energy_full_design")))?
Err(_) => read_file(&batt, "energy_full_design")?, .trim()
}; .parse()?;
let desired_capacity: u64 = contents.trim().parse()?; let remaining_capacity: u64 = read_to_string(batt.join("charge_now"))
.or_else(|_| read_to_string(batt.join("energy_now")))?
.trim()
.parse()?;
let contents = match read_file(&batt, "charge_now") { let status: char = match read_to_string(batt.join("status")) {
Ok(contents) => contents,
Err(_) => read_file(&batt, "energy_now")?,
};
let remaining_capacity: u64 = contents.trim().parse()?;
let status: char = match read_file(&batt, "status") {
Ok(contents) => { Ok(contents) => {
match &contents.trim()[..] { match &contents.trim()[..] {
"Full" => 'F', "Full" => 'F',
@ -97,8 +75,8 @@ pub fn get_batt(batt: &PathBuf) -> Result<String, Box<Error>> {
} }
/// Get the time for the provided time zone. /// Get the time for the provided time zone.
pub fn get_tz_time(tz_name: &str, fmt: &str) -> Result<String, Box<Error>> { pub fn get_tz_time(tz_name: &str, fmt: &str) -> Result<String> {
let tz = tz_name.parse::<chrono_tz::Tz>()?; let tz: chrono_tz::Tz = tz_name.parse().map_err(StatusError::ParseTz)?;
let utc = Utc::now().naive_utc(); let utc = Utc::now().naive_utc();
Ok(format!("{}", tz.from_utc_datetime(&utc).format(fmt))) Ok(format!("{}", tz.from_utc_datetime(&utc).format(fmt)))
} }
@ -152,13 +130,13 @@ impl RwmStatus {
}; };
let dir_filtered = dir.filter(|path_result| match path_result { let dir_filtered = dir.filter(|path_result| match path_result {
&Ok(ref path) => { Ok(path) => {
match path.file_name().to_str() { match path.file_name().to_str() {
Some(entry) => entry.starts_with(prefix), Some(entry) => entry.starts_with(prefix),
None => false, None => false,
} }
} }
&Err(_) => false, Err(_) => false,
}); });
let mut paths: Vec<PathBuf> = dir_filtered let mut paths: Vec<PathBuf> = dir_filtered
@ -180,7 +158,7 @@ impl RwmStatus {
let temp_strs: Vec<String> = self.hw_mons let temp_strs: Vec<String> = self.hw_mons
.iter() .iter()
.map(|hw_mon| get_temp(&hw_mon).unwrap_or(format!(""))) .map(|hw_mon| get_temp(&hw_mon).unwrap_or("".into()))
.collect(); .collect();
Some(temp_strs.join("|")) Some(temp_strs.join("|"))
} }
@ -199,7 +177,7 @@ impl RwmStatus {
let batt_strs: Vec<String> = self.batts let batt_strs: Vec<String> = self.batts
.iter() .iter()
.map(|batt| get_batt(&batt).unwrap_or(format!(""))) .map(|batt| get_batt(&batt).unwrap_or("".into()))
.collect(); .collect();
Some(batt_strs.join("|")) Some(batt_strs.join("|"))
} }
@ -212,7 +190,7 @@ impl RwmStatus {
format!( format!(
"{}:{}", "{}:{}",
tz.label, tz.label,
get_tz_time(&tz.name, "%H:%M").unwrap_or(format!("")) get_tz_time(&tz.name, "%H:%M").unwrap_or("".into())
) )
}) })
.collect(); .collect();
@ -220,3 +198,52 @@ impl RwmStatus {
tz_strs.join(" ") tz_strs.join(" ")
} }
} }
/// Internal `Result` type.
type Result<T> = std::result::Result<T, StatusError>;
/// Error type for `rwmstatus` functions.
#[derive(Debug)]
pub enum StatusError {
Io(std::io::Error),
ParseNum(std::num::ParseIntError),
ParseTz(String),
NotPresent(String),
System(i32),
}
impl std::fmt::Display for StatusError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
StatusError::Io(ioe) => ioe.fmt(f),
StatusError::ParseNum(pie) => pie.fmt(f),
StatusError::ParseTz(s) => write!(f, "{}", s),
StatusError::NotPresent(s) => write!(f, "{} not present", s),
StatusError::System(i) => write!(f, "System call returned {}", i),
}
}
}
impl std::error::Error for StatusError {
fn description(&self) -> &str {
match self {
StatusError::Io(ioe) => ioe.description(),
StatusError::ParseNum(pie) => pie.description(),
StatusError::ParseTz(_) => "Invalid timezone",
StatusError::NotPresent(_) => "Device not present",
StatusError::System(_) => "System call returned error",
}
}
}
impl From<std::io::Error> for StatusError {
fn from(err: std::io::Error) -> Self {
StatusError::Io(err)
}
}
impl From<std::num::ParseIntError> for StatusError {
fn from(err: std::num::ParseIntError) -> Self {
StatusError::ParseNum(err)
}
}

View File

@ -16,18 +16,11 @@ use rwmstatus::*;
// External crates // External crates
extern crate x11; extern crate x11;
// std module imports // std imports
use std::process;
use std::ptr;
use std::thread;
use std::time;
// std type imports
use std::ffi::CString; use std::ffi::CString;
// x11 imports // x11 imports
use x11::xlib::Display; use x11::xlib::{Display, XDefaultRootWindow, XOpenDisplay, XStoreName, XSync};
use x11::xlib::{XDefaultRootWindow, XOpenDisplay, XStoreName, XSync};
// Internal module imports // Internal module imports
mod config; mod config;
@ -36,12 +29,12 @@ fn main() {
let display: *mut Display; let display: *mut Display;
unsafe { unsafe {
display = XOpenDisplay(ptr::null()); display = XOpenDisplay(std::ptr::null());
} }
if display == ptr::null_mut() { if display.is_null() {
eprintln!("rwmstatus: cannot open display."); eprintln!("rwmstatus: cannot open display.");
process::exit(1); std::process::exit(1);
} }
let rwmstatus = RwmStatus::new(config::HW_MON_PATH, config::BATT_PATH, &config::TZS[..]); let rwmstatus = RwmStatus::new(config::HW_MON_PATH, config::BATT_PATH, &config::TZS[..]);
@ -69,6 +62,6 @@ fn main() {
XSync(display, false as i32); XSync(display, false as i32);
} }
thread::sleep(time::Duration::from_secs(60)); std::thread::sleep(std::time::Duration::from_secs(60));
} }
} }