github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/blocks/handle_show.go (about)

     1  // Copyright 2021 The TrueBlocks Authors. All rights reserved.
     2  // Use of this source code is governed by a license that can
     3  // be found in the LICENSE file.
     4  
     5  package blocksPkg
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"sort"
    11  	"sync"
    12  
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    18  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    19  )
    20  
    21  var errMutex sync.Mutex
    22  
    23  func (opts *BlocksOptions) HandleShow(rCtx *output.RenderCtx) error {
    24  	chain := opts.Globals.Chain
    25  	testMode := opts.Globals.TestMode
    26  	nErrors := 0
    27  
    28  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    29  		apps, _, err := identifiers.IdsToApps(chain, opts.BlockIds)
    30  		if err != nil {
    31  			errorChan <- err
    32  			rCtx.Cancel()
    33  		}
    34  
    35  		if sliceOfMaps, cnt, err := types.AsSliceOfMaps[types.Block](apps, false); err != nil {
    36  			errorChan <- err
    37  			rCtx.Cancel()
    38  
    39  		} else if cnt == 0 {
    40  			errorChan <- fmt.Errorf("no blocks found for the query")
    41  			rCtx.Cancel()
    42  
    43  		} else {
    44  			showProgress := opts.Globals.ShowProgress()
    45  			bar := logger.NewBar(logger.BarOptions{
    46  				Enabled: showProgress,
    47  				Total:   int64(cnt),
    48  			})
    49  
    50  			for _, thisMap := range sliceOfMaps {
    51  				if rCtx.WasCanceled() {
    52  					return
    53  				}
    54  
    55  				for app := range thisMap {
    56  					thisMap[app] = new(types.Block)
    57  				}
    58  
    59  				items := make([]*types.Block, 0, len(thisMap))
    60  				iterFunc := func(app types.Appearance, value *types.Block) error {
    61  					bn := base.Blknum(app.BlockNumber)
    62  					if block, err := opts.Conn.GetBlockBodyByNumber(bn); err != nil {
    63  						errMutex.Lock()
    64  						defer errMutex.Unlock()
    65  						delete(thisMap, app)
    66  						return err
    67  					} else {
    68  						*value = block
    69  						bar.Tick()
    70  					}
    71  					return nil
    72  				}
    73  
    74  				iterErrorChan := make(chan error)
    75  				iterCtx, iterCancel := context.WithCancel(context.Background())
    76  				defer iterCancel()
    77  				go utils.IterateOverMap(iterCtx, iterErrorChan, thisMap, iterFunc)
    78  				for err := range iterErrorChan {
    79  					if !testMode || nErrors == 0 {
    80  						errorChan <- err
    81  						nErrors++
    82  					}
    83  				}
    84  
    85  				for _, item := range thisMap {
    86  					items = append(items, item)
    87  				}
    88  				sort.Slice(items, func(i, j int) bool {
    89  					if items[i].BlockNumber == items[j].BlockNumber {
    90  						return items[i].Hash.Hex() < items[j].Hash.Hex()
    91  					}
    92  					return items[i].BlockNumber < items[j].BlockNumber
    93  				})
    94  
    95  				for _, item := range items {
    96  					modelChan <- item
    97  				}
    98  			}
    99  			bar.Finish(true /* newLine */)
   100  		}
   101  	}
   102  
   103  	extraOpts := map[string]any{
   104  		"uncles":     opts.Uncles,
   105  		"articulate": opts.Articulate,
   106  	}
   107  
   108  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
   109  }