-
Notifications
You must be signed in to change notification settings - Fork 0
/
spec.go
143 lines (124 loc) · 3.2 KB
/
spec.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
141
142
143
package vending
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/alevinval/vendor-go/pkg/log"
"gopkg.in/yaml.v3"
)
// VERSION of the current tool
const VERSION = "v0.4.9"
// Spec holds relevant information related to the specification of what
// versions need to be fetched when updating dependencies.
//
// This model directly maps to the serialized YAML of the spec lock file.
type Spec struct {
Version string `yaml:"version"`
PresetName string `yaml:"preset"`
VendorDir string `yaml:"vendor_dir,omitempty"`
Filters *Filters `yaml:",inline"`
Deps []*Dependency `yaml:"deps"`
preset Preset `yaml:"-"`
}
// NewSpec allocates a new Spec instance with the default initialization.
func NewSpec(preset Preset) *Spec {
preset = checkPreset(preset, true)
spec := &Spec{
Version: VERSION,
Filters: NewFilters(),
Deps: []*Dependency{},
}
spec.applyPreset(preset)
return spec
}
// AddDependency adds a Dependency to the list of dependencies to vendor.
func (s *Spec) AddDependency(dependency *Dependency) {
if dep, ok := s.findDep(dependency); ok {
dep.Update(dependency)
} else {
s.Deps = append(s.Deps, dependency)
}
s.applyPreset(s.preset)
}
// Load Spec from the filesystem.
func (s *Spec) Load() error {
preset := s.preset
filename := preset.GetSpecFilename()
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("cannot read file: %w", err)
}
err = yaml.Unmarshal(data, s)
if err != nil {
return fmt.Errorf("cannot unmarshal: %w", err)
}
s.applyPreset(preset)
return nil
}
// Save converts the Spec to YAML, and writes the data in the spec file,
// as specified by the Preset.
func (s *Spec) Save() error {
filename := s.preset.GetSpecFilename()
data, err := toYaml(s)
if err != nil {
return fmt.Errorf("cannot convert to yaml: %w", err)
}
err = os.WriteFile(filename, data, os.ModePerm)
if err != nil {
return fmt.Errorf("cannot write file: %w", err)
}
return nil
}
func (s *Spec) applyPreset(preset Preset) {
s.preset = preset
if s.VendorDir == "" {
s.VendorDir = preset.GetVendorDir()
}
s.PresetName = preset.GetPresetName()
if s.Version < VERSION {
s.Version = VERSION
}
s.Filters.ApplyPreset(preset)
for _, dep := range s.Deps {
dep.applyPreset(preset)
}
}
func (s *Spec) findDep(dependency *Dependency) (*Dependency, bool) {
for _, dep := range s.Deps {
if strings.EqualFold(dep.URL, dependency.URL) {
return dep, true
}
}
return nil, false
}
func checkPreset(preset Preset, warn bool) Preset {
if preset == nil {
if warn {
log.S().Warnf("no preset has been provided, using default preset")
}
return &DefaultPreset{}
}
if warn {
switch p := preset.(type) {
case *DefaultPreset:
break
default:
if p.GetPresetName() == "default" {
log.S().Warnf("custom preset injected, but the name is \"default\" which is used for the default preset name, this will be confusing for users, consider a different name for your preset")
}
}
}
return preset
}
func toYaml(obj interface{}) ([]byte, error) {
b := &bytes.Buffer{}
encoder := yaml.NewEncoder(b)
encoder.SetIndent(2)
err := encoder.Encode(obj)
if err != nil {
return nil, err
}
encoder.Close()
return b.Bytes(), nil
}