Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supported COWPATH env #33

Merged
merged 4 commits into from
Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 44 additions & 29 deletions cow.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
type Cow struct {
eyes string
tongue string
typ string
typ *CowFile
thoughts rune
thinking bool
bold bool
Expand All @@ -25,10 +25,14 @@ type Cow struct {
// New returns pointer of Cow struct that made by options
func New(options ...Option) (*Cow, error) {
cow := &Cow{
eyes: "oo",
tongue: " ",
thoughts: '\\',
typ: "cows/default.cow",
eyes: "oo",
tongue: " ",
thoughts: '\\',
typ: &CowFile{
Name: "default",
BasePath: "cows",
LocationType: InBinary,
},
ballonWidth: 40,
}
for _, o := range options {
Expand Down Expand Up @@ -103,13 +107,18 @@ func adjustTo2Chars(s string) string {
return " "
}

func containCows(t string) bool {
for _, cow := range AssetNames() {
if t == cow {
return true
func containCows(target string) (*CowFile, error) {
cowPaths, err := Cows()
if err != nil {
return nil, err
}
for _, cowPath := range cowPaths {
cowfile, ok := cowPath.Lookup(target)
if ok {
return cowfile, nil
}
}
return false
return nil, nil
}

// NotFound is indicated not found the cowfile.
Expand All @@ -126,21 +135,17 @@ func (n *NotFound) Error() string {
// Type specify name of the cowfile
func Type(s string) Option {
if s == "" {
s = "cows/default.cow"
}
if !strings.HasSuffix(s, ".cow") {
s += ".cow"
}
if !strings.HasPrefix(s, "cows/") {
s = "cows/" + s
s = "default"
}
return func(c *Cow) error {
if containCows(s) {
c.typ = s
cowfile, err := containCows(s)
if err != nil {
return err
}
if cowfile != nil {
c.typ = cowfile
return nil
}
s = strings.TrimPrefix(s, "cows/")
s = strings.TrimSuffix(s, ".cow")
return &NotFound{Cowfile: s}
}
}
Expand All @@ -165,20 +170,30 @@ func Thoughts(thoughts rune) Option {

// Random specifies something .cow from cows directory
func Random() Option {
pick := pickCow()
pick, err := pickCow()
return func(c *Cow) error {
if err != nil {
return err
}
c.typ = pick
return nil
}
}

func pickCow() string {
cows := AssetNames()
n := len(cows)
rand.Shuffle(n, func(i, j int) {
cows[i], cows[j] = cows[j], cows[i]
})
return cows[rand.Intn(n)]
func pickCow() (*CowFile, error) {
cowPaths, err := Cows()
if err != nil {
return nil, err
}
cowPath := cowPaths[rand.Intn(len(cowPaths))]

n := len(cowPath.CowFiles)
cowfile := cowPath.CowFiles[rand.Intn(n)]
return &CowFile{
Name: cowfile,
BasePath: cowPath.Name,
LocationType: cowPath.LocationType,
}, nil
}

// Bold enables bold mode
Expand Down
109 changes: 101 additions & 8 deletions cowsay.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package cowsay

import (
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"sort"
"strings"
"time"
Expand All @@ -20,21 +23,111 @@ func Say(phrase string, options ...Option) (string, error) {
return cow.Say(phrase)
}

// LocationType indicates the type of COWPATH.
type LocationType int

const (
// InBinary indicates the COWPATH in binary.
InBinary LocationType = iota

// InDirectory indicates the COWPATH in your directory.
InDirectory
)

// CowPath is information of the COWPATH.
type CowPath struct {
// Name is name of the COWPATH.
// If you specified `COWPATH=/foo/bar`, Name is `/foo/bar`.
Name string
// CowFiles are name of the cowfile which are trimmed ".cow" suffix.
CowFiles []string
// LocationType is the type of COWPATH
LocationType LocationType
}

// Lookup will look for the target cowfile in the specified path.
// If it exists, it returns the cowfile information and true value.
func (c *CowPath) Lookup(target string) (*CowFile, bool) {
for _, cowfile := range c.CowFiles {
if cowfile == target {
return &CowFile{
Name: cowfile,
BasePath: c.Name,
LocationType: c.LocationType,
}, true
}
}
return nil, false
}

// CowFile is information of the cowfile.
type CowFile struct {
// Name is name of the cowfile.
Name string
// BasePath is the path which the cowpath is in.
BasePath string
// LocationType is the type of COWPATH
LocationType LocationType
}

// ReadAll reads the cowfile content.
// If LocationType is InBinary, the file read from binary.
// otherwise reads from file system.
func (c *CowFile) ReadAll() ([]byte, error) {
joinedPath := filepath.Join(c.BasePath, c.Name+".cow")
if c.LocationType == InBinary {
return Asset(joinedPath)
}
return ioutil.ReadFile(joinedPath)
}

// Cows to get list of cows
func Cows() []string {
assets := AssetNames()
cows := make([]string, 0, len(assets))
for _, key := range assets {
cows = append(cows, strings.TrimSuffix(strings.TrimPrefix(key, "cows/"), ".cow"))
func Cows() ([]*CowPath, error) {
cowPaths, err := cowsFromCowPath()
if err != nil {
return nil, err
}
cowPaths = append(cowPaths, &CowPath{
Name: "cows",
CowFiles: CowsInBinary(),
LocationType: InBinary,
})
return cowPaths, nil
}

sort.Strings(cows)
return cows
func cowsFromCowPath() ([]*CowPath, error) {
cowPaths := make([]*CowPath, 0)
cowPath := os.Getenv("COWPATH")
if cowPath == "" {
return cowPaths, nil
}
paths := splitPath(cowPath)
for _, path := range paths {
dirEntries, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
path := &CowPath{
Name: path,
CowFiles: []string{},
LocationType: InDirectory,
}
for _, entry := range dirEntries {
name := entry.Name()
if strings.HasSuffix(name, ".cow") {
name = strings.TrimSuffix(name, ".cow")
path.CowFiles = append(path.CowFiles, name)
}
}
sort.Strings(path.CowFiles)
cowPaths = append(cowPaths, path)
}
return cowPaths, nil
}

// GetCow to get cow's ascii art
func (cow *Cow) GetCow() (string, error) {
src, err := Asset(cow.typ)
src, err := cow.typ.ReadAll()
if err != nil {
return "", err
}
Expand Down
Loading