234 lines
6.4 KiB
Go
234 lines
6.4 KiB
Go
// Package util includes various helper functions for common
|
|
// string manipulation and file handling operations.
|
|
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// Error messages.
|
|
err = struct {
|
|
dirNotCreated string
|
|
dirNotOpened string
|
|
dirNotRead string
|
|
fileNotCopied string
|
|
fileNotCreated string
|
|
fileNotLoaded string
|
|
}{
|
|
dirNotCreated: "Error: directory could not be created:",
|
|
dirNotOpened: "Error: directory could not be opened:",
|
|
dirNotRead: "Error: directory not readable:",
|
|
fileNotCopied: "Error: file could not be copied:",
|
|
fileNotCreated: "Error: file could not be created.",
|
|
fileNotLoaded: "Error: file could not be loaded:",
|
|
}
|
|
)
|
|
|
|
// HasError returns true if error e is a non-empty value and prints
|
|
// string msg to standard output, or false otherwise. If exitOnErr is
|
|
// true, sends exit signal code 3 instead of a returning a boolean value.
|
|
func HasError(e error, msg string, exitOnErr ...bool) bool {
|
|
if e != nil {
|
|
fmt.Println(msg)
|
|
if len(exitOnErr) == 1 {
|
|
if exitOnErr[0] {
|
|
os.Exit(3)
|
|
}
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// FileExists returns true a file or directory at path exists, or
|
|
// false otherwise.
|
|
func FileExists(path string) bool {
|
|
if _, fileErr := os.Stat(path); fileErr == nil {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// MinMax returns the smaller and larger value of two numbers,
|
|
// in that order.
|
|
func MinMax(n1, n2 int) (int, int) {
|
|
if n1 < n2 {
|
|
return n1, n2
|
|
} else {
|
|
return n2, n1
|
|
}
|
|
}
|
|
|
|
// MultiReplace returns a copy of string str after replacing values
|
|
// from map of key-value string pairs rep.
|
|
func MultiReplace(str string, rep map[string]string) string {
|
|
s := str
|
|
for k, v := range rep {
|
|
s = strings.ReplaceAll(s, k, v)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// MutliOrdReplace returns a copy of string str after replacing values
|
|
// from rep, a slice of maps containing key-value string pairs. Like
|
|
// MultiReplace for multiple maps.
|
|
func MultiOrdReplace(str string, rep []map[string]string) string {
|
|
s := str
|
|
for _, e := range rep {
|
|
for k, v := range e {
|
|
s = strings.ReplaceAll(s, k, v)
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
// ReplaceExt returns a copy of the string path with its file extension
|
|
// replaced with string ext. If path has no extension, returns a copy of path
|
|
// with ext appended to it.
|
|
func ReplaceExt(path, ext string) string {
|
|
if filepath.Ext(path) != "" {
|
|
return strings.Replace(path, filepath.Ext(path), ext, 1)
|
|
}
|
|
return path + ext
|
|
}
|
|
|
|
// ConvertPath returns a copy of string path with Unix shorthand locations
|
|
// "~/" and "$HOME" expanded to the full absolute path.
|
|
func ConvertPath(path string) string {
|
|
home, homeErr := os.UserHomeDir()
|
|
if homeErr == nil {
|
|
if strings.HasPrefix(path, "~/") {
|
|
return strings.Replace(path, "~/", home+string(os.PathSeparator), 1)
|
|
} else if strings.HasPrefix(path, "~"+filepath.Base(home)) {
|
|
return strings.Replace(path, "~"+filepath.Base(home), home, 1)
|
|
} else if strings.HasPrefix(path, "$HOME") {
|
|
return strings.Replace(path, "$HOME", home, 1)
|
|
}
|
|
}
|
|
return path
|
|
}
|
|
|
|
// GetFileList returns a string slice containing a list of files at path.
|
|
//
|
|
// sortOrder options: modTime, modTimeDesc, name, nameDesc
|
|
//
|
|
// If includeDirs is true, include directory names in the list.
|
|
func GetFileList(path string, sortOrder string, includeDirs bool) []string {
|
|
dir, openErr := os.Open(filepath.FromSlash(path))
|
|
HasError(openErr, err.dirNotOpened+" "+path)
|
|
files, readErr := dir.ReadDir(0)
|
|
HasError(readErr, err.dirNotRead+" "+path)
|
|
|
|
var list []string
|
|
if strings.Contains(sortOrder, "modTime") {
|
|
mapTimes := make(map[string]string)
|
|
var times []string
|
|
for _, file := range files {
|
|
if !file.IsDir() || (file.IsDir() && includeDirs) {
|
|
fi, _ := file.Info()
|
|
mapTimes[fmt.Sprint(fi.ModTime())] = filepath.Join(path,
|
|
fi.Name())
|
|
times = append(times, fmt.Sprint(fi.ModTime()))
|
|
}
|
|
}
|
|
slices.Sort(times)
|
|
for _, t := range times {
|
|
list = append(list, mapTimes[t])
|
|
}
|
|
} else if strings.Contains(sortOrder, "name") {
|
|
for _, file := range files {
|
|
if !file.IsDir() || (file.IsDir() && includeDirs) {
|
|
list = append(list, filepath.Join(path, file.Name()))
|
|
}
|
|
}
|
|
slices.Sort(list)
|
|
}
|
|
|
|
if strings.Contains(sortOrder, "Desc") {
|
|
slices.Reverse(list)
|
|
}
|
|
return list
|
|
}
|
|
|
|
// MakeDir returns true if a new directory is created successfully or already
|
|
// exists at path, or false otherwise. If exitOnErr is true, sends an
|
|
// application exit signal if an error occurs.
|
|
func MakeDir(path string, exitOnErr ...bool) bool {
|
|
toExit := false
|
|
if len(exitOnErr) == 1 {
|
|
if exitOnErr[0] == true {
|
|
toExit = true
|
|
}
|
|
}
|
|
if !HasError(os.MkdirAll(filepath.FromSlash(path), 0755), err.dirNotCreated+
|
|
" "+path, toExit) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// CopyFile returns true if a file is copied successfully from srcPath to
|
|
// destPath, or false otherwise.
|
|
func CopyFile(srcPath, destPath string) bool {
|
|
src, srcErr := os.Open(srcPath)
|
|
HasError(srcErr, err.fileNotLoaded+" "+srcPath)
|
|
defer src.Close()
|
|
|
|
dest, destErr := os.Create(destPath)
|
|
HasError(destErr, err.fileNotCreated)
|
|
defer dest.Close()
|
|
|
|
_, copyErr := io.Copy(dest, src)
|
|
return HasError(copyErr, err.fileNotCopied+" "+destPath)
|
|
}
|
|
|
|
// CopyFiles copies files in srcPath to destPath. If overwrite is true,
|
|
// copying overwrites files of the same name at destPath.
|
|
func CopyFiles(srcPath string, destPath string, overwrite bool) {
|
|
files := GetFileList(srcPath, "name", false)
|
|
for _, file := range files {
|
|
destFile := filepath.Join(destPath, filepath.Base(file))
|
|
if !FileExists(destFile) || (FileExists(destFile) && overwrite) {
|
|
CopyFile(file, destFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
// SaveFile writes the string contents of str to file at path. If exitOnErr is
|
|
// true, sends an application exit signal if an error occurs.
|
|
func SaveFile(path string, str string, exitOnErr ...bool) {
|
|
fh, fileErr := os.Create(filepath.FromSlash(path))
|
|
toExit := false
|
|
if len(exitOnErr) == 1 {
|
|
if exitOnErr[0] == true {
|
|
toExit = true
|
|
}
|
|
}
|
|
if !HasError(fileErr, err.fileNotCreated, toExit) {
|
|
fmt.Fprint(fh, str)
|
|
}
|
|
}
|
|
|
|
// LoadFile returns the contents of path as string. If exitOnErr is true, sends
|
|
// an application exit signal if an error occurs.
|
|
func LoadFile(path string, exitOnErr ...bool) string {
|
|
contents, fileErr := os.ReadFile(filepath.FromSlash(path))
|
|
toExit := false
|
|
if len(exitOnErr) == 1 {
|
|
if exitOnErr[0] == true {
|
|
toExit = true
|
|
}
|
|
}
|
|
HasError(fileErr, err.fileNotLoaded+" "+path, toExit)
|
|
return string(contents)
|
|
}
|