Cue’s scripting layer has a workflow engine that can build and run task graphs or DAGs.
The API allows you to leverage this workflow engine with your own tasks.
It will automatically detect dependencies between tasks for you
based on how fields and values are used.
You will need to implement
the TaskFunc interface which turns values into flow.Runner
packagemainimport("context""fmt""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext""cuelang.org/go/tools/flow")varCTXcue.Context// Our tasks specified as Cuevarinput=`
tasks: {
a: {
foo: 1
hello: string
}
b: {
foo: 2
}
c: {
foo: a.foo * 3
goo: b.foo * 3
}
}
`funcmain(){varerrerrorfmt.Println("Custom Flow Task")// create contextctx:=cuecontext.New()// Setup the flow Configcfg:=&flow.Config{Root:cue.ParsePath("tasks")}// compile our inputvalue:=ctx.CompileString(input,cue.Filename("input.cue"))ifvalue.Err()!=nil{fmt.Println("Error:",value.Err())return}// create the workflow whiich will build the task graphworkflow:=flow.New(cfg,value,TaskFactory)// run our custom workflowerr=workflow.Run(context.Background())iferr!=nil{fmt.Println("Error:",err)return}}// This function implements the Runner interface.// It parses Cue values, you will see all of them recursivelyfuncTaskFactory(valcue.Value)(flow.Runner,error){// You can see the recursive values with thisfmt.Println("TF: ",val)// Check that we have something that looks like a taskfoo:=val.Lookup("foo")if!foo.Exists(){returnnil,nil}num,err:=foo.Int64()iferr!=nil{returnnil,err}// Create and return a flow.Runnerct:=&CustomTask{Val:int(num),}returnct,nil}// Our custom task with some extra data// While we only have one task here, you can have as many as you like// It's up to the TaskFunc (TaskFactory above) to create the taskstypeCustomTaskstruct{Valint}// Tasks must implement a Run func, this is where we execute our taskfunc(C*CustomTask)Run(t*flow.Task,pErrerror)error{// not sure this is OK, but the value which was used for this taskval:=t.Value()fmt.Println("CustomTask:",C.Val,val)// Do some worknext:=map[string]interface{}{"bar":C.Val+1,}hello:=val.LookupPath(cue.ParsePath("foo"))ifhello.Exists(){next["hello"]="world"}// Use fill to "return" a result to the workflow enginet.Fill(next)returnnil}