github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/identifiers/range.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 identifiers
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"regexp"
    12  	"strconv"
    13  
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    15  )
    16  
    17  type IdentifierType int64
    18  
    19  const (
    20  	NotDefined IdentifierType = iota
    21  	BlockNumber
    22  	BlockTimestamp
    23  	BlockHash
    24  	BlockDate
    25  	BlockSpecial
    26  	TransactionIndex
    27  	TransactionHash
    28  	Period
    29  	Step
    30  )
    31  
    32  type Identifier struct {
    33  	StartType    IdentifierType `json:"startType,omitempty"`
    34  	Start        Point          `json:"start,omitempty"`
    35  	EndType      IdentifierType `json:"endType,omitempty"`
    36  	End          Point          `json:"end,omitempty"`
    37  	ModifierType IdentifierType `json:"modifierType,omitempty"`
    38  	Modifier     Modifier       `json:"modifier,omitempty"`
    39  	Orig         string         `json:"-"`
    40  }
    41  
    42  // NewBlockRange parses a string containing block range and returns a struct
    43  // that always has Start, StartType, EndType, ModifierType fields
    44  // and may as well have End and Modifier, if they are defined.
    45  // *Type fields can be used to quickly check what is the type of
    46  // the values.
    47  //
    48  // Consult ./parser.go for the supported format
    49  // TODO: This does not handle the zero block correctly
    50  func NewBlockRange(rangeStr string) (*Identifier, error) {
    51  	parsed, err := Parse(rangeStr)
    52  	newRange := &Identifier{}
    53  	if err != nil {
    54  		return nil, handleParserErrors(err)
    55  	}
    56  
    57  	newRange.Orig = rangeStr
    58  	newRange.StartType = getPointType(parsed.Points[0])
    59  	newRange.Start = *parsed.Points[0]
    60  
    61  	if len(parsed.Points) == 1 {
    62  		newRange.EndType = NotDefined
    63  	} else {
    64  		newRange.EndType = getPointType(parsed.Points[1])
    65  		newRange.End = *parsed.Points[1]
    66  	}
    67  
    68  	if parsed.Modifier == nil {
    69  		newRange.ModifierType = NotDefined
    70  	} else {
    71  		newRange.ModifierType = getModifierType(parsed.Modifier)
    72  		newRange.Modifier = *parsed.Modifier
    73  	}
    74  
    75  	return newRange, nil
    76  }
    77  
    78  func NewTxRange(rangeStr string) (*Identifier, error) {
    79  	parsed, err := Parse(rangeStr)
    80  	if err != nil {
    81  		return nil, handleParserErrors(err)
    82  	}
    83  
    84  	// returns at least one point
    85  	newRange := &Identifier{
    86  		Orig:      rangeStr,
    87  		StartType: getPointType(parsed.Points[0]),
    88  		Start:     *parsed.Points[0],
    89  	}
    90  
    91  	if len(parsed.Points) == 2 {
    92  		newRange.EndType = getPointType(parsed.Points[1])
    93  		newRange.End = *parsed.Points[1]
    94  	}
    95  
    96  	if parsed.Modifier != nil {
    97  		newRange.ModifierType = getModifierType(parsed.Modifier)
    98  		newRange.Modifier = *parsed.Modifier
    99  	}
   100  
   101  	return newRange, nil
   102  }
   103  
   104  func (id IdentifierType) String() string {
   105  	return []string{
   106  		"NotDefined",
   107  		"BlockNumber",
   108  		"BlockTimestamp",
   109  		"BlockHash",
   110  		"BlockDate",
   111  		"BlockSpecial",
   112  		"TransactionIndex",
   113  		"TransactionHash",
   114  		"Period",
   115  		"Step",
   116  	}[id]
   117  }
   118  
   119  func (id *Identifier) UnmarshalJSON(data []byte) error {
   120  	str, err := strconv.Unquote(string(data))
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	newBlock, err := NewBlockRange(str)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	*id = *newBlock
   131  
   132  	return nil
   133  }
   134  
   135  func (id *Identifier) String() string {
   136  	str, _ := json.MarshalIndent(id, "", "  ")
   137  	return string(str)
   138  }
   139  
   140  func getPointType(p *Point) IdentifierType {
   141  	if p == nil {
   142  		return NotDefined
   143  	}
   144  
   145  	if p.Date != "" {
   146  		return BlockDate
   147  	}
   148  
   149  	if p.Special != "" {
   150  		return BlockSpecial
   151  	}
   152  
   153  	if p.Hash != "" {
   154  		return BlockHash
   155  	}
   156  
   157  	if p.Number >= utils.EarliestEvmTs {
   158  		return BlockTimestamp
   159  	}
   160  
   161  	return BlockNumber
   162  }
   163  
   164  func getModifierType(m *Modifier) IdentifierType {
   165  	if m == nil {
   166  		return Step
   167  	}
   168  
   169  	if m.Period != "" {
   170  		return Period
   171  	}
   172  
   173  	return Step
   174  }
   175  
   176  type WrongModifierError struct {
   177  	Token string
   178  }
   179  
   180  func (e *WrongModifierError) Error() string {
   181  	return fmt.Sprintf("wrong modifier: %s", e.Token)
   182  }
   183  
   184  func handleParserErrors(parseError error) error {
   185  	modifierMatch, err := regexp.MatchString("expected Modifier", parseError.Error())
   186  	if err != nil || !modifierMatch {
   187  		return parseError
   188  	}
   189  
   190  	matches := regexp.MustCompile(`unexpected token "(.+)"`).FindStringSubmatch(parseError.Error())
   191  
   192  	if len(matches) < 2 {
   193  		return errors.New("no matches")
   194  	}
   195  
   196  	return &WrongModifierError{
   197  		Token: matches[1],
   198  	}
   199  }