-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsnowblock.go
140 lines (119 loc) · 4.9 KB
/
snowblock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (C) 2017-present Arctic Ice Studio <development@arcticicestudio.com>
// Copyright (C) 2017-present Sven Greb <development@svengreb.de>
//
// Project: snowsaw
// Repository: https://github.com/arcticicestudio/snowsaw
// License: MIT
// Author: Arctic Ice Studio <development@arcticicestudio.com>
// Author: Sven Greb <development@svengreb.de>
// Since: 0.4.0
// Package snowblock provides the implementation of the snowblock API.
package snowblock
import (
"fmt"
"io/ioutil"
"path/filepath"
"github.com/fatih/color"
api "github.com/arcticicestudio/snowsaw/pkg/api/snowblock"
"github.com/arcticicestudio/snowsaw/pkg/config/encoder"
"github.com/arcticicestudio/snowsaw/pkg/config/encoder/json"
"github.com/arcticicestudio/snowsaw/pkg/config/source/file"
"github.com/arcticicestudio/snowsaw/pkg/prt"
"github.com/arcticicestudio/snowsaw/pkg/util/filesystem"
)
// Configuration i
type Configuration []map[string]interface{}
// Snowblock represents the state and actions of a snowblock.
// It implements the snowblock API interface.
type Snowblock struct {
// IsValid indicates if this snowblock is valid.
IsValid bool
// Path is the path of this snowblock.
Path string
// TaskObjects is a list of task objects this snowblock configuration consists of.
TaskObjects []api.Task
// TaskRunnerMapping contains the assignments from task objects to a matching task runner.
TaskRunnerMapping map[api.TaskRunner]api.TaskConfiguration
// UnsupportedTasks is a list of task names that are not supported by an registered task runner.
UnsupportedTasks []api.TaskConfiguration
}
// NewSnowblock returns a new snowblock.
func NewSnowblock(path string) *Snowblock {
return &Snowblock{
Path: path,
TaskObjects: make([]api.Task, 0),
TaskRunnerMapping: make(map[api.TaskRunner]api.TaskConfiguration),
}
}
// Dispatch handles the processing of the snowblock by dispatching the configured tasks to a registered runner that can
// handle it.
func (s *Snowblock) Dispatch() error {
for runner, instructions := range s.TaskRunnerMapping {
if err := runner.Run(instructions, s.Path); err != nil {
return err
}
}
return nil
}
// Validate ensures the snowblock and the configuration file exist.
// It returns false along with an corresponding error if the snowblock path doesn't exist or is not a directory.
// The snowblock is also not valid either when the given directory does not contain a configuration file or the file
// is not parsable.
// Defined tasks that are not supported by any of the given task runners are filtered into the separate array.
func (s *Snowblock) Validate(taskRunner map[string]api.TaskRunner) error {
// Expand the given snowblock path and convert it into an absolute path.
expandedPath, expPathErr := filesystem.ExpandPath(s.Path)
if expPathErr != nil {
return fmt.Errorf("could not dissolve path: %v", expPathErr)
}
expandedAbsPath, expAbsPathErr := filepath.Abs(expandedPath)
if expAbsPathErr != nil {
return fmt.Errorf("could not convert into absolute path: %v", expAbsPathErr)
}
// Ensure the path is a directory and exists.
dirExists, dirExistsErr := filesystem.DirExists(expandedAbsPath)
if dirExistsErr != nil {
return dirExistsErr
}
if !dirExists {
return fmt.Errorf("no such directory: %s", expandedAbsPath)
}
s.Path = expandedAbsPath
// Try to read and encode the task objects when the directory contains a configuration file.
configFilePath := filepath.Join(s.Path, fmt.Sprintf("%s.%s", api.ConfigurationFileName, encoder.ExtensionsJson))
if configLoadErr := loadConfigFile(configFilePath, &s.TaskObjects); configLoadErr != nil {
prt.Debugf("Ignoring snowblock directory %s without valid configuration file: %s: %v",
color.CyanString(filepath.Base(s.Path)), color.BlueString(configFilePath), configLoadErr)
return nil
}
// Assign each task object to a registered task runner when matching, otherwise add to list of unsupported tasks.
for _, taskObject := range s.TaskObjects {
for taskName, taskConfigMap := range taskObject {
runner, exists := taskRunner[taskName]
if exists {
s.TaskRunnerMapping[runner] = taskConfigMap
continue
}
s.UnsupportedTasks = append(s.UnsupportedTasks, taskName)
prt.Debugf("Ignoring task without registered runner: %s", color.RedString(taskName))
}
}
s.IsValid = true
return nil
}
func loadConfigFile(absPath string, tasks *[]api.Task) error {
f := file.NewFile(absPath).WithEncoder(json.NewJsonEncoder())
// Check if the file exists...
if exists, _ := filesystem.FileExists(f.Path); !exists {
return fmt.Errorf("no such snowblock configuration file: %s", color.RedString(f.Path))
}
// ...and decode the file content with the given tasks using the assigned encoder.
content, err := ioutil.ReadFile(f.Path)
if err != nil {
return err
}
if encErr := f.Encoder.Decode(content, tasks); encErr != nil {
return fmt.Errorf("could not load snowblock configuration file: %s: %v", f.Path, encErr)
}
return nil
}