mirror of
https://github.com/go-task/task.git
synced 2025-03-19 21:17:46 +02:00
192 lines
5.3 KiB
Go
192 lines
5.3 KiB
Go
package ast
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/elliotchance/orderedmap/v2"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/go-task/task/v3/errors"
|
|
"github.com/go-task/task/v3/internal/deepcopy"
|
|
)
|
|
|
|
type (
|
|
// Include represents information about included taskfiles
|
|
Include struct {
|
|
Namespace string
|
|
Taskfile string
|
|
Dir string
|
|
Optional bool
|
|
Internal bool
|
|
Aliases []string
|
|
Excludes []string
|
|
AdvancedImport bool
|
|
Vars *Vars
|
|
Flatten bool
|
|
}
|
|
// Includes is an ordered map of namespaces to includes.
|
|
Includes struct {
|
|
om *orderedmap.OrderedMap[string, *Include]
|
|
mutex sync.RWMutex
|
|
}
|
|
// An IncludeElement is a key-value pair that is used for initializing an
|
|
// Includes structure.
|
|
IncludeElement orderedmap.Element[string, *Include]
|
|
)
|
|
|
|
// NewIncludes creates a new instance of Includes and initializes it with the
|
|
// provided set of elements, if any. The elements are added in the order they
|
|
// are passed.
|
|
func NewIncludes(els ...*IncludeElement) *Includes {
|
|
includes := &Includes{
|
|
om: orderedmap.NewOrderedMap[string, *Include](),
|
|
}
|
|
for _, el := range els {
|
|
includes.Set(el.Key, el.Value)
|
|
}
|
|
return includes
|
|
}
|
|
|
|
// Len returns the number of includes in the Includes map.
|
|
func (includes *Includes) Len() int {
|
|
if includes == nil || includes.om == nil {
|
|
return 0
|
|
}
|
|
defer includes.mutex.RUnlock()
|
|
includes.mutex.RLock()
|
|
return includes.om.Len()
|
|
}
|
|
|
|
// Get returns the value the the include with the provided key and a boolean
|
|
// that indicates if the value was found or not. If the value is not found, the
|
|
// returned include is a zero value and the bool is false.
|
|
func (includes *Includes) Get(key string) (*Include, bool) {
|
|
if includes == nil || includes.om == nil {
|
|
return &Include{}, false
|
|
}
|
|
defer includes.mutex.RUnlock()
|
|
includes.mutex.RLock()
|
|
return includes.om.Get(key)
|
|
}
|
|
|
|
// Set sets the value of the include with the provided key to the provided
|
|
// value. If the include already exists, its value is updated. If the include
|
|
// does not exist, it is created.
|
|
func (includes *Includes) Set(key string, value *Include) bool {
|
|
if includes == nil {
|
|
includes = NewIncludes()
|
|
}
|
|
if includes.om == nil {
|
|
includes.om = orderedmap.NewOrderedMap[string, *Include]()
|
|
}
|
|
defer includes.mutex.Unlock()
|
|
includes.mutex.Lock()
|
|
return includes.om.Set(key, value)
|
|
}
|
|
|
|
// Range calls the provided function for each include in the map. The function
|
|
// receives the include's key and value as arguments. If the function returns
|
|
// an error, the iteration stops and the error is returned.
|
|
func (includes *Includes) Range(f func(k string, v *Include) error) error {
|
|
if includes == nil || includes.om == nil {
|
|
return nil
|
|
}
|
|
for pair := includes.om.Front(); pair != nil; pair = pair.Next() {
|
|
if err := f(pair.Key, pair.Value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
|
func (includes *Includes) UnmarshalYAML(node *yaml.Node) error {
|
|
if includes == nil || includes.om == nil {
|
|
*includes = *NewIncludes()
|
|
}
|
|
switch node.Kind {
|
|
case yaml.MappingNode:
|
|
// NOTE: orderedmap does not have an unmarshaler, so we have to decode
|
|
// the map manually. We increment over 2 values at a time and assign
|
|
// them as a key-value pair.
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
keyNode := node.Content[i]
|
|
valueNode := node.Content[i+1]
|
|
|
|
// Decode the value node into an Include struct
|
|
var v Include
|
|
if err := valueNode.Decode(&v); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
|
|
// Set the include namespace
|
|
v.Namespace = keyNode.Value
|
|
|
|
// Add the include to the ordered map
|
|
includes.Set(keyNode.Value, &v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("includes")
|
|
}
|
|
|
|
func (include *Include) UnmarshalYAML(node *yaml.Node) error {
|
|
switch node.Kind {
|
|
|
|
case yaml.ScalarNode:
|
|
var str string
|
|
if err := node.Decode(&str); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
include.Taskfile = str
|
|
return nil
|
|
|
|
case yaml.MappingNode:
|
|
var includedTaskfile struct {
|
|
Taskfile string
|
|
Dir string
|
|
Optional bool
|
|
Internal bool
|
|
Flatten bool
|
|
Aliases []string
|
|
Excludes []string
|
|
Vars *Vars
|
|
}
|
|
if err := node.Decode(&includedTaskfile); err != nil {
|
|
return errors.NewTaskfileDecodeError(err, node)
|
|
}
|
|
include.Taskfile = includedTaskfile.Taskfile
|
|
include.Dir = includedTaskfile.Dir
|
|
include.Optional = includedTaskfile.Optional
|
|
include.Internal = includedTaskfile.Internal
|
|
include.Aliases = includedTaskfile.Aliases
|
|
include.Excludes = includedTaskfile.Excludes
|
|
include.AdvancedImport = true
|
|
include.Vars = includedTaskfile.Vars
|
|
include.Flatten = includedTaskfile.Flatten
|
|
return nil
|
|
}
|
|
|
|
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("include")
|
|
}
|
|
|
|
// DeepCopy creates a new instance of IncludedTaskfile and copies
|
|
// data by value from the source struct.
|
|
func (include *Include) DeepCopy() *Include {
|
|
if include == nil {
|
|
return nil
|
|
}
|
|
return &Include{
|
|
Namespace: include.Namespace,
|
|
Taskfile: include.Taskfile,
|
|
Dir: include.Dir,
|
|
Optional: include.Optional,
|
|
Internal: include.Internal,
|
|
Excludes: deepcopy.Slice(include.Excludes),
|
|
AdvancedImport: include.AdvancedImport,
|
|
Vars: include.Vars.DeepCopy(),
|
|
Flatten: include.Flatten,
|
|
}
|
|
}
|