site/vendor/github.com/jtolds/qod/qod.go

203 lines
5.0 KiB
Go

// Copyright (C) 2017 JT Olds
// See LICENSE for copying information.
// Package qod should NOT be used in a serious software engineering
// environment. qod stands for Quick and Dirty bahaha I just realized I got the
// acronym wrong. It's fine. It's on brand. Quick AND Dirty.
//
// The context is I noticed that Go is my favorite language, but when a task
// gets too complicated for a shell pipeline or awk or something, I turn to
// Python. Why not Go?
//
// In Python, I'd frequently write something like:
//
// for line in sys.stdin:
// vals = map(int, line.split())
//
// Here that is in Go:
//
// package main
//
// import (
// "bufio"
// "fmt"
// "os"
// "strconv"
// "strings"
// )
//
// func main() {
// scanner := bufio.NewScanner(os.Stdin)
// for scanner.Scan() {
// var vals []int64
// for _, str := range strings.Fields(scanner.Text()) {
// val, err := strconv.ParseInt(str, 10, 64)
// if err != nil {
// panic(err)
// }
// vals = append(vals, val)
// }
// }
// if err := scanner.Err(); err != nil {
// panic(err)
// }
// }
//
// Ugh! Considering I don't care about this throwaway shell pipeline
// replacement, I'm clearly fine with it blowing up if something's wrong, and
// wow this was too much.
//
// Package qod allows me to write the same type of thing in Go. Here is a
// reimplementation of the Python code above using qod:
//
// package main
//
// import (
// "os"
// "strings"
//
// "github.com/jtolds/qod"
// )
//
// func main() {
// for line := range qod.Lines(os.Stdin) {
// vals := qod.Int64Slice(strings.Fields(line))
// }
// }
//
// Better! I'm more likely to use Go now for little scripts!
//
// Reminder: don't use this for anything real. Most of the stuff in here
// panics at the sight of any errors. That's obviously Bad and Wrong and you
// should actually handle your errors. Set up your build system's linter to
// reject anything that imports github.com/jtolds/qod please. If you have a
// build system for what you're doing at all this isn't for you. If you have
// some one-off tab-delimited data you need to process real quick like I seem
// to ALL THE TIME then okay.
package qod
import (
"bufio"
"fmt"
"io"
"os"
"sort"
"strconv"
"strings"
"unicode"
)
// ANE stands for Assert No Error. It panics if err != nil.
func ANE(err error) {
if err != nil {
panic(err)
}
}
// AFH stands for Assert File Handle. It asserts there was no error and
// passes the file handle on through. Usage like:
//
// fh := qod.AFH(os.Open(path))
func AFH(f *os.File, err error) *os.File {
ANE(err)
return f
}
// AI stands for Assert Int. It asserts there was no error and
// passes the int on through. Usage like:
//
// qod.AI(fmt.Println("a line"))
func AI(i int, err error) int {
ANE(err)
return i
}
// Lines makes reading lines easier. Usage like:
//
// for line := range Lines(os.Stdin) {
// // do something with the line
// }
//
// Returned lines will be right-stripped of whitespace.
// If you care about the lifetime of the channel that you're reading from and
// don't want it to leak, you probably shouldn't be using this package at all.
func Lines(r io.Reader) <-chan string {
ch := make(chan string)
go func() {
defer close(ch)
br := bufio.NewReader(r)
for {
l, err := br.ReadString('\n')
if err == io.EOF {
if l != "" {
ch <- strings.TrimRightFunc(l, unicode.IsSpace)
}
break
}
ANE(err)
ch <- strings.TrimRightFunc(l, unicode.IsSpace)
}
}()
return ch
}
// Float64 converts a string to a float64
func Float64(val string) float64 {
casted, err := strconv.ParseFloat(val, 64)
ANE(err)
return casted
}
// Float64Slice converts a []string to a []float64
func Float64Slice(vals []string) (rv []float64) {
rv = make([]float64, 0, len(vals))
for _, val := range vals {
rv = append(rv, Float64(val))
}
return rv
}
// Int64 converts a string to an int64
func Int64(val string) int64 {
casted, err := strconv.ParseInt(val, 10, 64)
ANE(err)
return casted
}
// Int64Slice converts a []string to an []int64
func Int64Slice(vals []string) (rv []int64) {
rv = make([]int64, 0, len(vals))
for _, val := range vals {
rv = append(rv, Int64(val))
}
return rv
}
// Printlnf is just cause I constantly use Println, then turn it into Printf,
// then get frustrated I forgot the newline.
func Printlnf(format string, vals ...interface{}) {
AI(fmt.Printf(format+"\n", vals...))
}
// Bytes will take an integer amount of bytes and format it with units.
func Bytes(amount int64) string {
val := float64(amount)
moves := 0
for val >= 1024 {
val /= 1024
moves += 1
}
return fmt.Sprintf("%0.02f %s", val, []string{
"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}[moves])
}
// SortedKeysBool returns the keys of a map[string]bool in sorted order.
func SortedKeysBool(v map[string]bool) []string {
rv := make([]string, 0, len(v))
for key := range v {
rv = append(rv, key)
}
sort.Strings(rv)
return rv
}