// TestMethodUsageFlags tests the MethodUsage function ensure it returns the // expected flags and errors. func TestMethodUsageFlags(t *testing.T) { t.Parallel() tests := []struct { name string method string err error flags dcrjson.UsageFlag }{ { name: "unregistered type", method: "bogusmethod", err: dcrjson.Error{Code: dcrjson.ErrUnregisteredMethod}, }, { name: "getblock", method: "getblock", flags: 0, }, { name: "walletpassphrase", method: "walletpassphrase", flags: dcrjson.UFWalletOnly, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { flags, err := dcrjson.MethodUsageFlags(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.(dcrjson.Error).Code if gotErrorCode != test.err.(dcrjson.Error).Code { t.Errorf("Test #%d (%s) mismatched error code "+ "- got %v (%v), want %v", i, test.name, gotErrorCode, err, test.err.(dcrjson.Error).Code) continue } continue } // Ensure flags match the expected value. if flags != test.flags { t.Errorf("Test #%d (%s) mismatched flags - got %v, "+ "want %v", i, test.name, flags, test.flags) continue } } }
// 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 := dcrjson.RegisteredCmdMethods() categorized := make([][]string, numCategories) for _, method := range cmdMethods { flags, err := dcrjson.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 := dcrjson.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&dcrjson.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() } }
func execute(protected *bool, cfg *config, line string, clear *bool) bool { switch line { case "h": fmt.Printf("[h]elp print this message\n") fmt.Printf("[l]ist list all available commands\n") fmt.Printf("[p]rotect toggle protected mode (for passwords)\n") fmt.Printf("[c]lear clear command history\n") fmt.Printf("[q]uit/ctrl+d exit\n") fmt.Printf("Enter commands with arguments to execute them.\n") case "l": fallthrough case "list": listCommands() case "q": fallthrough case "quit": return true case "p": fallthrough case "protect": if *protected { *protected = false return false } *protected = true return false case "c": fallthrough case "clear": *clear = true default: args := strings.Split(line, " ") if len(args) < 1 { usage("No command specified") return false } // Ensure the specified method identifies a valid registered command and // is one of the usable types. listCmdMessageLocal := "Enter [l]ist to list commands" method := args[0] usageFlags, err := dcrjson.MethodUsageFlags(method) if err != nil { fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method) fmt.Fprintln(os.Stderr, listCmdMessageLocal) return false } if usageFlags&unusableFlags != 0 { fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+ "websockets\n", method) fmt.Fprintln(os.Stderr, listCmdMessageLocal) return false } // Convert remaining command line args to a slice of interface values // to be passed along as parameters to new command creation function. // // Since some commands, such as submitblock, can involve data which is // too large for the Operating System to allow as a normal command line // parameter, support using '-' as an argument to allow the argument // to be read from a stdin pipe. bio := bufio.NewReader(os.Stdin) params := make([]interface{}, 0, len(args[1:])) for _, arg := range args[1:] { if arg == "-" { param, err := bio.ReadString('\n') if err != nil && err != io.EOF { fmt.Fprintf(os.Stderr, "Failed to read data "+ "from stdin: %v\n", err) return false } if err == io.EOF && len(param) == 0 { fmt.Fprintln(os.Stderr, "Not enough lines "+ "provided on stdin") return false } param = strings.TrimRight(param, "\r\n") params = append(params, param) continue } params = append(params, arg) } // Attempt to create the appropriate command using the arguments // provided by the user. cmd, err := dcrjson.NewCmd(method, params...) if err != nil { // Show the error along with its error code when it's a // dcrjson.Error as it reallistcally will always be since the // NewCmd function is only supposed to return errors of that // type. if jerr, ok := err.(dcrjson.Error); ok { fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n", method, err, jerr.Code) commandUsage(method) return false } // The error is not a dcrjson.Error and this really should not // happen. Nevertheless, fallback to just showing the error // if it should happen due to a bug in the package. fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err) commandUsage(method) return false } // Marshal the command into a JSON-RPC byte slice in preparation for // sending it to the RPC server. marshalledJSON, err := dcrjson.MarshalCmd(1, cmd) if err != nil { fmt.Fprintln(os.Stderr, err) return false } // Send the JSON-RPC request to the server using the user-specified // connection configuration. result, err := sendPostRequest(marshalledJSON, cfg) if err != nil { fmt.Fprintln(os.Stderr, err) return false } // Choose how to display the result based on its type. strResult := string(result) if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") { var dst bytes.Buffer if err := json.Indent(&dst, result, "", " "); err != nil { fmt.Fprintf(os.Stderr, "Failed to format result: %v\n", err) return false } fmt.Println(dst.String()) return false } else if strings.HasPrefix(strResult, `"`) { var str string if err := json.Unmarshal(result, &str); err != nil { fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v\n", err) return false } fmt.Println(str) return false } else if strResult != "null" { fmt.Println(strResult) } } return false }
func main() { cfg, args, err := loadConfig() if err != nil { os.Exit(1) } if cfg.Terminal { startTerminal(cfg) os.Exit(1) } if len(args) < 1 { usage("No command specified") os.Exit(1) } // Ensure the specified method identifies a valid registered command and // is one of the usable types. method := args[0] usageFlags, err := dcrjson.MethodUsageFlags(method) if err != nil { fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method) fmt.Fprintln(os.Stderr, listCmdMessage) os.Exit(1) } if usageFlags&unusableFlags != 0 { fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+ "websockets\n", method) fmt.Fprintln(os.Stderr, listCmdMessage) os.Exit(1) } // Convert remaining command line args to a slice of interface values // to be passed along as parameters to new command creation function. // // Since some commands, such as submitblock, can involve data which is // too large for the Operating System to allow as a normal command line // parameter, support using '-' as an argument to allow the argument // to be read from a stdin pipe. bio := bufio.NewReader(os.Stdin) params := make([]interface{}, 0, len(args[1:])) for _, arg := range args[1:] { if arg == "-" { param, err := bio.ReadString('\n') if err != nil && err != io.EOF { fmt.Fprintf(os.Stderr, "Failed to read data "+ "from stdin: %v\n", err) os.Exit(1) } if err == io.EOF && len(param) == 0 { fmt.Fprintln(os.Stderr, "Not enough lines "+ "provided on stdin") os.Exit(1) } param = strings.TrimRight(param, "\r\n") params = append(params, param) continue } params = append(params, arg) } // Attempt to create the appropriate command using the arguments // provided by the user. cmd, err := dcrjson.NewCmd(method, params...) if err != nil { // Show the error along with its error code when it's a // dcrjson.Error as it reallistcally will always be since the // NewCmd function is only supposed to return errors of that // type. if jerr, ok := err.(dcrjson.Error); ok { fmt.Fprintf(os.Stderr, "%s command: %v (code: %s)\n", method, err, jerr.Code) commandUsage(method) os.Exit(1) } // The error is not a dcrjson.Error and this really should not // happen. Nevertheless, fallback to just showing the error // if it should happen due to a bug in the package. fmt.Fprintf(os.Stderr, "%s command: %v\n", method, err) commandUsage(method) os.Exit(1) } // Marshal the command into a JSON-RPC byte slice in preparation for // sending it to the RPC server. marshalledJSON, err := dcrjson.MarshalCmd(1, cmd) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } // Send the JSON-RPC request to the server using the user-specified // connection configuration. result, err := sendPostRequest(marshalledJSON, cfg) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } // Choose how to display the result based on its type. strResult := string(result) if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") { var dst bytes.Buffer if err := json.Indent(&dst, result, "", " "); err != nil { fmt.Fprintf(os.Stderr, "Failed to format result: %v", err) os.Exit(1) } fmt.Println(dst.String()) } else if strings.HasPrefix(strResult, `"`) { var str string if err := json.Unmarshal(result, &str); err != nil { fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %v", err) os.Exit(1) } fmt.Println(str) } else if strResult != "null" { fmt.Println(strResult) } }