Skip to content

Commit

Permalink
refactor: update structure and improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
Tshaka Eric Lekholoane committed Dec 14, 2021
1 parent ed20c9e commit f940da4
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 269 deletions.
3 changes: 2 additions & 1 deletion .github/ISSUE_TEMPLATE/bug.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ body:
label: Version
description: Which version of bat are you running?
options:
- "0.8.3 (Latest)"
- "0.8.4 (Latest)"
- "0.8.3"
- "0.8.2"
- "0.8.1"
- "0.8"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ There have also been some [problems setting the charging threshold inside of a v

## Installation

Precompiled binaries (Linux x86-64) are available from the [GitHub releases page](https://github.com/tshakalekholoane/bat/releases), the latest of which can be downloaded from [here](https://github.com/tshakalekholoane/bat/releases/download/0.8.3/bat).
Precompiled binaries (Linux x86-64) are available from the [GitHub releases page](https://github.com/tshakalekholoane/bat/releases), the latest of which can be downloaded from [here](https://github.com/tshakalekholoane/bat/releases/download/0.8.4/bat).

After downloading the binary, give it permission to execute on your system by running the following command. For example, assuming the binary is located in the user's Downloads folder:

Expand Down
134 changes: 2 additions & 132 deletions cmd/bat/main.go
Original file line number Diff line number Diff line change
@@ -1,137 +1,7 @@
package main

import (
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"

"github.com/tshakalekholoane/bat/internal/docs"
"github.com/tshakalekholoane/bat/internal/io"
"github.com/tshakalekholoane/bat/internal/persist"
"github.com/tshakalekholoane/bat/internal/threshold"
)

// printFile is a wrapper around `io.FileContents` to simplify printing
// the values of some (battery) virtual files.
func printFile(vf string) {
s, err := io.FileContents(vf)
if err != nil {
if err.Error() == "virtual file not found" {
fmt.Println(
"This program is most likely not compatible with your system." +
" See\nhttps://github.com/tshakalekholoane/bat#disclaimer.")
os.Exit(1)
}
log.Fatal(err)
}
fmt.Println(s)
}
import "tshaka.co/bat/internal/cli"

func main() {
if len(os.Args) == 1 {
docs.Help()
os.Exit(0)
}
switch os.Args[1] {
case "-c", "--capacity":
printFile("capacity")
case "-h", "--help":
err := docs.Help()
if err != nil {
log.Fatal(err)
}
case "-p", "--persist":
err := persist.WriteServices()
if err != nil {
switch {
case err.Error() == "bash not found":
fmt.Println("Requires Bash to persist the charging threshold.")
os.Exit(1)
case err.Error() == "incompatible systemd version":
fmt.Println("Requires systemd version 244-rc1 or later.")
os.Exit(1)
case err.Error() == "virtual file not found":
fmt.Println(
"This program is most likely not compatible with your " +
"system. See\n" +
"https://github.com/tshakalekholoane/bat#disclaimer.")
os.Exit(1)
case strings.HasSuffix(err.Error(), "permission denied"):
fmt.Println("This command requires sudo permissions.")
os.Exit(1)
default:
log.Fatal(err)
}
}
fmt.Println("Persistence of the current charging threshold enabled.")
case "-r", "--reset":
err := persist.RemoveServices()
if err != nil {
if strings.HasSuffix(err.Error(), "permission denied") {
fmt.Println("This command requires sudo permissions.")
os.Exit(1)
}
log.Fatal(err)
}
fmt.Println("Charging threshold persistence reset.")
case "-s", "--status":
printFile("status")
case "-t", "--threshold":
switch {
case len(os.Args) > 3:
fmt.Println("Expects a single argument.")
os.Exit(1)
case len(os.Args) == 3:
t, err := strconv.Atoi(os.Args[2])
if err != nil {
if errors.Is(err, strconv.ErrSyntax) {
fmt.Println("Argument should be an integer.")
os.Exit(1)
}
log.Fatal(err)
}
if t < 1 || t > 100 {
fmt.Println("Number should be between 1 and 100.")
os.Exit(1)
}
err = threshold.Write(t)
if err != nil {
switch {
case err.Error() == "incompatible kernel version":
fmt.Println("Requires Linux kernel version 5.4 or later.")
os.Exit(1)
case err.Error() == "virtual file not found":
fmt.Println(
"This program is most likely not compatible with your" +
" system. See\n" +
"https://github.com/tshakalekholoane/bat#disclaimer.")
os.Exit(1)
case strings.HasSuffix(err.Error(), "permission denied"):
fmt.Println("This command requires sudo permissions.")
os.Exit(1)
default:
log.Fatal(err)
}
}
fmt.Println(
"Charging threshold set.\nUse `sudo bat --persist` to " +
"persist the setting between restarts.")
default:
printFile("charge_control_end_threshold")
}
case "-v", "--version":
err := docs.VersionInfo()
if err != nil {
log.Fatal(err)
}
default:
fmt.Printf(
"There is no %s option. Use `bat --help` to see a list of " +
"available options.\n",
os.Args[1])
os.Exit(1)
}
cli.Run()
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/tshakalekholoane/bat
module tshaka.co/bat

go 1.17
160 changes: 160 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Package cli handles the command line user interface for bat.
package cli

import (
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"syscall"

"tshaka.co/bat/internal/docs"
"tshaka.co/bat/internal/file"
"tshaka.co/bat/internal/persist"
"tshaka.co/bat/internal/threshold"
)

// common error messages
const (
incompat = "This program is most likely not compatible with your system. " +
"See\nhttps://github.com/tshakalekholoane/bat#disclaimer for details."
permissionDenied = "Permission denied. Try running this command using sudo."
)

// errPermissionDenied indicates that the user has insufficient
// permissions to perform an action.
var errPermissionDenied = syscall.EACCES

// context is a helper function that returns an io.Writer and a status
// code the program should exit with after performing an action.
func context(fatal bool) (io.Writer, int) {
var out io.Writer = os.Stdout
var code int
if fatal {
out = os.Stderr
code = 1
}
return out, code
}

// reportf formats according to a format specifier and writes to either
// standard error or standard output depending on the context.
func reportf(fatal bool, format string, a ...interface{}) {
out, code := context(fatal)
fmt.Fprintf(out, format, a...)
os.Exit(code)
}

// reportln formats using the default formats for its operands, appends
// a new line and, writes to standard output.
func reportln(fatal bool, a ...interface{}) {
reportf(fatal, "%v\n", a...)
}

// show prints the value of a /sys/class/power_supply/BAT?/ variable.
func show(v string) {
c, err := file.Contents(v)
if err != nil {
if errors.Is(err, file.ErrNotFound) {
reportln(true, incompat)
}
log.Fatalln(err)
}
reportln(false, strings.TrimSpace(string(c)))
}

// Run executes the application.
func Run() {
if len(os.Args) == 1 {
docs.Usage()
os.Exit(0)
}
switch os.Args[1] {
case "-c", "--capacity":
show("capacity")
case "-h", "--help":
err := docs.Usage()
if err != nil {
log.Fatalln(err)
}
os.Exit(0)
case "-p", "--persist":
err := persist.WriteServices()
if err != nil {
switch {
case errors.Is(err, persist.ErrBashNotFound):
reportln(true, "Cannot find Bash on your system.")
case errors.Is(err, persist.ErrIncompatSystemd):
reportln(true, "Requires systemd version 244-rc1 or later.")
case errors.Is(err, file.ErrNotFound):
reportln(true, incompat)
case errors.Is(err, errPermissionDenied):
reportln(true, permissionDenied)
default:
log.Fatalln(err)
}
}
reportln(false, "Persistence of the current charging threshold enabled.")
case "-r", "--reset":
err := persist.DeleteServices()
if err != nil {
if errors.Is(err, errPermissionDenied) {
reportln(true, permissionDenied)
}
log.Fatal(err)
}
reportln(false, "Charging threshold persistence reset.")
case "-s", "--status":
show("status")
case "-t", "--threshold":
switch {
case len(os.Args) > 3:
reportln(true, "Expects a single argument.")
case len(os.Args) == 3:
t, err := strconv.Atoi(os.Args[2])
if err != nil {
if errors.Is(err, strconv.ErrSyntax) {
reportln(true, "Argument should be an integer.")
}
log.Fatal(err)
}
if t < 1 || t > 100 {
reportln(true, "Number should be between 1 and 100.")
}
err = threshold.Set(t)
if err != nil {
switch {
case errors.Is(err, threshold.ErrIncompatKernel):
reportln(true, "Requires Linux kernel version 5.4 or later.")
case errors.Is(err, file.ErrNotFound):
reportln(true, incompat)
case errors.Is(err, errPermissionDenied):
reportln(true, permissionDenied)
default:
log.Fatal(err)
}
}
reportln(
false,
"Charging threshold set.\nUse `sudo bat --persist` to persist the "+
"setting between restarts.")
default:
show("charge_control_end_threshold")
}
case "-v", "--version":
err := docs.Version()
if err != nil {
log.Fatal(err)
}
os.Exit(0)
default:
reportf(
true,
"There is no %s option. Run `bat --help` to see a list of available "+
"options.\n",
os.Args[1])
}
}
28 changes: 14 additions & 14 deletions internal/docs/docs.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package docs implements functions for displaying documentation.
package docs

import (
Expand All @@ -7,16 +8,17 @@ import (
"strings"
)

//go:embed help.txt
var help string

//go:embed version.txt
var version string
var (
//go:embed help.txt
help string
//go:embed version.txt
version string
)

// page calls the less pager on the `doc` string input. A successful
// call returns err == nil.
// page filters the string doc through the less pager.
func page(doc string) error {
cmd := exec.Command("less", "--IGNORE-CASE", "--quit-if-one-screen")
cmd := exec.Command(
"less", "--no-init", "--quit-if-one-screen", "--IGNORE-CASE")
cmd.Stdin = strings.NewReader(doc)
cmd.Stdout = os.Stdout
err := cmd.Run()
Expand All @@ -26,19 +28,17 @@ func page(doc string) error {
return nil
}

// Help shows the help document through the less pager. A successful
// call returns err == nil.
func Help() error {
// Usage pages the help documentation through less.
func Usage() error {
err := page(help)
if err != nil {
return err
}
return nil
}

// VersionInfo shows version information through the less pager. A
// successful call returns err == nil.
func VersionInfo() error {
// Version pages version information through less.
func Version() error {
err := page(version)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion internal/docs/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ SUPPORT
REFERENCE
https://wiki.archlinux.org/title/Laptop/ASUS#Battery_charge_threshold

10 JUNE 2021
13 DECEMBER 2021
2 changes: 1 addition & 1 deletion internal/docs/version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
bat 0.8.3
bat 0.8.4
Copyright (c) 2021 Tshaka Eric Lekholoane.
MIT Licence.
Loading

0 comments on commit f940da4

Please sign in to comment.