github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/tokens/options.go (about) 1 // Copyright 2016, 2024 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 * Parts of this file were auto generated. Edit only those parts of 6 * the code inside of 'EXISTING_CODE' tags. 7 */ 8 9 package tokensPkg 10 11 import ( 12 // EXISTING_CODE 13 "encoding/json" 14 "io" 15 "net/http" 16 "net/url" 17 "strings" 18 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 26 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 27 // EXISTING_CODE 28 ) 29 30 // TokensOptions provides all command options for the chifra tokens command. 31 type TokensOptions struct { 32 Addrs []string `json:"addrs,omitempty"` // Two or more addresses (0x...), the first is an ERC20 token, balances for the rest are reported 33 Blocks []string `json:"blocks,omitempty"` // An optional list of one or more blocks at which to report balances, defaults to 'latest' 34 BlockIds []identifiers.Identifier `json:"blockIds,omitempty"` // Block identifiers 35 Parts []string `json:"parts,omitempty"` // Which parts of the token information to retrieve 36 ByAcct bool `json:"byAcct,omitempty"` // Consider each address an ERC20 token except the last, whose balance is reported for each token 37 Changes bool `json:"changes,omitempty"` // Only report a balance when it changes from one block to the next 38 NoZero bool `json:"noZero,omitempty"` // Suppress the display of zero balance accounts 39 Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options 40 Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server 41 BadFlag error `json:"badFlag,omitempty"` // An error flag if needed 42 // EXISTING_CODE 43 // EXISTING_CODE 44 } 45 46 var defaultTokensOptions = TokensOptions{} 47 48 // testLog is used only during testing to export the options for this test case. 49 func (opts *TokensOptions) testLog() { 50 logger.TestLog(len(opts.Addrs) > 0, "Addrs: ", opts.Addrs) 51 logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks) 52 logger.TestLog(len(opts.Parts) > 0, "Parts: ", opts.Parts) 53 logger.TestLog(opts.ByAcct, "ByAcct: ", opts.ByAcct) 54 logger.TestLog(opts.Changes, "Changes: ", opts.Changes) 55 logger.TestLog(opts.NoZero, "NoZero: ", opts.NoZero) 56 opts.Conn.TestLog(opts.getCaches()) 57 opts.Globals.TestLog() 58 } 59 60 // String implements the Stringer interface 61 func (opts *TokensOptions) String() string { 62 b, _ := json.MarshalIndent(opts, "", " ") 63 return string(b) 64 } 65 66 // tokensFinishParseApi finishes the parsing for server invocations. Returns a new TokensOptions. 67 func tokensFinishParseApi(w http.ResponseWriter, r *http.Request) *TokensOptions { 68 values := r.URL.Query() 69 if r.Header.Get("User-Agent") == "testRunner" { 70 values.Set("testRunner", "true") 71 } 72 return TokensFinishParseInternal(w, values) 73 } 74 75 func TokensFinishParseInternal(w io.Writer, values url.Values) *TokensOptions { 76 copy := defaultTokensOptions 77 copy.Globals.Caps = getCaps() 78 opts := © 79 for key, value := range values { 80 switch key { 81 case "addrs": 82 for _, val := range value { 83 s := strings.Split(val, " ") // may contain space separated items 84 opts.Addrs = append(opts.Addrs, s...) 85 } 86 case "blocks": 87 for _, val := range value { 88 s := strings.Split(val, " ") // may contain space separated items 89 opts.Blocks = append(opts.Blocks, s...) 90 } 91 case "parts": 92 for _, val := range value { 93 s := strings.Split(val, " ") // may contain space separated items 94 opts.Parts = append(opts.Parts, s...) 95 } 96 case "byAcct": 97 opts.ByAcct = true 98 case "changes": 99 opts.Changes = true 100 case "noZero": 101 opts.NoZero = true 102 default: 103 if !copy.Globals.Caps.HasKey(key) { 104 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "tokens") 105 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 106 opts.BadFlag = err 107 } 108 } 109 } 110 } 111 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 112 113 // EXISTING_CODE 114 if len(opts.Addrs) == 1 && len(opts.Parts) == 0 { 115 opts.Parts = append(opts.Parts, "all") 116 } 117 if len(opts.Blocks) == 0 { 118 if opts.Globals.TestMode { 119 opts.Blocks = []string{"17000000"} 120 } else { 121 opts.Blocks = []string{"latest"} 122 } 123 } 124 // EXISTING_CODE 125 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 126 127 return opts 128 } 129 130 // tokensFinishParse finishes the parsing for command line invocations. Returns a new TokensOptions. 131 func tokensFinishParse(args []string) *TokensOptions { 132 // remove duplicates from args if any (not needed in api mode because the server does it). 133 dedup := map[string]int{} 134 if len(args) > 0 { 135 tmp := []string{} 136 for _, arg := range args { 137 if value := dedup[arg]; value == 0 { 138 tmp = append(tmp, arg) 139 } 140 dedup[arg]++ 141 } 142 args = tmp 143 } 144 145 defFmt := "txt" 146 opts := GetOptions() 147 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 148 149 // EXISTING_CODE 150 if len(args) > 0 { 151 // The first argument in the list is the token, so not a dup... 152 cnt := dedup[args[0]] 153 if cnt > 1 { 154 // The first token was present more than once, which is okay for 155 // tokens who happen to own themselves, so add it back in. 156 args = append(args, args[0]) 157 } 158 159 // we need to separate blocks from addresses 160 for _, arg := range args { 161 if base.IsValidAddress(arg) { 162 opts.Addrs = append(opts.Addrs, arg) 163 } else { 164 opts.Blocks = append(opts.Blocks, arg) 165 } 166 } 167 } 168 if len(opts.Addrs) == 1 && len(opts.Parts) == 0 { 169 opts.Parts = append(opts.Parts, "all") 170 } 171 if len(opts.Blocks) == 0 { 172 if opts.Globals.TestMode { 173 opts.Blocks = []string{"17000000"} 174 } else { 175 opts.Blocks = []string{"latest"} 176 } 177 } 178 // EXISTING_CODE 179 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 180 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 181 opts.Globals.Format = defFmt 182 } 183 184 return opts 185 } 186 187 func GetOptions() *TokensOptions { 188 // EXISTING_CODE 189 // EXISTING_CODE 190 return &defaultTokensOptions 191 } 192 193 func getCaps() caps.Capability { 194 var capabilities caps.Capability // capabilities for chifra tokens 195 capabilities = capabilities.Add(caps.Default) 196 capabilities = capabilities.Add(caps.Caching) 197 capabilities = capabilities.Add(caps.Names) 198 // EXISTING_CODE 199 // EXISTING_CODE 200 return capabilities 201 } 202 203 func ResetOptions(testMode bool) { 204 // We want to keep writer between command file calls 205 w := GetOptions().Globals.Writer 206 opts := TokensOptions{} 207 globals.SetDefaults(&opts.Globals) 208 opts.Globals.TestMode = testMode 209 opts.Globals.Writer = w 210 opts.Globals.Caps = getCaps() 211 defaultTokensOptions = opts 212 } 213 214 func (opts *TokensOptions) getCaches() (caches map[walk.CacheType]bool) { 215 // EXISTING_CODE 216 caches = map[walk.CacheType]bool{ 217 walk.Cache_Tokens: true, 218 } 219 // EXISTING_CODE 220 return 221 } 222 223 // EXISTING_CODE 224 // EXISTING_CODE