github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/processes/step.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package processes 19 20 import ( 21 "context" 22 "fmt" 23 "github.com/aacfactory/errors" 24 "unicode/utf8" 25 ) 26 27 type Unit interface { 28 Handle(ctx context.Context) (result interface{}, err error) 29 } 30 31 type Result struct { 32 StepNo int64 33 StepNum int64 34 StepName string 35 UnitNo int64 36 UnitNum int64 37 Data interface{} 38 Error error 39 } 40 41 func (result Result) String() string { 42 succeed := make([]byte, utf8.RuneLen(rune('√'))) 43 utf8.EncodeRune(succeed, rune('√')) 44 status := "succeed" 45 if result.Error != nil { 46 if IsAbortErr(result.Error) { 47 status = "aborted" 48 } else { 49 status = " failed" 50 } 51 } 52 return fmt.Sprintf("[%d/%d] %s [%d/%d] %s", result.StepNo, result.StepNum, result.StepName, result.UnitNo, result.UnitNum, status) 53 } 54 55 type Step struct { 56 no int64 57 name string 58 num int64 59 units []Unit 60 resultCh chan<- Result 61 } 62 63 func (step *Step) Execute(ctx context.Context) (err error) { 64 if ctx.Err() != nil { 65 err = ctx.Err() 66 return 67 } 68 if step.units == nil || len(step.units) == 0 { 69 return 70 } 71 unitNum := int64(len(step.units)) 72 stepResultCh := make(chan Result, unitNum) 73 for i, unit := range step.units { 74 unitNo := int64(i + 1) 75 if unit == nil { 76 step.resultCh <- Result{ 77 StepNo: step.no, 78 StepNum: step.num, 79 StepName: step.name, 80 UnitNo: unitNo, 81 UnitNum: unitNum, 82 Data: nil, 83 Error: errors.Warning("processes: unit is nil").WithMeta("step", step.name), 84 } 85 continue 86 } 87 go func(ctx context.Context, unitNo int64, unit Unit, step *Step, stepResultCh chan Result) { 88 if ctx.Err() != nil { 89 stepResultCh <- Result{ 90 StepNo: step.no, 91 StepNum: step.num, 92 StepName: step.name, 93 UnitNo: unitNo, 94 UnitNum: unitNum, 95 Data: nil, 96 Error: ctx.Err(), 97 } 98 return 99 } 100 data, unitErr := unit.Handle(ctx) 101 defer func() { 102 _ = recover() 103 }() 104 stepResultCh <- Result{ 105 StepNo: step.no, 106 StepNum: step.num, 107 StepName: step.name, 108 UnitNo: unitNo, 109 UnitNum: unitNum, 110 Data: data, 111 Error: unitErr, 112 } 113 }(ctx, unitNo, unit, step, stepResultCh) 114 } 115 resultErrs := errors.MakeErrors() 116 executed := int64(0) 117 for { 118 result, ok := <-stepResultCh 119 if !ok { 120 err = errors.Warning("processes: panic") 121 break 122 } 123 if result.Error != nil { 124 resultErrs.Append(result.Error) 125 } 126 step.resultCh <- result 127 executed++ 128 if executed >= unitNum { 129 break 130 } 131 } 132 err = resultErrs.Error() 133 return 134 }