package main import ( "fmt" "net/http" "net/url" "os" "os/signal" "sort" "strings" "sync" "io" ) type wordsByCount struct { words *map[string]uint keys []string } func newWordsByCount(w *map[string]uint) wordsByCount { keys := make([]string, len(*w)) i := 0 for w, _ := range *w { keys[i] = w i++ } return wordsByCount{words: w, keys: keys} } func (wbc wordsByCount) Len() int { return len(*wbc.words) } func (wbc wordsByCount) Swap(i, j int) { wbc.keys[i], wbc.keys[j] = wbc.keys[j], wbc.keys[i] } func (wbc wordsByCount) Less(i, j int) bool { return (*wbc.words)[wbc.keys[i]] > (*wbc.words)[wbc.keys[j]] } func (wbc wordsByCount) MostCommonWords() []string { sort.Sort(wbc) mcw := make([]string, len(*wbc.words)) for i, p := range wbc.keys { mcw[i] = p } return mcw } type Model struct { lock sync.RWMutex words map[string]uint } func NewModel() Model { return Model{words: make(map[string]uint)} } func (m *Model) AddWords(words []string) { m.lock.Lock() defer m.lock.Unlock() for _, word := range words { v, _ := m.words[word] m.words[word] = v + 1 } } func (m *Model) GetMostCommonPairs(how_many uint) []string { m.lock.RLock() defer m.lock.RUnlock() wbc := newWordsByCount(&m.words) mcw := wbc.MostCommonWords() if how_many >= uint(len(mcw)) { how_many = uint(len(mcw)) } return mcw[:how_many] } type View struct{} func NewView() View { return View{} } func (v View) WordsFromInput(input string) []string { return strings.Fields(input) } func (v View) TableFromRankedPairs(ranked_pairs []string) string { // we're not actually getting pairs, just strings var b strings.Builder for _, s := range ranked_pairs { fmt.Fprintf(&b, "%s\n", s) } return b.String() } func NewHttpController(m Model, v View) *http.ServeMux { controller := http.NewServeMux() controller.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { if req.Method == "GET" { uri, _ := url.Parse(req.RequestURI) q := uri.Query() n, _ := q["n"] var how_many uint if len(n) > 0 { fmt.Sscanf(n[0], "%d", &how_many) } if how_many == 0 { how_many = 10 } most_common_pairs := m.GetMostCommonPairs(how_many) table := v.TableFromRankedPairs(most_common_pairs) strings.NewReader(table + "\n").WriteTo(w) } else if req.Method == "POST" { var body strings.Builder io.Copy(&body,req.Body) words := v.WordsFromInput(body.String()) m.AddWords(words) } }) return controller } func main() { model := NewModel() view := NewView() httpcontroller := NewHttpController(model, view) go http.ListenAndServe("localhost:8080", httpcontroller) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt) <-sigs }