// rpcUsage returns one-line usage for all support RPC commands. // // This function is safe for concurrent access. func (c *helpCacher) rpcUsage(includeWebsockets bool) (string, error) { c.Lock() defer c.Unlock() // Return the cached usage if it is available. if c.usage != "" { return c.usage, nil } // Generate a list of one-line usage for every command. usageTexts := make([]string, 0, len(rpcHandlers)) for k := range rpcHandlers { usage, err := btcjson.MethodUsageText(k) if err != nil { return "", err } usageTexts = append(usageTexts, usage) } // Include websockets commands if requested. if includeWebsockets { for k := range wsHandlers { usage, err := btcjson.MethodUsageText(k) if err != nil { return "", err } usageTexts = append(usageTexts, usage) } } sort.Sort(sort.StringSlice(usageTexts)) c.usage = strings.Join(usageTexts, "\n") return c.usage, nil }
// TestRPCMethodUsageGeneration ensures that single line usage text can be // generated for every supported request of the RPC server. func TestRPCMethodUsageGeneration(t *testing.T) { needsGenerate := false defer func() { if needsGenerate && !t.Failed() { t.Error("Generated help usages are out of date: run 'go generate'") return } if t.Failed() { t.Log("Regenerate help usage with 'go generate' after fixing") } }() svrMethods := serverMethods() usageStrs := make([]string, 0, len(rpchelp.Methods)) for _, m := range rpchelp.Methods { delete(svrMethods, m.Method) usage, err := btcjson.MethodUsageText(m.Method) if err != nil { t.Errorf("Cannot generate single line usage for method '%s': %v", m.Method, err) } if !t.Failed() { usageStrs = append(usageStrs, usage) } } if !t.Failed() { usages := strings.Join(usageStrs, "\n") needsGenerate = usages != requestUsages } }
func writeUsage() { usageStrs := make([]string, len(rpchelp.Methods)) var err error for i := range rpchelp.Methods { usageStrs[i], err = btcjson.MethodUsageText(rpchelp.Methods[i].Method) if err != nil { log.Fatal(err) } } usages := strings.Join(usageStrs, "\n") writefln("var requestUsages = %q", usages) }
// commandUsage display the usage for a specific command. func commandUsage(method string) { usage, err := btcjson.MethodUsageText(method) if err != nil { // This should never happen since the method was already checked // before calling this function, but be safe. fmt.Fprintln(os.Stderr, "Failed to obtain command usage:", err) return } fmt.Fprintln(os.Stderr, "Usage:") fmt.Fprintf(os.Stderr, " %s\n", usage) }
// listCommands categorizes and lists all of the usable commands along with // their one-line usage. func listCommands() { const ( categoryChain uint8 = iota categoryWallet numCategories ) // Get a list of registered commands and categorize and filter them. cmdMethods := btcjson.RegisteredCmdMethods() categorized := make([][]string, numCategories) for _, method := range cmdMethods { flags, err := btcjson.MethodUsageFlags(method) if err != nil { // This should never happen since the method was just // returned from the package, but be safe. continue } // Skip the commands that aren't usable from this utility. if flags&unusableFlags != 0 { continue } usage, err := btcjson.MethodUsageText(method) if err != nil { // This should never happen since the method was just // returned from the package, but be safe. continue } // Categorize the command based on the usage flags. category := categoryChain if flags&btcjson.UFWalletOnly != 0 { category = categoryWallet } categorized[category] = append(categorized[category], usage) } // Display the command according to their categories. categoryTitles := make([]string, numCategories) categoryTitles[categoryChain] = "Chain Server Commands:" categoryTitles[categoryWallet] = "Wallet Server Commands (--wallet):" for category := uint8(0); category < numCategories; category++ { fmt.Println(categoryTitles[category]) for _, usage := range categorized[category] { fmt.Println(usage) } fmt.Println() } }
// TestMethodUsageText tests the MethodUsageText function ensure it returns the // expected text. func TestMethodUsageText(t *testing.T) { t.Parallel() tests := []struct { name string method string err error expected string }{ { name: "unregistered type", method: "bogusmethod", err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, }, { name: "getblockcount", method: "getblockcount", expected: "getblockcount", }, { name: "getblock", method: "getblock", expected: `getblock "hash" (verbose=true verbosetx=false)`, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { usage, err := btcjson.MethodUsageText(test.method) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ "want %T", i, test.name, err, test.err) continue } if err != nil { gotErrorCode := err.(btcjson.Error).ErrorCode if gotErrorCode != test.err.(btcjson.Error).ErrorCode { t.Errorf("Test #%d (%s) mismatched error code "+ "- got %v (%v), want %v", i, test.name, gotErrorCode, err, test.err.(btcjson.Error).ErrorCode) continue } continue } // Ensure usage matches the expected value. if usage != test.expected { t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ "want %v", i, test.name, usage, test.expected) continue } // Get the usage again to excerise caching. usage, err = btcjson.MethodUsageText(test.method) if err != nil { t.Errorf("Test #%d (%s) unexpected error: %v", i, test.name, err) continue } // Ensure usage still matches the expected value. if usage != test.expected { t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ "want %v", i, test.name, usage, test.expected) continue } } }