11package taskgraph
22
3- import "fmt"
3+ import (
4+ "errors"
5+ "fmt"
6+ )
47
58// TaskBuilder helps construct taskgraph Tasks with a fluent API.
69type TaskBuilder [T any ] struct {
@@ -66,32 +69,47 @@ func (b *TaskBuilder[T]) WithDefaultBindings(bindings ...Binding) *TaskBuilder[T
6669}
6770
6871// Build constructs and returns the Task.
69- func (b * TaskBuilder [T ]) Build () TaskSet {
72+ func (b * TaskBuilder [T ]) Build () ( TaskSet , error ) {
7073 reflect := Reflect [T ]{
7174 Name : b .name ,
7275 ResultKey : b .resultKey ,
7376 Depends : b .depends ,
7477 Fn : b .fn ,
7578 }
7679 reflect .location = getLocation (2 )
77- var task TaskSet = reflect
80+
81+ // Eagerly build to validate and get the underlying task
82+ task , err := reflect .Build ()
83+ if err != nil {
84+ return nil , err
85+ }
86+ var ts TaskSet = task
7887
7988 if b .condition != nil {
8089 conditional := Conditional {
81- Wrapped : task ,
90+ Wrapped : ts ,
8291 Condition : b .condition ,
8392 }
8493
8594 if b .defaultSet {
86- conditional .DefaultBindings = append (conditional .DefaultBindings , b .resultKey .Bind (b .defaultVal ))
95+ conditional .DefaultBindings = append (
96+ conditional .DefaultBindings ,
97+ b .resultKey .Bind (b .defaultVal ),
98+ )
8799 }
88100 conditional .DefaultBindings = append (conditional .DefaultBindings , b .defaultBindings ... )
89101
90102 conditional .location = getLocation (2 )
91- task = conditional
103+ ts = conditional
92104 }
93105
94- return task
106+ return ts , nil
107+ }
108+
109+ // Tasks satisfies the TaskSet interface to avoid the need to call Build(). It is equivalent to
110+ // calling Must(Build()).
111+ func (b * TaskBuilder [T ]) Tasks () []Task {
112+ return Must (b .Build ()).Tasks ()
95113}
96114
97115// MultiTaskBuilder helps construct taskgraph Tasks that provide multiple outputs or perform side effects.
@@ -102,6 +120,7 @@ type MultiTaskBuilder struct {
102120 provides []ID
103121 condition Condition
104122 defaultBindings []Binding
123+ errors []error
105124}
106125
107126// NewMultiTaskBuilder creates a new builder for a multi-output or side-effect task.
@@ -122,11 +141,13 @@ func (b *MultiTaskBuilder) Provides(keys ...any) *MultiTaskBuilder {
122141 for _ , k := range keys {
123142 rk , err := newReflectKey (k )
124143 if err != nil {
125- panic (fmt .Errorf ("invalid key passed to Provides: %w" , err ))
144+ b .errors = append (b .errors , fmt .Errorf ("invalid key passed to Provides: %w" , err ))
145+ continue
126146 }
127147 id , err := rk .ID ()
128148 if err != nil {
129- panic (fmt .Errorf ("invalid key ID in Provides: %w" , err ))
149+ b .errors = append (b .errors , fmt .Errorf ("invalid key ID in Provides: %w" , err ))
150+ continue
130151 }
131152 b .provides = append (b .provides , id )
132153 }
@@ -165,7 +186,11 @@ func (b *MultiTaskBuilder) WithDefaultBindings(bindings ...Binding) *MultiTaskBu
165186}
166187
167188// Build constructs and returns the Task.
168- func (b * MultiTaskBuilder ) Build () TaskSet {
189+ func (b * MultiTaskBuilder ) Build () (TaskSet , error ) {
190+ if len (b .errors ) > 0 {
191+ return nil , errors .Join (b .errors ... )
192+ }
193+
169194 reflect := ReflectMulti {
170195 Name : b .name ,
171196 Depends : b .depends ,
@@ -185,5 +210,11 @@ func (b *MultiTaskBuilder) Build() TaskSet {
185210 task = conditional
186211 }
187212
188- return task
189- }
213+ return task , nil
214+ }
215+
216+ // Tasks satisfies the TaskSet interface to avoid the need to call Build(). It is equivalent to
217+ // calling Must(Build()).
218+ func (b * MultiTaskBuilder ) Tasks () []Task {
219+ return Must (b .Build ()).Tasks ()
220+ }
0 commit comments