// Copyright © 2014 Steve Francia . // // Licensed under the Simple Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://opensource.org/licenses/Simple-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helpers import ( "bytes" "crypto/md5" "encoding/hex" "errors" "fmt" bp "github.com/spf13/hugo/bufferpool" "github.com/spf13/viper" "io" "net" "path/filepath" "reflect" "strings" ) // Filepath separator defined by os.Separator. const FilePathSeparator = string(filepath.Separator) func FindAvailablePort() (*net.TCPAddr, error) { l, err := net.Listen("tcp", ":0") if err == nil { defer l.Close() addr := l.Addr() if a, ok := addr.(*net.TCPAddr); ok { return a, nil } return nil, fmt.Errorf("Unable to obtain a valid tcp port. %v", addr) } return nil, err } // InStringArray checks if a string is an element of a slice of strings // and returns a boolean value. func InStringArray(arr []string, el string) bool { for _, v := range arr { if v == el { return true } } return false } // GuessType attempts to guess the type of file from a given string. func GuessType(in string) string { switch strings.ToLower(in) { case "md", "markdown", "mdown": return "markdown" case "rst": return "rst" case "html", "htm": return "html" } return "unknown" } // ReaderToBytes takes an io.Reader argument, reads from it // and returns bytes. func ReaderToBytes(lines io.Reader) []byte { b := bp.GetBuffer() defer bp.PutBuffer(b) b.ReadFrom(lines) bc := make([]byte, b.Len(), b.Len()) copy(bc, b.Bytes()) return bc } // ReaderToString is the same as ReaderToBytes, but returns a string. func ReaderToString(lines io.Reader) string { b := bp.GetBuffer() defer bp.PutBuffer(b) b.ReadFrom(lines) return b.String() } // StringToReader does the opposite of ReaderToString. func StringToReader(in string) io.Reader { return strings.NewReader(in) } // BytesToReader does the opposite of ReaderToBytes. func BytesToReader(in []byte) io.Reader { return bytes.NewReader(in) } func ThemeSet() bool { return viper.GetString("theme") != "" } // SliceToLower goes through the source slice and lowers all values. func SliceToLower(s []string) []string { if s == nil { return nil } l := make([]string, len(s)) for i, v := range s { l[i] = strings.ToLower(v) } return l } // Md5String takes a string and returns its MD5 hash. func Md5String(f string) string { h := md5.New() h.Write([]byte(f)) return hex.EncodeToString(h.Sum([]byte{})) } // DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to // determine the type of the two terms. func DoArithmetic(a, b interface{}, op rune) (interface{}, error) { av := reflect.ValueOf(a) bv := reflect.ValueOf(b) var ai, bi int64 var af, bf float64 var au, bu uint64 switch av.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ai = av.Int() switch bv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: bi = bv.Int() case reflect.Float32, reflect.Float64: af = float64(ai) // may overflow ai = 0 bf = bv.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: bu = bv.Uint() if ai >= 0 { au = uint64(ai) ai = 0 } else { bi = int64(bu) // may overflow bu = 0 } default: return nil, errors.New("Can't apply the operator to the values") } case reflect.Float32, reflect.Float64: af = av.Float() switch bv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: bf = float64(bv.Int()) // may overflow case reflect.Float32, reflect.Float64: bf = bv.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: bf = float64(bv.Uint()) // may overflow default: return nil, errors.New("Can't apply the operator to the values") } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: au = av.Uint() switch bv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: bi = bv.Int() if bi >= 0 { bu = uint64(bi) bi = 0 } else { ai = int64(au) // may overflow au = 0 } case reflect.Float32, reflect.Float64: af = float64(au) // may overflow au = 0 bf = bv.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: bu = bv.Uint() default: return nil, errors.New("Can't apply the operator to the values") } case reflect.String: as := av.String() if bv.Kind() == reflect.String && op == '+' { bs := bv.String() return as + bs, nil } else { return nil, errors.New("Can't apply the operator to the values") } default: return nil, errors.New("Can't apply the operator to the values") } switch op { case '+': if ai != 0 || bi != 0 { return ai + bi, nil } else if af != 0 || bf != 0 { return af + bf, nil } else if au != 0 || bu != 0 { return au + bu, nil } else { return 0, nil } case '-': if ai != 0 || bi != 0 { return ai - bi, nil } else if af != 0 || bf != 0 { return af - bf, nil } else if au != 0 || bu != 0 { return au - bu, nil } else { return 0, nil } case '*': if ai != 0 || bi != 0 { return ai * bi, nil } else if af != 0 || bf != 0 { return af * bf, nil } else if au != 0 || bu != 0 { return au * bu, nil } else { return 0, nil } case '/': if bi != 0 { return ai / bi, nil } else if bf != 0 { return af / bf, nil } else if bu != 0 { return au / bu, nil } else { return nil, errors.New("Can't divide the value by 0") } default: return nil, errors.New("There is no such an operation") } }