Jumplist Database Program in Go

Tag: PROGRAM
Edit
Created: 2019/10/24
Updated: 2019/10/24

Jumplist database

This program consists of two files, main.go, and kv.go, listed below.

It is used to manage the database of jump locations, and is intended to be called by a series of batch scripts

It’s currently done in a naive way, so as to preserve insertion order, and to keep from having to attach to something like SQLite. Even if the jumplist gets up to 100 elements, this code will remain quick, and it preserves order, and avoid a dependency on anything that Go doesn’t ship with.

You’ll want to drop these two files into a folder, run go build -o jumplist.exe on them, and then take jumplist.exe and copy it to a directory on your path.

main.go


package main

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    if len(os.Args) > 1 {
        dispatch()
    } else {
        fmt.Println("Not enough args!")
    }
}

func dispatch() {
    cmd := os.Args[1]
    kv := OpenStore()
    switch cmd {
    case "-add":
        if len(os.Args) < 3 {
            fmt.Println("Need at least one argument to the -add flag")
            return
        }
        wd, err := os.Getwd()
        die(err)
        kv.Add(entry{Key: os.Args[2], Value: wd})
    case "-rm":
        if len(os.Args) < 3 {
            fmt.Println("Need at least one argument to the -rm flag")
            return
        }
        kv.Remove(os.Args[2])
    case "-ls":
        ListDirs(kv)
    default:
        // If it's not one of the above commands, emit the path for the wrapping script to use for jumping.
        if target, found := kv.Get(os.Args[1]); found {
            fmt.Println(target.Value)
        } else {
            fmt.Println(".")
        }
    }
    f, err := os.Create(dbpath())
    defer f.Close()
    die(err)
    die(SaveKVFile(f, kv))

}

func die(e error) {
    if e != nil {
        panic(e)
    }
}

func ListDirs(kv store) {
    maxKeyLen := 0
    for _, entry := range kv {
        if len(entry.Key) > maxKeyLen {
            maxKeyLen = len(entry.Key)
        }
    }
    for _, entry := range kv {
        fmt.Print(RightPad2Len(entry.Key, " ", maxKeyLen))
        fmt.Print(" -> ")
        fmt.Println(entry.Value)
    }
}
// RightPad2Len https://github.com/DaddyOh/golang-samples/blob/master/pad.go
func RightPad2Len(s string, padStr string, overallLen int) string {
    var padCountInt = 1 + ((overallLen - len(padStr)) / len(padStr))
    var retStr = s + strings.Repeat(padStr, padCountInt)
    return retStr[:overallLen]
}

func dbpath() string {
    home, err := os.UserHomeDir()
    die(err)
    return filepath.Join(home, ".jumpdb")
}

func OpenStore() store {
    kv, err := LoadKVFile(dbpath())
    die(err)
    return kv
}

kv.go


package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
)

type store []*entry

type entry struct {
    Key   string
    Value string
}

var ASCII_RECORD_SEPERATOR = []byte{byte(30)}
var ASCII_UNIT_SEPERATOR = []byte{byte(31)}

func (me *store) Get(key string) (*entry, bool) {
    for _, e := range *me {
        if e.Key == key {
            return e, true
        }
    }
    return nil, false
}

func (me *store) Remove(key string) {
    for i, e := range *me {
        if e.Key == key {
            // Delete the lement from the slice?
            copy((*me)[i:], (*me)[i+1:])
            *me = (*me)[:len(*me)-1]
            break
        }
    }
}

func (me *store) Add(e entry) {
    temp := append(*me, &e)
    *me = temp
}

func LoadKVFile(path string) (store, error) {
    data, err := ioutil.ReadFile(path)
    if err != nil {
        return nil, err
    }
    if len(data) == 0 {
        return make(store, 0), nil
    }
    rows := bytes.Split(data, ASCII_RECORD_SEPERATOR)
    kvStore := make(store, 0)
    for _, row := range rows {
        if len(row) == 0 {
            continue
        }
        cols := bytes.Split(row, ASCII_UNIT_SEPERATOR)
        if len(cols) != 2 {
            return nil, fmt.Errorf("Invalid row: %v", row)
        }
        kvStore = append(kvStore, &entry{
            Key:   string(cols[0]),
            Value: string(cols[1]),
        })
    }
    return kvStore, nil
}

func SaveKVFile(result io.Writer, entries store) error {
    for _, row := range entries {
        io.WriteString(result, row.Key)
        result.Write(ASCII_UNIT_SEPERATOR)
        io.WriteString(result, row.Value)
        result.Write(ASCII_RECORD_SEPERATOR)
    }
    return nil

}