Skip to content

Commit 8531f47

Browse files
Exported utility function packages
There was only the `internal/util` package, but the functions were well suited for task runners that can be implemented by users to extend snowsaw's capability to handle custom tasks. To allow to use the utility functions the package has been moved to the `pkg/util` parent package. Epic GH-33 Resolves GH-66
1 parent 79afc12 commit 8531f47

File tree

4 files changed

+122
-68
lines changed

4 files changed

+122
-68
lines changed

internal/util/filesystem.go

Lines changed: 0 additions & 57 deletions
This file was deleted.

pkg/config/builder/builder.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ import (
1818
"path/filepath"
1919
"reflect"
2020

21+
"github.com/fatih/color"
2122
"github.com/imdario/mergo"
2223

23-
"github.com/arcticicestudio/snowsaw/internal/util"
2424
"github.com/arcticicestudio/snowsaw/pkg/config/encoder"
2525
"github.com/arcticicestudio/snowsaw/pkg/config/source/file"
2626
"github.com/arcticicestudio/snowsaw/pkg/prt"
27+
"github.com/arcticicestudio/snowsaw/pkg/util/filesystem"
2728
)
2829

2930
// builder contains the current configuration building state.
@@ -39,17 +40,17 @@ func Load(files ...*file.File) *builder {
3940

4041
for _, f := range files {
4142
// Convert to absolute path and check if file exists, otherwise ignore and check next.
42-
f.Path, _ = util.AbsPath(f.Path)
43-
if exists, _ := util.FileExists(f.Path); !exists {
44-
prt.Debugf("Ignoring non-existent configuration file: %s", f.Path)
43+
f.Path, _ = filepath.Abs(f.Path)
44+
if exists, _ := filesystem.FileExists(f.Path); !exists {
45+
prt.Debugf("Ignoring non-existent configuration file: %s", color.CyanString(f.Path))
4546
continue
4647
}
4748

4849
// Find matching encoder by file extension if not already set.
4950
if f.Encoder == nil {
5051
fileExt := filepath.Ext(f.Path)
5152
if len(fileExt) <= 1 {
52-
prt.Debugf("Ignoring configuration file without supported extension: %s", f.Path)
53+
prt.Debugf("Ignoring configuration file without supported extension: %s", color.CyanString(f.Path))
5354
continue
5455
}
5556

@@ -73,7 +74,8 @@ func Load(files ...*file.File) *builder {
7374
}
7475

7576
// Into accepts a configuration struct pointer and populates it with the current config state.
76-
func (s *builder) Into(c interface{}) error {
77+
// The order of the files array is maintained when merging the configuration states into the struct is enabled.
78+
func (s *builder) Into(c interface{}, merge bool) error {
7779
base := reflect.New(reflect.TypeOf(c).Elem()).Interface()
7880

7981
for _, f := range s.Files {
@@ -82,15 +84,21 @@ func (s *builder) Into(c interface{}) error {
8284
return err
8385
}
8486

87+
if !merge {
88+
// Decode the file content into the given base configuration state using the assigned encoder...
89+
if encErr := f.Encoder.Decode(content, &c); encErr != nil {
90+
return fmt.Errorf("%s: %v", f.Path, encErr)
91+
}
92+
continue
93+
}
94+
95+
// ...or merge into the given base configuration state.
8596
raw := base
86-
// Decode the read content using the assigned encoder.
8797
if encErr := f.Encoder.Decode(content, &raw); encErr != nil {
88-
return fmt.Errorf("%s\n%v", f.Path, err)
98+
return fmt.Errorf("%s: %v", f.Path, encErr)
8999
}
90-
91-
// Merge decoded data into given base configuration state.
92100
if encErr := mergo.Merge(c, raw, mergo.WithAppendSlice, mergo.WithOverride); encErr != nil {
93-
return fmt.Errorf("%s\n%v", f.Path, err)
101+
return fmt.Errorf("%s: %v", f.Path, encErr)
94102
}
95103
}
96104

File renamed without changes.

pkg/util/filesystem/filesystem.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (C) 2017-present Arctic Ice Studio <[email protected]>
2+
// Copyright (C) 2017-present Sven Greb <[email protected]>
3+
//
4+
// Project: snowsaw
5+
// Repository: https://github.com/arcticicestudio/snowsaw
6+
// License: MIT
7+
8+
// Author: Arctic Ice Studio <[email protected]>
9+
// Author: Sven Greb <[email protected]>
10+
// Since: 0.4.0
11+
12+
// Package filesystem provides utility functions related to filesystem actions.
13+
package filesystem
14+
15+
import (
16+
"fmt"
17+
"os"
18+
19+
"github.com/mitchellh/go-homedir"
20+
)
21+
22+
// DirExists checks if the directory at the path exists and is not a file.
23+
// Returns true if the given path exists and is a directory, false otherwise.
24+
// If an error occurs, false is returned along with the error.
25+
func DirExists(path string) (bool, error) {
26+
info, err := os.Stat(path)
27+
if err == nil && info.IsDir() {
28+
return true, nil
29+
}
30+
if os.IsNotExist(err) {
31+
return false, nil
32+
}
33+
if !info.IsDir() {
34+
return false, fmt.Errorf("%s is a file", path)
35+
}
36+
37+
return false, err
38+
}
39+
40+
// ExpandPath expands environment variables and special elements like the tilde character for the given path.
41+
// If an error occurs the original passed path is returned along with the corresponding error.
42+
func ExpandPath(path string) (string, error) {
43+
// Handle special case for Unix tilde character expansion.
44+
expandedUserHomePath, err := homedir.Expand(path)
45+
if err != nil {
46+
return path, err
47+
}
48+
49+
return os.ExpandEnv(expandedUserHomePath), nil
50+
}
51+
52+
// FileExists checks if the file at the given path exists and is not a directory.
53+
// If an error occurs, false is returned along with the corresponding error.
54+
func FileExists(path string) (bool, error) {
55+
info, err := os.Stat(path)
56+
if err == nil && !info.IsDir() {
57+
return true, nil
58+
}
59+
if os.IsNotExist(err) {
60+
return false, nil
61+
}
62+
if info.IsDir() {
63+
return false, fmt.Errorf("%s is a directory", path)
64+
}
65+
66+
return false, err
67+
}
68+
69+
// IsFileWritable checks if the file at the given path is writable.
70+
// If an error occurs, false is returned along with the corresponding error.
71+
func IsFileWritable(path string) (bool, error) {
72+
_, err := os.OpenFile(path, os.O_WRONLY, 0660)
73+
if err != nil {
74+
return false, err
75+
}
76+
77+
return true, nil
78+
}
79+
80+
// IsSymlink checks if the specified path is a symbolic link.
81+
// If an error occurs, false is returned along with the corresponding error.
82+
func IsSymlink(path string) (bool, error) {
83+
fi, err := os.Lstat(path)
84+
if err != nil {
85+
return false, err
86+
}
87+
88+
return fi.Mode()&os.ModeSymlink == os.ModeSymlink, nil
89+
}
90+
91+
// NodeExists checks if the node at the given path exists.
92+
// If an error occurs, false is returned along with the corresponding error.
93+
func NodeExists(path string) (bool, error) {
94+
_, err := os.Stat(path)
95+
if err == nil {
96+
return true, nil
97+
}
98+
if os.IsNotExist(err) {
99+
return false, nil
100+
}
101+
102+
return false, err
103+
}

0 commit comments

Comments
 (0)