~loshz/chargectl

d2d48be6e95db67ab9186cbad02f2f7f1e387fd5 — Dan Bond 4 months ago 71c34d2
update deps

Signed-off-by: Dan Bond <danbond@protonmail.com>
5 files changed, 49 insertions(+), 50 deletions(-)

M Cargo.lock
M src/cli.rs
M src/error.rs
M src/main.rs
M src/sysfs.rs
M Cargo.lock => Cargo.lock +16 -16
@@ 4,9 4,9 @@ version = 3

[[package]]
name = "anstyle"
version = "1.0.4"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"

[[package]]
name = "chargectl"


@@ 17,9 17,9 @@ dependencies = [

[[package]]
name = "clap"
version = "4.5.4"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [
 "clap_builder",
 "clap_derive",


@@ 27,9 27,9 @@ dependencies = [

[[package]]
name = "clap_builder"
version = "4.5.2"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [
 "anstyle",
 "clap_lex",


@@ 37,9 37,9 @@ dependencies = [

[[package]]
name = "clap_derive"
version = "4.5.4"
version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [
 "heck",
 "proc-macro2",


@@ 49,9 49,9 @@ dependencies = [

[[package]]
name = "clap_lex"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"

[[package]]
name = "heck"


@@ 61,27 61,27 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"

[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "syn"
version = "2.0.38"
version = "2.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
dependencies = [
 "proc-macro2",
 "quote",

M src/cli.rs => src/cli.rs +2 -2
@@ 2,7 2,7 @@ use std::ffi::OsString;

use clap::{Args, Parser, Subcommand};

use crate::error::Error;
use crate::error::ChargeError;
use crate::sysfs;

#[derive(Parser)]


@@ 45,7 45,7 @@ struct Battery {
}

impl Chargectl {
    pub fn run(self) -> Result<(), Error> {
    pub fn run(self) -> Result<(), ChargeError> {
        match self.command {
            Commands::Full(args) => {
                sysfs::is_ac_power_online()?;

M src/error.rs => src/error.rs +9 -9
@@ 6,7 6,7 @@ use crate::sysfs;

// Wrapped operation errors.
#[derive(Debug)]
pub enum Error {
pub enum ChargeError {
    AC,
    Battery(OsString),
    IO(std::io::Error),


@@ 14,12 14,12 @@ pub enum Error {
    Threshold,
}

impl fmt::Display for Error {
impl fmt::Display for ChargeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let description: String = match self {
            Error::AC => "AC power is not connected".to_string(),
            Error::Battery(bat) => format!("battery not found: {:?}", bat),
            Error::IO(err) => {
            ChargeError::AC => "AC power is not connected".to_string(),
            ChargeError::Battery(bat) => format!("battery not found: {:?}", bat),
            ChargeError::IO(err) => {
                match err.kind() {
                    // Usually fixed by running sudo.
                    ErrorKind::PermissionDenied => {


@@ 30,7 30,7 @@ impl fmt::Display for Error {
                    // _should_ exist.
                    ErrorKind::NotFound => {
                        format!(
                            "battery thresholds not found - do they exist? `{:?}`",
                            "battery thresholds not found {:?}",
                            sysfs::CLASS_POWER_SUPPLY
                        )
                    }


@@ 38,8 38,8 @@ impl fmt::Display for Error {
                    _ => format!("failed to write charge threshold: {err}"),
                }
            }
            Error::Unsupported => "unsupported platform".to_string(),
            Error::Threshold => {
            ChargeError::Unsupported => "unsupported platform".to_string(),
            ChargeError::Threshold => {
                "thresholds must be numerical [1-100], and start < stop".to_string()
            }
        };


@@ 47,4 47,4 @@ impl fmt::Display for Error {
    }
}

impl std::error::Error for Error {}
impl std::error::Error for ChargeError {}

M src/main.rs => src/main.rs +5 -6
@@ 1,4 1,4 @@
use std::process;
use std::process::ExitCode;

use clap::Parser;



@@ 6,11 6,10 @@ mod cli;
mod error;
mod sysfs;

fn main() {
    let cli = cli::Chargectl::parse();

    if let Err(e) = cli.run() {
fn main() -> ExitCode {
    if let Err(e) = cli::Chargectl::parse().run() {
        eprintln!("Error: {e}");
        process::exit(1);
        return ExitCode::FAILURE;
    }
    ExitCode::SUCCESS
}

M src/sysfs.rs => src/sysfs.rs +17 -17
@@ 3,7 3,7 @@ use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use crate::error::Error;
use crate::error::ChargeError;

// Class used to represent power supply in sysfs.
// REF: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power


@@ 18,27 18,27 @@ const DEFAULT_BATTERY: &str = "BAT0";

// General check to determine if the current OS is supported.
// TODO: could this be better?
pub fn is_platform_supported() -> Result<(), Error> {
pub fn is_platform_supported() -> Result<(), ChargeError> {
    if !Path::new(CLASS_POWER_SUPPLY).exists() {
        return Err(Error::Unsupported);
        return Err(ChargeError::Unsupported);
    }

    Ok(())
}

// Check sysfs to see if AC power is online.
pub fn is_ac_power_online() -> Result<(), Error> {
pub fn is_ac_power_online() -> Result<(), ChargeError> {
    let sysfs_ac = Path::new(CLASS_POWER_SUPPLY).join("AC/online");
    let online = read_threshold(sysfs_ac)?;
    if online == 0 {
        return Err(Error::AC);
        return Err(ChargeError::AC);
    }

    Ok(())
}

// Construct a sysfs path for a given battery.
pub fn get_battery_path(battery: Option<OsString>) -> Result<PathBuf, Error> {
pub fn get_battery_path(battery: Option<OsString>) -> Result<PathBuf, ChargeError> {
    // Set battery default if not specified.
    let bat: OsString = match battery {
        Some(b) => b.to_ascii_uppercase(),


@@ 47,14 47,14 @@ pub fn get_battery_path(battery: Option<OsString>) -> Result<PathBuf, Error> {

    let sysfs_bat = Path::new(CLASS_POWER_SUPPLY).join(bat.clone());
    if !sysfs_bat.exists() {
        return Err(Error::Battery(bat));
        return Err(ChargeError::Battery(bat));
    }

    Ok(sysfs_bat)
}

// Sets the start and stop battery charge thresholds in sysfs.
pub fn set_thresholds(start: u8, stop: u8, battery: Option<OsString>) -> Result<(), Error> {
pub fn set_thresholds(start: u8, stop: u8, battery: Option<OsString>) -> Result<(), ChargeError> {
    // Generic check for platform support, AC power and valid thresholds.
    is_platform_supported()?;
    validate_thresholds(start, stop)?;


@@ 83,7 83,7 @@ pub fn set_thresholds(start: u8, stop: u8, battery: Option<OsString>) -> Result<
}

// Gets the start and stop battery charge thresholds from sysfs.
pub fn get_thresholds(battery: Option<OsString>) -> Result<(), Error> {
pub fn get_thresholds(battery: Option<OsString>) -> Result<(), ChargeError> {
    // Generic check for platform support and valid thresholds.
    is_platform_supported()?;



@@ 103,43 103,43 @@ pub fn get_thresholds(battery: Option<OsString>) -> Result<(), Error> {
}

// General validation to check start and stop charge thresholds.
pub fn validate_thresholds(start: u8, stop: u8) -> Result<(), Error> {
pub fn validate_thresholds(start: u8, stop: u8) -> Result<(), ChargeError> {
    // Simple sanity check for valid threshold values.
    // The kernel will also enforce these values, but it's a simple check for us to do.
    if start == 0 || stop == 0 || start > 100 || stop > 100 || start >= stop {
        return Err(Error::Threshold);
        return Err(ChargeError::Threshold);
    }

    Ok(())
}

// Attempts to write a charge threshold value.
pub fn write_threshold(path: PathBuf, threshold: u8) -> Result<(), Error> {
pub fn write_threshold(path: PathBuf, threshold: u8) -> Result<(), ChargeError> {
    // Attempt to open the file in write mode while truncating any existing data.
    // This will fail if the file does not already exist.
    let mut f = OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(path)
        .map_err(Error::IO)?;
        .map_err(ChargeError::IO)?;

    // Attempt to write the charge threshold.
    f.write_all(threshold.to_string().as_bytes())
        .map_err(Error::IO)?;
        .map_err(ChargeError::IO)?;
    Ok(())
}

// Attempts to read a charge threshold value.
pub fn read_threshold(path: PathBuf) -> Result<u8, Error> {
pub fn read_threshold(path: PathBuf) -> Result<u8, ChargeError> {
    let mut f = OpenOptions::new()
        .write(false)
        .read(true)
        .open(path)
        .map_err(Error::IO)?;
        .map_err(ChargeError::IO)?;

    // Read threshold into buffer and strip newlines.
    let mut buf = String::new();
    f.read_to_string(&mut buf).map_err(Error::IO)?;
    f.read_to_string(&mut buf).map_err(ChargeError::IO)?;

    // Attempt to parse threshold value.
    // If the OS returns an unparsable value, we should treat this as fatal.