github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/export/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 exportPkg
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"sort"
    11  
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/articulate"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/filter"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/monitor"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
    18  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    19  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    20  )
    21  
    22  func (opts *ExportOptions) HandleShow(rCtx *output.RenderCtx, monitorArray []monitor.Monitor) error {
    23  	abiCache := articulate.NewAbiCache(opts.Conn, opts.Articulate)
    24  	filter := filter.NewFilter(
    25  		opts.Reversed,
    26  		opts.Reverted,
    27  		opts.Fourbytes,
    28  		base.BlockRange{First: opts.FirstBlock, Last: opts.LastBlock},
    29  		base.RecordRange{First: opts.FirstRecord, Last: opts.GetMax()},
    30  	)
    31  
    32  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    33  		for _, mon := range monitorArray {
    34  			if apps, cnt, err := mon.ReadAndFilterAppearances(filter, false /* withCount */); err != nil {
    35  				errorChan <- err
    36  				rCtx.Cancel()
    37  
    38  			} else if cnt == 0 {
    39  				errorChan <- fmt.Errorf("no blocks found for the query")
    40  				continue
    41  
    42  			} else {
    43  				if sliceOfMaps, _, err := types.AsSliceOfMaps[types.Transaction](apps, filter.Reversed); err != nil {
    44  					errorChan <- err
    45  					rCtx.Cancel()
    46  
    47  				} else {
    48  					showProgress := opts.Globals.ShowProgress()
    49  					bar := logger.NewBar(logger.BarOptions{
    50  						Prefix:  mon.Address.Hex(),
    51  						Enabled: showProgress,
    52  						Total:   int64(cnt),
    53  					})
    54  
    55  					// TODO: BOGUS - THIS IS NOT CONCURRENCY SAFE
    56  					finished := false
    57  					for _, thisMap := range sliceOfMaps {
    58  						if rCtx.WasCanceled() {
    59  							return
    60  						}
    61  
    62  						if finished {
    63  							continue
    64  						}
    65  
    66  						for app := range thisMap {
    67  							thisMap[app] = new(types.Transaction)
    68  						}
    69  
    70  						iterFunc := func(app types.Appearance, value *types.Transaction) error {
    71  							if tx, err := opts.Conn.GetTransactionByAppearance(&app, false); err != nil {
    72  								return err
    73  							} else {
    74  								passes, _ := filter.ApplyTxFilters(tx)
    75  								if passes {
    76  									*value = *tx
    77  								}
    78  								if bar != nil {
    79  									bar.Tick()
    80  								}
    81  								return nil
    82  							}
    83  						}
    84  
    85  						// Set up and interate over the map calling iterFunc for each appearance
    86  						iterCtx, iterCancel := context.WithCancel(context.Background())
    87  						defer iterCancel()
    88  						errChan := make(chan error)
    89  						go utils.IterateOverMap(iterCtx, errChan, thisMap, iterFunc)
    90  						if stepErr := <-errChan; stepErr != nil {
    91  							errorChan <- stepErr
    92  							return
    93  						}
    94  
    95  						items := make([]*types.Transaction, 0, len(thisMap))
    96  						for _, tx := range thisMap {
    97  							if opts.Articulate {
    98  								if err = abiCache.ArticulateTransaction(tx); err != nil {
    99  									errorChan <- err // continue even on error
   100  								}
   101  							}
   102  							items = append(items, tx)
   103  						}
   104  
   105  						sort.Slice(items, func(i, j int) bool {
   106  							if opts.Reversed {
   107  								i, j = j, i
   108  							}
   109  							if items[i].BlockNumber == items[j].BlockNumber {
   110  								return items[i].TransactionIndex < items[j].TransactionIndex
   111  							}
   112  							return items[i].BlockNumber < items[j].BlockNumber
   113  						})
   114  
   115  						for _, item := range items {
   116  							if item.BlockHash.IsZero() {
   117  								continue
   118  							}
   119  							var passes bool
   120  							passes, finished = filter.ApplyCountFilter()
   121  							if passes {
   122  								modelChan <- item
   123  							}
   124  							if finished {
   125  								break
   126  							}
   127  						}
   128  					}
   129  					bar.Finish(true /* newLine */)
   130  				}
   131  			}
   132  		}
   133  	}
   134  
   135  	extraOpts := map[string]any{
   136  		"articulate": opts.Articulate,
   137  		"export":     true,
   138  	}
   139  
   140  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
   141  }