+/*
+ * Copyright 2020 Huawei Technologies Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package workspace
+
+import (
+ "sync"
+)
+
+type PlanIf interface {
+ SetErrorStep(stepName string)
+ UsingSpace(spaceName string)
+ RunBackground(task ...interface{})
+ RunParallel(task ...interface{})
+ RunSerial(task ...interface{})
+}
+
+type SpaceIf interface {
+ PlanIf
+ getPlan() *PlanBase
+}
+
+type ErrCode int
+
+const (
+ TaskContinue ErrCode = -1
+ TaskOK ErrCode = iota
+ TaskFail
+)
+
+const FINALLY string = "finally"
+
+
+type SubGrp struct {
+ Policy GoPolicy
+ CurStepIdx int
+ StepNames []string
+ StepObjs []interface{}
+}
+
+func (s *SubGrp) Install(task []interface{}) bool {
+ for _, stepObj := range task {
+ if stepObj == nil {
+ return false
+ }
+ s.StepObjs = append(s.StepObjs, stepObj)
+ }
+ return true
+}
+
+type SerErrInfo struct {
+ ErrCode int
+ Message string
+ GrpIdx int
+ TaskIdx int
+}
+
+type PlanBase struct {
+ SerError *SerErrInfo
+ PlanName string
+ SpaceName string
+ ErrStep string
+ PlanGrp []SubGrp
+ CurGrpIdx int
+ WtPlan sync.WaitGroup
+}
+
+func (b *PlanBase) SetErrorStep(stepName string) {
+ b.ErrStep = stepName
+}
+
+func (b *PlanBase) UsingSpace(spaceName string) {
+ b.SpaceName = spaceName
+}
+
+func (b *PlanBase) RunBackground(task ...interface{}) {
+ b.LoadTask(GoBackground, task)
+}
+
+func (b *PlanBase) RunParallel(task ...interface{}) {
+ b.LoadTask(GoParallel, task)
+}
+
+func (b *PlanBase) RunSerial(task ...interface{}) {
+ b.LoadTask(GoSerial, task)
+}
+
+func (b *PlanBase) RunSerialName(task interface{}, name string) {
+ var subGrp SubGrp
+ subGrp.Policy = GoSerial
+ subGrp.Install([]interface{}{task})
+ b.LoadData([]interface{}{task})
+ subGrp.StepNames = []string{name}
+ b.PlanGrp = append(b.PlanGrp, subGrp)
+}
+
+func (b *PlanBase) Try(task ...interface{}) {
+ b.SetErrorStep(FINALLY)
+ b.LoadTask(GoSerial, task)
+}
+
+func (b *PlanBase) Finally(task interface{}) {
+ var subGrp SubGrp
+ subGrp.Policy = GoSerial
+ subGrp.Install([]interface{}{task})
+ b.LoadData([]interface{}{task})
+ subGrp.StepNames = []string{FINALLY}
+ b.PlanGrp = append(b.PlanGrp, subGrp)
+}
+
+func (b *PlanBase) LoadTask(policy GoPolicy, task []interface{}) {
+ var subGrp SubGrp
+ subGrp.Policy = policy
+ subGrp.Install(task)
+ b.LoadData(task)
+ b.PlanGrp = append(b.PlanGrp, subGrp)
+}
+
+func (b *PlanBase) LoadData(task []interface{}) bool {
+ for _, stepObj := range task {
+ if stepObj == nil {
+ return false
+ }
+ stepIf, ok := stepObj.(TaskBaseIf)
+ if !ok {
+ continue
+ }
+ stepIf.SetSerErrInfo(b.SerError)
+ }
+ return true
+}
+
+type SpaceBase struct {
+ PlanBase
+}
+
+func (s *SpaceBase) Init() {
+ s.SerError = &SerErrInfo{}
+}
+
+func (s *SpaceBase) getPlan() *PlanBase {
+ return &s.PlanBase
+}
+
+func GotoErrorStep(curPlan *PlanBase, grpNum int) bool {
+ for idx, stepName := range curPlan.PlanGrp[grpNum].StepNames {
+ if stepName == curPlan.ErrStep {
+ if curPlan.CurGrpIdx > grpNum {
+ return true
+ }
+ curPlan.CurGrpIdx = grpNum
+ if curPlan.PlanGrp[grpNum].CurStepIdx < idx {
+ curPlan.PlanGrp[grpNum].CurStepIdx = idx
+ }
+ return true
+ }
+ }
+ return false
+}
+
+func RecordErrInfo(curPlan *PlanBase, stepIdx int) {
+ if curPlan.CurGrpIdx >= len(curPlan.PlanGrp) {
+ return
+ }
+ curGrp := curPlan.PlanGrp[curPlan.CurGrpIdx]
+ if stepIdx < 0 || stepIdx >= len(curGrp.StepObjs) {
+ return
+ }
+
+ curPlan.SerError.GrpIdx = curPlan.CurGrpIdx
+ curPlan.SerError.TaskIdx = stepIdx
+ curStep := curGrp.StepObjs[stepIdx]
+ stepIf, ok := curStep.(TaskBaseIf)
+ if !ok {
+ return
+ }
+ errCode, msg := stepIf.GetErrCode()
+ curPlan.SerError.ErrCode = int(errCode)
+ curPlan.SerError.Message = msg
+}
+
+func GotoErrorProc(curPlan *PlanBase) {
+ curPlan.CurGrpIdx++
+ for grpNum := 0; grpNum < len(curPlan.PlanGrp); grpNum++ {
+ done := GotoErrorStep(curPlan, grpNum)
+ if done {
+ break
+ }
+ }
+}