Skip to content

Instantly share code, notes, and snippets.

@3m3x
Last active December 16, 2019 04:24
Show Gist options
  • Save 3m3x/f586c6bbaf5528af3584254947affc03 to your computer and use it in GitHub Desktop.
Save 3m3x/f586c6bbaf5528af3584254947affc03 to your computer and use it in GitHub Desktop.
Poor-man's CLI in Go
/*
Objective:
- to dynamically and arbitrarily call functions of varying signature
- to look up help info stored against a function
Example:
- given the command "func1" and parameter "abc"
- call the function by that name (if present), passing in "abc"
- given a non-existent function name, return an error message
- given command "?"
- display the documentation for each function
References:
https://stackoverflow.com/questions/6769020/go-map-of-functions
https://gobyexample.com/errors
*/
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
/*
Store a function and its description
*/
type command struct {
docstring string
function func(args []string) error
}
/*
Custom error for handling incorrect parameters to a function
*/
type argError struct {
args int
}
func (e *argError) Error() string {
return fmt.Sprintf("error - arguments required: %d\n", e.args)
}
/*
Our function map or "table" of functions
*/
var FUNC_JUMP = map[string]command{
/*
func1 <arg>
*/
"func1": command{
docstring: "func1 <arg1> - does a thingy",
function: func(args []string) error {
if len(args) != 1 {
return &argError{1}
}
fmt.Printf("func1! %v\n", args)
return nil
},
},
/*
func2 <arg1> <arg2>
*/
"func2": command{
docstring: "func2 <arg1> <arg2> - does a thingy",
function: func(args []string) error {
if len(args) != 2 {
return &argError{2}
}
fmt.Printf("func2! %v\n", args)
return nil
},
},
/*
func3
*/
"func3": command{
docstring: "func3 - does a thingy",
function: func(args []string) error {
if len(args) != 0 {
return &argError{0}
}
fmt.Printf("func3! %#v\n", args)
return nil
},
},
}
func printHelp() {
for _, command := range FUNC_JUMP {
fmt.Println(command.docstring)
}
fmt.Println("\nexit - quit the program")
}
func main() {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("> ")
text, _ := reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
// Careful! Split can return an array with an emptry string in it which
// can 'look invisible' when you're debugging it (so use '%#v' in your
// print statements). PSA: your 'empty' array might not be so empty
// afterall.
tokens := strings.Split(text, " ")
if tokens[len(tokens)-1] == "" {
tokens = tokens[:len(tokens)-1] // remove the emptry string
}
cmd := tokens[0]
if cmd == "exit" {
return
} else if cmd == "?" {
printHelp()
continue
} else if _, found := FUNC_JUMP[cmd]; !found {
fmt.Printf("invalid command: %s\n", cmd)
continue
}
f := FUNC_JUMP[cmd].function
args := tokens[1:]
err := f(args)
if err != nil {
fmt.Println(err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment