summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authorVictor Häggqvist <[email protected]>2022-09-14 23:47:31 +0200
committerVictor Häggqvist <[email protected]>2022-09-14 23:47:31 +0200
commit4e0fb944f18411381366e620ef5193f886957b6f (patch)
treefe64f7b10af9b9e7ad25d96a3a386a3079d84680 /main.go
parentca4f546cc3189d2635bd42d74be4332dd35761fe (diff)
rename main.go
Diffstat (limited to 'main.go')
-rw-r--r--main.go264
1 files changed, 264 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..5ccfd0b
--- /dev/null
+++ b/main.go
@@ -0,0 +1,264 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/user"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+
+ "github.com/gocarina/gocsv"
+ flag "github.com/spf13/pflag"
+ "gopkg.in/yaml.v2"
+)
+
+var version = "0.0.0" // injected at build time
+
+type Runnable struct {
+ Cmd string `csv:"name"`
+ Count int `csv:"count"`
+}
+
+type Config struct {
+ DmenuParams string `yaml:"dmenu_params"`
+ Ignore []string `yaml:"ignore,flow"`
+}
+
+type CmdList []*Runnable
+
+func (c CmdList) Len() int {
+ return len(c)
+}
+
+func (c CmdList) Swap(i, j int) {
+ c[i], c[j] = c[j], c[i]
+}
+
+func (c CmdList) Less(i, j int) bool {
+ return c[i].Count > c[j].Count
+}
+
+var (
+ prePhase bool
+ postPhase bool
+ launcher bool
+ stats bool
+ gc bool
+ dbFilePath string
+ config Config
+)
+
+func isInIgnoreList(item string) bool {
+ for _, i := range config.Ignore {
+ if i == item {
+ return true
+ }
+ }
+ return false
+}
+
+func addIfNotContains(items []string, item string) []string {
+ for _, i := range items {
+ if i == item {
+ return items
+ }
+ }
+ return append(items, item)
+}
+
+func openDB() map[string]int {
+ if _, err := os.Stat(dbFilePath); err != nil {
+ return make(map[string]int)
+ }
+
+ list := CmdList{}
+ file, _ := os.Open(dbFilePath)
+ if err := gocsv.UnmarshalFile(file, &list); err != nil {
+ panic(err)
+ }
+
+ itemSet := make(map[string]int)
+ for _, item := range list {
+ itemSet[item.Cmd] = item.Count
+ }
+ return itemSet
+}
+
+func generatePath() []string {
+ sysPath := os.Getenv("PATH")
+ paths := strings.Split(sysPath, ":")
+
+ pathItems := sort.StringSlice{}
+
+ for _, p := range paths {
+ if _, err := os.Stat(p); err == nil {
+ files, _ := ioutil.ReadDir(p)
+ for _, f := range files {
+ if !isInIgnoreList(f.Name()) {
+ pathItems = addIfNotContains(pathItems, f.Name())
+ }
+ }
+ }
+ }
+
+ sort.Sort(pathItems)
+ return pathItems
+}
+
+func rankPath(pathItems []string) CmdList {
+ itemSet := openDB()
+
+ rankedItems := CmdList{}
+ for _, item := range pathItems {
+ count, exists := itemSet[item]
+ if !exists {
+ count = 0
+ }
+ rankedItems = append(rankedItems, &Runnable{item, count})
+ }
+
+ sort.Sort(rankedItems)
+ return rankedItems
+}
+
+func loadIni() {
+ usr, _ := user.Current()
+ configPath := filepath.Join(usr.HomeDir, ".goboom")
+ iniFile := filepath.Join(configPath, "config.yaml")
+ dbFilePath = filepath.Join(configPath, "rankdb.csv")
+
+ if _, err := os.Stat(configPath); err != nil {
+ err := os.Mkdir(configPath, os.ModePerm)
+ if err != nil {
+ fmt.Println("failed to create config dir")
+ }
+ }
+
+ config = Config{
+ DmenuParams: "-b -i -nb black -nf orange -sb black -p \">\"",
+ Ignore: []string{"X", "su"},
+ }
+
+ if _, err := os.Stat(iniFile); err != nil {
+ buff, err := yaml.Marshal(config)
+ if err != nil {
+ panic(err)
+ }
+ if err := ioutil.WriteFile(iniFile, buff, 0644); err != nil {
+ panic(err)
+ }
+ } else {
+ buff, err := ioutil.ReadFile(iniFile)
+ if err != nil {
+ panic(err)
+ }
+ if err := yaml.Unmarshal(buff, &config); err != nil {
+ panic(err)
+ }
+ }
+}
+
+func updateRank(runnable string) {
+ itemSet := openDB()
+
+ count, exists := itemSet[runnable]
+ if !exists {
+ itemSet[runnable] = 1
+ } else {
+ itemSet[runnable] = count + 1
+ }
+
+ writeDB(itemSet)
+}
+
+func writeDB(itemSet map[string]int) {
+ items := CmdList{}
+ for runnable, count := range itemSet {
+ items = append(items, &Runnable{runnable, count})
+ }
+
+ file, err := os.OpenFile(dbFilePath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
+ if err != nil {
+ panic(err)
+ }
+ if err = gocsv.MarshalFile(&items, file); err != nil {
+ panic(err)
+ }
+}
+
+func displayStats() {
+ items := openDB()
+
+ sortedList := CmdList{}
+ for runnable, count := range items {
+ sortedList = append(sortedList, &Runnable{runnable, count})
+ }
+
+ sort.Sort(sortedList)
+
+ fmt.Println("Name\tLaunch count")
+ for _, i := range sortedList {
+ fmt.Printf("%s\t%d\n", i.Cmd, i.Count)
+ }
+}
+
+func runGC() int {
+ items := openDB()
+
+ cleanItems := make(map[string]int)
+ removedCount := 0
+
+ for runnable, count := range items {
+ if count > 1 {
+ cleanItems[runnable] = count
+ } else {
+ removedCount = removedCount + 1
+ }
+ }
+
+ writeDB(cleanItems)
+ return removedCount
+}
+
+func main() {
+ flag.BoolVar(&prePhase, "pre", false, "Generate dmenu in")
+ flag.BoolVar(&launcher, "launcher", false, "Output launcher command")
+ flag.BoolVar(&postPhase, "post", false, "Update ranking DB")
+ flag.BoolVar(&stats, "stats", false, "View DB stats")
+ flag.BoolVar(&gc, "gc", false, "Run garbage collection of the DB")
+ flag.Usage = func() {
+ fmt.Println(version + " " + runtime.Version())
+ fmt.Print("\nTo actually use goboom execute goboom_run\n\n")
+ fmt.Println("Options: ")
+ flag.PrintDefaults()
+ }
+ flag.Parse()
+
+ loadIni()
+
+ if prePhase {
+ pathList := generatePath()
+ sortedList := rankPath(pathList)
+ for _, item := range sortedList {
+ fmt.Println(item.Cmd)
+ }
+ } else if postPhase {
+ scanner := bufio.NewScanner(os.Stdin)
+ scanner.Scan()
+ input := scanner.Text()
+ updateRank(input)
+ fmt.Print(input)
+ } else if launcher {
+ fmt.Print("dmenu " + config.DmenuParams)
+ } else if stats {
+ displayStats()
+ } else if gc {
+ fmt.Printf("Removed %d items\n", runGC())
+ } else {
+ flag.Usage()
+ }
+}