lamium/util/util.go

234 lines
6.4 KiB
Go
Raw Permalink Normal View History

2024-06-29 20:38:42 +00:00
// 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)
}