You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
2.4 KiB

package trivia
import (
"encoding/json"
"io"
"math/rand"
"strconv"
"strings"
"sync"
)
// represents the structure of trivia questions stored
// in the trivia.json file
type Question struct {
Question string `json:"question"`
Category string `json:"category"`
Format string `json:"format"`
Choices []string `json:"choices"`
Answer string `json:"answer"`
}
type Questions struct {
Categories []string
M map[string][]Question
lock *sync.RWMutex
}
func (q *Questions) Init() {
q.lock = &sync.RWMutex{}
}
func (q *Questions) Load(r io.Reader) error {
q.lock.Lock()
defer q.lock.Unlock()
if q.M == nil {
q.M = make(map[string][]Question, 0)
}
err := json.NewDecoder(r).Decode(&q.M)
if err != nil {
return err
}
if q.Categories == nil {
q.Categories = make([]string, 0)
}
for key := range q.M {
q.Categories = append(q.Categories, key)
}
return nil
}
func (q *Questions) categoryExists(cat string) bool {
q.lock.RLock()
defer q.lock.RUnlock()
cat = strings.ToLower(cat)
if q.M[cat] == nil || len(q.M[cat]) < 1 {
return false
}
return true
}
func (q *Questions) getRandomCategory() string {
q.lock.RLock()
defer q.lock.RUnlock()
return q.Categories[rand.Int()%len(q.Categories)]
}
// Gets a random question from the category, if specified.
func (q *Questions) GetRandomQuestion(category string) (*Question, int) {
q.lock.RLock()
defer q.lock.RUnlock()
// NOTE: it is okay to call another function that locks the RWMutex here,
// as it will not cause a deadlock since CategoryExists only locks the Read
if category == "" {
category = q.getRandomCategory()
} else if !q.categoryExists(category) {
return nil, 0
}
category = strings.ToLower(category)
qIndex := rand.Int() % len(q.M[category])
return &q.M[category][qIndex], qIndex
}
func (q *Questions) GetQuestionById(id string) *Question {
// get values from question_id
questionSlice := strings.Split(id, "|")
if len(questionSlice) != 2 {
return nil
}
category, indexS := questionSlice[0], questionSlice[1]
category = strings.ToLower(category)
index, err := strconv.Atoi(indexS)
if err != nil {
return nil
}
q.lock.RLock()
defer q.lock.RUnlock()
// ensure category exists
if !q.categoryExists(category) {
return nil
}
// ensure question index is valid
if len(q.M[category]) <= index {
return nil
}
// retrieve question
return &q.M[category][index]
}