@ ca876656b16b47f3baa059af47bbdf8ef3a849e8 | history
package cli
import (
"flag"
"fmt"
"io"
"io/fs"
"log"
"math/rand"
"os"
"path"
"path/filepath"
"sort"
"strings"
"text/template"
"nmyk.io/cowsay"
)
var (
list = flag.Bool("l", false, "list available cowfiles")
randomCow = flag.Bool("r", false, "random cowfile")
cowfile = flag.String("f", "default", "choose a cowfile")
noWrap = flag.Bool("n", false, "disable word wrap")
padWidth = flag.Int("W", 40, "width of speech bubble")
borg = flag.Bool("b", false, "borg")
greedy = flag.Bool("g", false, "greedy")
paranoid = flag.Bool("p", false, "paranoid")
stoned = flag.Bool("s", false, "stoned")
tired = flag.Bool("t", false, "tired")
wired = flag.Bool("w", false, "wired")
youthful = flag.Bool("y", false, "youthful")
dead = flag.Bool("d", false, "dead")
eyes = flag.String("e", defaultEyes, "eyes")
tongue = flag.String("T", defaultTongue, "tongue")
)
// Run is the entrypoint for the cowsay CLI, with a boolean switch to enable
// "cowthink mode"
func Run(think bool) {
flag.Usage = usage
flag.Parse()
cowpath := parseCowpath()
if *list {
ls(cowpath)
os.Exit(0)
}
message := slurpInput()
if message == "" {
flag.Usage()
os.Exit(1)
}
cow := getCowBody()
cow.Mood = getMood()
cow.Width = getWidth()
if think {
cow.Think(message)
} else {
cow.Say(message)
}
}
func usage() {
flagSet := flag.CommandLine
intro := `Usage: %s [-bdgpstwy] [-r] [-h] [-e eyes]
[-f cowfile] [-l] [-n] [-T tongue] [-W wrapcolumn] [message]
Options:
`
fmt.Printf(intro, os.Args[0])
order := "lrfnW1bgpstwyd2eT"
for _, name := range order {
if name == '1' {
fmt.Println("Moods:")
continue
} else if name == '2' {
fmt.Println("Customize:")
continue
}
flag := flagSet.Lookup(string(name))
fmt.Printf("-%s ", flag.Name)
if flag.DefValue != "false" {
fmt.Printf(" %s (default: %#v) \n", flag.Usage, flag.DefValue)
} else {
fmt.Printf(" %s\n", flag.Usage)
}
}
}
func parseCowpath() string {
cowpathEnv := os.Getenv("COWPATH")
type cowfilePath struct {
path string
entry fs.DirEntry
}
if cowpathEnv != "" {
cowsay.Cowfiles = nil
tmpl := template.New("")
cowpaths := filepath.SplitList(cowpathEnv)
allEntries := make([]cowfilePath, 0)
for _, cowpath := range cowpaths {
entries, err := os.ReadDir(cowpath)
if err != nil {
continue
}
for _, entry := range entries {
allEntries = append(allEntries, cowfilePath{cowpath, entry})
}
}
for _, e := range allEntries {
if !e.entry.Type().IsRegular() || filepath.Ext(e.entry.Name()) != ".cow" {
continue
}
cowdata, err := os.ReadFile(path.Join(e.path, e.entry.Name()))
if err != nil {
panic(err.Error())
}
t, err := tmpl.New(e.entry.Name()).Parse(string(cowdata))
if err != nil {
continue
}
tmpl = t
}
cowsay.Cowfiles = tmpl
}
return cowpathEnv
}
func ls(cowpath string) {
if cowpath != "" {
fmt.Printf("Cow files in %s:\n", cowpath)
} else {
fmt.Println("Cow files:")
}
if cowsay.Cowfiles == nil {
fmt.Println("none")
return
}
cowfiles := cowsay.Cowfiles.Templates()
names := make([]string, 0)
for _, key := range cowfiles {
path := strings.Split(key.Name(), "/")
basename := path[len(path)-1]
name := strings.Replace(basename, ".cow", "", -1)
names = append(names, name)
}
sort.Strings(names)
fmt.Println(strings.Join(names, " "))
return
}
func slurpInput() (message string) {
fi, _ := os.Stdin.Stat()
if (fi.Mode() & os.ModeCharDevice) == 0 {
m, _ := io.ReadAll(os.Stdin)
message = strings.TrimRight(string(m), "\n")
} else {
args := strings.Join(flag.Args(), " ")
message = args
}
return
}
func getCowBody() cowsay.Cow {
var label string
if *randomCow {
var assets []string
cowfiles := cowsay.Cowfiles.Templates()
for _, key := range cowfiles {
assets = append(assets, key.Name())
}
label = assets[rand.Intn(len(assets))]
} else if *cowfile != "" {
label = *cowfile
}
var cow cowsay.Cow
if label != "" {
var err error
cow, err = cowsay.Load(label)
if err != nil {
log.Fatalf(err.Error())
}
}
return cow
}
const (
defaultEyes = "oo"
defaultTongue = " "
)
func getMood() cowsay.Mood {
var mood cowsay.Mood
switch {
case *borg:
mood = cowsay.Borg
case *greedy:
mood = cowsay.Greedy
case *paranoid:
mood = cowsay.Paranoid
case *stoned:
mood = cowsay.Stoned
case *tired:
mood = cowsay.Tired
case *wired:
mood = cowsay.Wired
case *youthful:
mood = cowsay.Youthful
case *dead:
mood = cowsay.Dead
default:
}
if *eyes != defaultEyes {
mood.Eyes = *eyes
}
if *tongue != defaultTongue {
mood.Tongue = *tongue
}
return mood
}
func getWidth() int {
var width int
if *noWrap {
width = -1
} else {
width = *padWidth
}
return width
}