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 }