mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* Config: fix don't create empty dir when resolving path * Config: refactor config file loaders * add a layer of abstraction so that config can be loaded from non-files * use io.Reader / io.Writer abstraction to separate data operations from file operations * remove dryrun option from SaveConfig - now it always saves * rename read and save methods to mention file operations * log error when encryption prompt fails * as the user didn't make a choice, we'd prompt again next time the file is loaded * add file.Writer tests * skip permissions test for windows * defer creating the writer on save to the last moment * this avoids truncating file when there is error with password prompt * add a test * tests with StdIn cannot run in parallel
125 lines
2.7 KiB
Go
125 lines
2.7 KiB
Go
package file
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/csv"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Write writes selected data to a file or returns an error if it fails. This
|
|
// func also ensures that all files are set to this permission (only rw access
|
|
// for the running user and the group the user is a member of)
|
|
func Write(file string, data []byte) error {
|
|
basePath := filepath.Dir(file)
|
|
if !Exists(basePath) {
|
|
if err := os.MkdirAll(basePath, 0770); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return ioutil.WriteFile(file, data, 0770)
|
|
}
|
|
|
|
// Writer creates a writer to a file or returns an error if it fails. This
|
|
// func also ensures that all files are set to this permission (only rw access
|
|
// for the running user and the group the user is a member of)
|
|
func Writer(file string) (*os.File, error) {
|
|
basePath := filepath.Dir(file)
|
|
if !Exists(basePath) {
|
|
if err := os.MkdirAll(basePath, 0770); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0770)
|
|
}
|
|
|
|
// Move moves a file from a source path to a destination path
|
|
// This must be used across the codebase for compatibility with Docker volumes
|
|
// and Golang (fixes Invalid cross-device link when using os.Rename)
|
|
func Move(sourcePath, destPath string) error {
|
|
sourceAbs, err := filepath.Abs(sourcePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
destAbs, err := filepath.Abs(destPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sourceAbs == destAbs {
|
|
return nil
|
|
}
|
|
inputFile, err := os.Open(sourcePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
destDir := filepath.Dir(destPath)
|
|
if !Exists(destDir) {
|
|
err = os.MkdirAll(destDir, 0770)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
outputFile, err := os.Create(destPath)
|
|
if err != nil {
|
|
inputFile.Close()
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(outputFile, inputFile)
|
|
inputFile.Close()
|
|
outputFile.Close()
|
|
if err != nil {
|
|
if errRem := os.Remove(destPath); errRem != nil {
|
|
return fmt.Errorf(
|
|
"unable to os.Remove error: %s after io.Copy error: %s",
|
|
errRem,
|
|
err,
|
|
)
|
|
}
|
|
return err
|
|
}
|
|
|
|
return os.Remove(sourcePath)
|
|
}
|
|
|
|
// Exists returns whether or not a file or path exists
|
|
func Exists(name string) bool {
|
|
_, err := os.Stat(name)
|
|
return !os.IsNotExist(err)
|
|
}
|
|
|
|
// WriteAsCSV takes a table of records and writes it as CSV
|
|
func WriteAsCSV(filename string, records [][]string) error {
|
|
if len(records) == 0 {
|
|
return errors.New("no records in matrix")
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
w := csv.NewWriter(&buf)
|
|
|
|
alignment := len(records[0])
|
|
for i := range records {
|
|
if len(records[i]) != alignment {
|
|
return errors.New("incorrect alignment")
|
|
}
|
|
|
|
err := w.Write(records[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
err := w.Error()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return Write(filename, buf.Bytes())
|
|
}
|