Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid a long switch-case statement in Go

Tags:

go

I'm writing a chat bot in Go and wondering how can I avoid a long switch-case statement similar to this one:

switch {

// @bot search me HMAC
case strings.Contains(message, "search me"):
    query := strings.Split(message, "search me ")[1]
    return webSearch(query), "html"

// @bot thesaurus me challenge
case strings.Contains(message, "thesaurus me"):
    query := strings.Split(message, "thesaurus me ")[1]
    return synonyms(query), "html"

Should I define those handlers each in a separate package or should I just use structs and interfaces? Which method will allow me to have a good structure, avoid switch-case and let external developers to easier create handlers?

I think packages will be a better choice but I'm not sure how to register the handlers with the main bot. Would appreciate an example.

like image 613
Maklaus Avatar asked Dec 18 '22 06:12

Maklaus


1 Answers

You could use a map[string]command similar to how the net/http package registers handlers. Something akin to this:

https://play.golang.org/p/9YzHyLodAQ

package main

import (
    "fmt"
    "errors"
)

type BotFunc func(string) (string, error)

type BotMap map[string]BotFunc

var Bot = BotMap{}

func (b BotMap) RegisterCommand(command string, f BotFunc) error {
    if _, exists := b[command]; exists {
        return errors.New("command already exists")
    }
    b[command] = f
    return nil
}

func (b BotMap) Execute(statement string) (string, error) {
    // parse out command and query however you choose (not this way obviously)
    command := statement[:9]
    query := statement[10:]

    return b.ExecuteQuery(command, query)
}

func (b BotMap) ExecuteQuery(command, query string) (string, error) {
    if com, exists := b[command]; exists {
        return com(query)
    }
    return "", errors.New("command doesn't exist")

}

func main() {
    err := Bot.RegisterCommand("search me", func(query string) (string, error) {
        fmt.Println("search", query)
        return "searched", nil
    })
    if err != nil {
        fmt.Println(err)
        return
    }
    err = Bot.RegisterCommand("thesaurus me", func(query string) (string, error) {
        fmt.Println("thesaurus", query)
        return "thesaurused", nil
    })
    if err != nil {
        fmt.Println(err)
        return
    }

    result, err := Bot.Execute("search me please")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(result)
}

Obviously there's a lot of checks missing here, but this is the basic idea.

like image 89
RayfenWindspear Avatar answered Jan 16 '23 02:01

RayfenWindspear