Skip to content

Commit

Permalink
Questioner interface
Browse files Browse the repository at this point in the history
  • Loading branch information
tjayrush committed Jan 28, 2025
1 parent 8eed677 commit a6cedda
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 205 deletions.
40 changes: 36 additions & 4 deletions app/action_init.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package app

import (
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/types"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/wizard"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -58,9 +62,37 @@ func validOk(msg, value string) error {
return fmt.Errorf(msg+"%w", wizard.ErrValidateMsg)
}

func validSkipNext(msg, value string) error {
if strings.Contains(msg, "%s") {
return fmt.Errorf(msg+"%w", value, wizard.ErrSkipQuestion)
func validSkipNext() error {
return fmt.Errorf("skip next %w", wizard.ErrSkipQuestion)
}

// --------------------------------------------------------
type processFn[T any] func(cfg *types.Config) (string, T, error)

// --------------------------------------------------------
func prepare[T any](q *wizard.Question, fn processFn[T]) (string, error) {
if cfg, ok := q.Screen.Wizard.Backing.(*types.Config); ok {
input, copy, err := fn(cfg)
bytes, _ := json.Marshal(copy)
q.State = string(bytes)
return input, err
}
return "", validContinue()
}

// --------------------------------------------------------
func confirm[T any](q *wizard.Question, fn processFn[T]) (string, error) {
if cfg, ok := q.Screen.Wizard.Backing.(*types.Config); ok {
input, copy, err := fn(cfg)
if !errors.Is(err, wizard.ErrValidate) {
err1 := cfg.WriteToFile(types.GetConfigFnNoCreate())
if err1 != nil {
fmt.Println(colors.Red+"error writing config file: %v", err, colors.Off)
}
}
bytes, _ := json.Marshal(copy)
q.State = string(bytes)
return input, err
}
return fmt.Errorf(msg+"%w", wizard.ErrSkipQuestion)
return "", validContinue()
}
106 changes: 93 additions & 13 deletions app/action_init_chains.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package app

import (
"encoding/json"
"fmt"
"log"
"strings"

"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/types"
"github.com/TrueBlocks/trueblocks-khedra/v2/pkg/wizard"
)

Expand All @@ -11,23 +17,23 @@ func getChainsScreen() wizard.Screen {
cSubtitle := ``
cInstructions := `Type your answers and press enter. ("b"=back, "q"=quit)`
cBody := `
Khedra will index any number of EVM chains, however it requires an
RPC endpoint for each to do so. Fast, dedicated local endpoints are
preferred. Likely, you will get rate limited if you point to a remote
endpoing, but if you do, you may use the Sleep option to slow down
operation. See "help".
Khedra will index any EVM chain. The only requirement is a working RPC
endpoint. You may index more than one chain.
The wizard allows you to enter the name of a chain and then asks you for
an RPC endpoint for that chain. It won't proceed until you provide one.
An Ethereum "mainnet" RPC is required.
You may add chains to the list by typing the chain's name. Remove chains
with "remove <chain>". Or, an easier way is to edit the configuration
file directly by typing "edit". The mainnet chain is required.
The code prefers fast local endpoints, although remote endpoints do work.
If you are rate limited (likely), use the sleep option. See "help".
`
cReplacements := []wizard.Replacement{
{Color: colors.Yellow, Values: []string{cTitle}},
{Color: colors.Green, Values: []string{
"remove <chain>", "Sleep", "mainnet", "\"edit\"",
"sleep", "mainnet", "\"edit\"",
}},
}
cQuestions := []wizard.Question{c0, c1}
cQuestions := []wizard.Questioner{&c0, &c1, &c2}
cStyle := wizard.NewStyle()

return wizard.Screen{
Expand All @@ -43,11 +49,49 @@ file directly by typing "edit". The mainnet chain is required.

// --------------------------------------------------------
func cPrepare(key, input string, q *wizard.Question) (string, error) {
return input, nil
if cfg, ok := q.Screen.Wizard.Backing.(*types.Config); ok {
if key == "mainnet" {
if ch, ok := cfg.Chains[key]; !ok {
ch.Name = key
ch.Enabled = true
cfg.Chains[key] = ch
}

if ch, ok := cfg.Chains[key]; !ok {
log.Fatal("chain not found")
} else {
if !ch.HasValidRpc() {
bytes, _ := json.Marshal(&ch)
q.State = string(bytes)
msg := fmt.Sprintf("no rpcs for chain %s ", key)
return strings.Join(ch.RPCs, ","), fmt.Errorf(msg+"%w", wizard.ErrValidate)
}
}
}
}
return input, validOk("skip - have all rpcs", input)
}

// --------------------------------------------------------
func cValidate(key string, input string, q *wizard.Question) (string, error) {
if _, ok := q.Screen.Wizard.Backing.(*types.Config); ok {
if cfg, ok := q.Screen.Wizard.Backing.(*types.Config); ok {
if key == "mainnet" {
if ch, ok := cfg.Chains[key]; !ok {
log.Fatal("chain not found")
} else {
ch.RPCs = strings.Split(input, ",")
cfg.Chains[key] = ch
if !ch.HasValidRpc() {
bytes, _ := json.Marshal(&ch)
q.State = string(bytes)
msg := fmt.Sprintf("no rpcs for chain %s ", key)
return strings.Join(ch.RPCs, ","), fmt.Errorf(msg+"%w", wizard.ErrValidate)
}
}
}
}
}
return input, nil
}

Expand All @@ -59,6 +103,42 @@ var c0 = wizard.Question{
// --------------------------------------------------------
var c1 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Question: `Which chains do you want to index? (Enter a chain's name
|directly to add chains or "remove <chain>" to remove them.)`,
Question: `Please provide an RPC for Ethereum mainnet?`,
Hint: `Khedra requires an Ethereum mainnet RPC. It needs to read
|state from the Unchained Index smart contract. Type "help" for
|more information.`,
PrepareFn: func(input string, q *wizard.Question) (string, error) {
// qq := ChainQuestion{Question: *q}
return cPrepare("mainnet", input, q)
},
Validate: func(input string, q *wizard.Question) (string, error) {
// qq := ChainQuestion{Question: *q}
return cValidate("mainnet", input, q)
},
Replacements: []wizard.Replacement{
{Color: colors.Green, Values: []string{"\"help\"", "Unchained Index"}},
},
}

// --------------------------------------------------------
var c2 = wizard.Question{
//.....question-|---------|---------|---------|---------|---------|----|65
Question: `Which chains do you want to index?`,
Hint: `Enter a comma separated list of chains to index. The wizard will
|ask you next for RPCs. Enter "chains" to open a large list of
|EVM chains. Use the shortNames from that list to name your
|chains. When you publish your index, others' indexes will
|match (e.g., mainnet, gnosis, optimism, sepolia, etc.)`,
PrepareFn: func(input string, q *wizard.Question) (string, error) {
// qq := ChainQuestion{Question: *q}
return cPrepare("chain", input, q)
},
Validate: func(input string, q *wizard.Question) (string, error) {
// qq := ChainQuestion{Question: *q}
return cValidate("chain", input, q)
},
}

// type ChainQuestion struct {
// wizard.Question
// }
Loading

0 comments on commit a6cedda

Please sign in to comment.