From cc1fd3d03ee4fc00fe16d4eb81aad96725c1711f Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sat, 25 Mar 2023 19:13:06 +0000 Subject: [PATCH] fix: deep copying pointers inside slices (#1072) --- CHANGELOG.md | 1 + taskfile/cmd.go | 46 ++++++++++++------------------------ taskfile/copy.go | 20 ++++++++++++---- taskfile/dep.go | 50 ++++++++++++++++++++++++++++++++++++++++ taskfile/platforms.go | 10 ++++++++ taskfile/precondition.go | 10 ++++++++ taskfile/task.go | 3 +++ 7 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 taskfile/dep.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a7420c5f..5b49a039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ arguments not whitespaces ([#1040](https://github.com/go-task/task/issues/1040), [#1059](https://github.com/go-task/task/pull/1059) by @dhanusaputra). - Fix the value of `{{.CHECKSUM}}` variable in status ([#1076](https://github.com/go-task/task/issues/1076), [#1080](https://github.com/go-task/task/pull/1080) by @pd93). +- Fixed deep copy implementation ([#1072](https://github.com/go-task/task/pull/1072) by @pd93) ## v3.22.0 - 2023-03-10 diff --git a/taskfile/cmd.go b/taskfile/cmd.go index b036a6d1..326a0287 100644 --- a/taskfile/cmd.go +++ b/taskfile/cmd.go @@ -19,10 +19,21 @@ type Cmd struct { Platforms []*Platform } -// Dep is a task dependency -type Dep struct { - Task string - Vars *Vars +func (c *Cmd) DeepCopy() *Cmd { + if c == nil { + return nil + } + return &Cmd{ + Cmd: c.Cmd, + Silent: c.Silent, + Task: c.Task, + Set: deepCopySlice(c.Set), + Shopt: deepCopySlice(c.Shopt), + Vars: c.Vars.DeepCopy(), + IgnoreError: c.IgnoreError, + Defer: c.Defer, + Platforms: deepCopySlice(c.Platforms), + } } func (c *Cmd) UnmarshalYAML(node *yaml.Node) error { @@ -94,30 +105,3 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error { return fmt.Errorf("yaml: line %d: cannot unmarshal %s into command", node.Line, node.ShortTag()) } - -func (d *Dep) UnmarshalYAML(node *yaml.Node) error { - switch node.Kind { - - case yaml.ScalarNode: - var task string - if err := node.Decode(&task); err != nil { - return err - } - d.Task = task - return nil - - case yaml.MappingNode: - var taskCall struct { - Task string - Vars *Vars - } - if err := node.Decode(&taskCall); err != nil { - return err - } - d.Task = taskCall.Task - d.Vars = taskCall.Vars - return nil - } - - return fmt.Errorf("cannot unmarshal %s into dependency", node.ShortTag()) -} diff --git a/taskfile/copy.go b/taskfile/copy.go index 10e1a017..d2c12f67 100644 --- a/taskfile/copy.go +++ b/taskfile/copy.go @@ -1,23 +1,35 @@ package taskfile -import "golang.org/x/exp/constraints" +type DeepCopier[T any] interface { + DeepCopy() T +} func deepCopySlice[T any](orig []T) []T { if orig == nil { return nil } c := make([]T, len(orig)) - copy(c, orig) + for i, v := range orig { + if copyable, ok := any(v).(DeepCopier[T]); ok { + c[i] = copyable.DeepCopy() + } else { + c[i] = v + } + } return c } -func deepCopyMap[K constraints.Ordered, V any](orig map[K]V) map[K]V { +func deepCopyMap[K comparable, V any](orig map[K]V) map[K]V { if orig == nil { return nil } c := make(map[K]V, len(orig)) for k, v := range orig { - c[k] = v + if copyable, ok := any(v).(DeepCopier[V]); ok { + c[k] = copyable.DeepCopy() + } else { + c[k] = v + } } return c } diff --git a/taskfile/dep.go b/taskfile/dep.go new file mode 100644 index 00000000..6b6ba01b --- /dev/null +++ b/taskfile/dep.go @@ -0,0 +1,50 @@ +package taskfile + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +// Dep is a task dependency +type Dep struct { + Task string + Vars *Vars +} + +func (d *Dep) DeepCopy() *Dep { + if d == nil { + return nil + } + return &Dep{ + Task: d.Task, + Vars: d.Vars.DeepCopy(), + } +} + +func (d *Dep) UnmarshalYAML(node *yaml.Node) error { + switch node.Kind { + + case yaml.ScalarNode: + var task string + if err := node.Decode(&task); err != nil { + return err + } + d.Task = task + return nil + + case yaml.MappingNode: + var taskCall struct { + Task string + Vars *Vars + } + if err := node.Decode(&taskCall); err != nil { + return err + } + d.Task = taskCall.Task + d.Vars = taskCall.Vars + return nil + } + + return fmt.Errorf("cannot unmarshal %s into dependency", node.ShortTag()) +} diff --git a/taskfile/platforms.go b/taskfile/platforms.go index f71dcbb8..20d74f72 100644 --- a/taskfile/platforms.go +++ b/taskfile/platforms.go @@ -15,6 +15,16 @@ type Platform struct { Arch string } +func (p *Platform) DeepCopy() *Platform { + if p == nil { + return nil + } + return &Platform{ + OS: p.OS, + Arch: p.Arch, + } +} + type ErrInvalidPlatform struct { Platform string } diff --git a/taskfile/precondition.go b/taskfile/precondition.go index 3ef37d3a..e558d921 100644 --- a/taskfile/precondition.go +++ b/taskfile/precondition.go @@ -18,6 +18,16 @@ type Precondition struct { Msg string } +func (p *Precondition) DeepCopy() *Precondition { + if p == nil { + return nil + } + return &Precondition{ + Sh: p.Sh, + Msg: p.Msg, + } +} + // UnmarshalYAML implements yaml.Unmarshaler interface. func (p *Precondition) UnmarshalYAML(node *yaml.Node) error { switch node.Kind { diff --git a/taskfile/task.go b/taskfile/task.go index 7cfd7523..d5621751 100644 --- a/taskfile/task.go +++ b/taskfile/task.go @@ -131,6 +131,9 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error { // DeepCopy creates a new instance of Task and copies // data by value from the source struct. func (t *Task) DeepCopy() *Task { + if t == nil { + return nil + } c := &Task{ Task: t.Task, Cmds: deepCopySlice(t.Cmds),