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  }