Implement GCP Cloud Function
This commit is contained in:
parent
794b873ddd
commit
15d4b66afa
13 changed files with 242686 additions and 38 deletions
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Puzzle Solver
|
||||||
|
|
||||||
|
Match patterns and solve anagrams - handy for crossword fanatics.
|
||||||
|
|
||||||
|
## Standalone Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cloud Function
|
||||||
|
|
||||||
|
To test using the Cloud Functions Framework:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
env FUNCTION_TARGET=WordSearch WORDLIST_BUCKET=word-search-1729-assets \
|
||||||
|
WORDLIST_PATH=data/wordlist.txt LOCAL_ONLY=true go run cmd/main.go
|
||||||
|
|
||||||
|
curl 'http://localhost:8080?mode=anagrams&pattern=idea'
|
||||||
|
```
|
|
@ -10,6 +10,18 @@ import (
|
||||||
|
|
||||||
type DB interface {
|
type DB interface {
|
||||||
FindAnagrams(s string) []string
|
FindAnagrams(s string) []string
|
||||||
|
Add(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HashDBImpl map[string][]string
|
||||||
|
|
||||||
|
func New() HashDBImpl {
|
||||||
|
return make(HashDBImpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db HashDBImpl) Add(s string) {
|
||||||
|
k := toKey(s)
|
||||||
|
db[k] = append(db[k], s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toKey(s string) string {
|
func toKey(s string) string {
|
||||||
|
@ -18,15 +30,11 @@ func toKey(s string) string {
|
||||||
return string(xs)
|
return string(xs)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HashDBImpl map[string][]string
|
|
||||||
|
|
||||||
func Load(r io.Reader) (DB, error) {
|
func Load(r io.Reader) (DB, error) {
|
||||||
db := make(HashDBImpl)
|
db := New()
|
||||||
sc := bufio.NewScanner(r)
|
sc := bufio.NewScanner(r)
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
s := sc.Text()
|
db.Add(sc.Text())
|
||||||
k := toKey(s)
|
|
||||||
db[k] = append(db[k], s)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if err := sc.Err(); err != nil {
|
if err := sc.Err(); err != nil {
|
||||||
|
|
|
@ -3,10 +3,11 @@ div.center {
|
||||||
}
|
}
|
||||||
|
|
||||||
div#results {
|
div#results {
|
||||||
overflow-y: auto;
|
height: 60dvh;
|
||||||
max-height: 70dvh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div#results > ul {
|
div#results > ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
141
cloudfn/cloudfn.go
Normal file
141
cloudfn/cloudfn.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package cloudfn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"cloud.google.com/go/storage"
|
||||||
|
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
|
||||||
|
"github.com/ray1729/puzzle-solver/anagram"
|
||||||
|
"github.com/ray1729/puzzle-solver/match"
|
||||||
|
"github.com/rs/cors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var anagramDB anagram.DB
|
||||||
|
var matchDB match.DB
|
||||||
|
|
||||||
|
func initializeDB(ctx context.Context, bucketName, objectName string) error {
|
||||||
|
client, err := storage.NewClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating storage client: %v", err)
|
||||||
|
}
|
||||||
|
r, err := client.Bucket(bucketName).Object(objectName).NewReader(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening gs://%s/%s: %v", bucketName, objectName, err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
anagramDB = anagram.New()
|
||||||
|
matchDB = match.New()
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
s := sc.Text()
|
||||||
|
anagramDB.Add(s)
|
||||||
|
matchDB.Add(s)
|
||||||
|
}
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return fmt.Errorf("error reading gs://%s/%s: %v", bucketName, objectName, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ctx := context.Background()
|
||||||
|
bucketName := mustGetenv("WORDLIST_BUCKET")
|
||||||
|
objectName := mustGetenv("WORDLIST_PATH")
|
||||||
|
log.Println("Initializing databases")
|
||||||
|
if err := initializeDB(ctx, bucketName, objectName); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var corsHandler = cors.Default()
|
||||||
|
log.Println("Registering HTTP function with the Functions Framework")
|
||||||
|
functions.HTTP("WordSearch", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
corsHandler.ServeHTTP(w, r, handleFormSubmission)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustGetenv(s string) string {
|
||||||
|
v := os.Getenv(s)
|
||||||
|
if len(v) == 0 {
|
||||||
|
panic(fmt.Sprintf("environment variable %s not set", s))
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFormSubmission(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
log.Printf("error parsing form: %v", err)
|
||||||
|
http.Error(w, "error parsing form", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mode := r.Form.Get("mode")
|
||||||
|
pattern := r.Form.Get("pattern")
|
||||||
|
if len(pattern) == 0 {
|
||||||
|
http.Error(w, "Missing pattern", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch mode {
|
||||||
|
case "match":
|
||||||
|
results := matchResults(matchDB, pattern)
|
||||||
|
renderTemplate(w, resultsTmpl, results)
|
||||||
|
case "anagrams":
|
||||||
|
results := anagramResults(anagramDB, pattern)
|
||||||
|
renderTemplate(w, resultsTmpl, results)
|
||||||
|
default:
|
||||||
|
log.Printf("invalid mode: %s", mode)
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid mode: %s", mode), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func anagramResults(db anagram.DB, pattern string) ResultParams {
|
||||||
|
var params ResultParams
|
||||||
|
params.Results = db.FindAnagrams(pattern)
|
||||||
|
if len(params.Results) > 0 {
|
||||||
|
params.Preamble = fmt.Sprintf("Anagrams of %q:", pattern)
|
||||||
|
} else {
|
||||||
|
params.Preamble = fmt.Sprintf("Found no anagrams of %q", pattern)
|
||||||
|
}
|
||||||
|
sort.Slice(params.Results, func(i, j int) bool { return params.Results[i] < params.Results[j] })
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchResults(db match.DB, pattern string) ResultParams {
|
||||||
|
var params ResultParams
|
||||||
|
params.Results = db.FindMatches(pattern)
|
||||||
|
if len(params.Results) > 0 {
|
||||||
|
params.Preamble = fmt.Sprintf("Matches for %q:", pattern)
|
||||||
|
} else {
|
||||||
|
params.Preamble = fmt.Sprintf("Found no matches for %q", pattern)
|
||||||
|
}
|
||||||
|
sort.Slice(params.Results, func(i, j int) bool { return params.Results[i] < params.Results[j] })
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderTemplate(w http.ResponseWriter, t *template.Template, params any) {
|
||||||
|
err := t.Execute(w, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error rendering template %s: %v", t.Name(), err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultParams struct {
|
||||||
|
Preamble string
|
||||||
|
Results []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultsTmpl = template.Must(template.New("results").Parse(`
|
||||||
|
{{ with .Preamble }}
|
||||||
|
<p>{{ . }}</p>
|
||||||
|
{{ end }}
|
||||||
|
<ul>
|
||||||
|
{{ range .Results }}
|
||||||
|
<li>{{.}}</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
`))
|
29
cmd/main.go
Normal file
29
cmd/main.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
// Blank-import the function package so the init() runs
|
||||||
|
"github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
|
||||||
|
_ "github.com/ray1729/puzzle-solver/cloudfn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Use PORT environment variable, or default to 8080.
|
||||||
|
port := "8080"
|
||||||
|
if envPort := os.Getenv("PORT"); envPort != "" {
|
||||||
|
port = envPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, listen on all interfaces. If testing locally, run with
|
||||||
|
// LOCAL_ONLY=true to avoid triggering firewall warnings and
|
||||||
|
// exposing the server outside of your own machine.
|
||||||
|
hostname := ""
|
||||||
|
if localOnly := os.Getenv("LOCAL_ONLY"); localOnly == "true" {
|
||||||
|
hostname = "127.0.0.1"
|
||||||
|
}
|
||||||
|
if err := funcframework.StartHostPort(hostname, port); err != nil {
|
||||||
|
log.Fatalf("funcframework.StartHostPort: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
25
fix-wordlist/main.go
Normal file
25
fix-wordlist/main.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/encoding/charmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dec := charmap.ISO8859_1.NewDecoder()
|
||||||
|
sc := bufio.NewScanner(dec.Reader(os.Stdin))
|
||||||
|
for sc.Scan() {
|
||||||
|
s := strings.TrimSpace(sc.Text())
|
||||||
|
if len(s) > 0 {
|
||||||
|
fmt.Println(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
42
go.mod
42
go.mod
|
@ -2,4 +2,44 @@ module github.com/ray1729/puzzle-solver
|
||||||
|
|
||||||
go 1.21.1
|
go 1.21.1
|
||||||
|
|
||||||
require golang.org/x/text v0.13.0 // indirect
|
require (
|
||||||
|
cloud.google.com/go/storage v1.33.0
|
||||||
|
github.com/GoogleCloudPlatform/functions-framework-go v1.8.0
|
||||||
|
github.com/rs/cors v1.10.1
|
||||||
|
golang.org/x/text v0.13.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.110.4 // indirect
|
||||||
|
cloud.google.com/go/compute v1.20.1 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
cloud.google.com/go/functions v1.15.1 // indirect
|
||||||
|
cloud.google.com/go/iam v1.1.0 // indirect
|
||||||
|
github.com/cloudevents/sdk-go/v2 v2.14.0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.4 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.10 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||||
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
go.uber.org/atomic v1.4.0 // indirect
|
||||||
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
|
go.uber.org/zap v1.10.0 // indirect
|
||||||
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
|
golang.org/x/net v0.12.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
|
google.golang.org/api v0.132.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||||
|
google.golang.org/grpc v1.56.2 // indirect
|
||||||
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
|
)
|
||||||
|
|
25
main.go
25
main.go
|
@ -1,37 +1,40 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ray1729/puzzle-solver/anagram"
|
"github.com/ray1729/puzzle-solver/anagram"
|
||||||
"github.com/ray1729/puzzle-solver/grep"
|
"github.com/ray1729/puzzle-solver/match"
|
||||||
"github.com/ray1729/puzzle-solver/server"
|
"github.com/ray1729/puzzle-solver/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var grepDB grep.DB
|
var matchDB match.DB
|
||||||
var anagramDB anagram.DB
|
var anagramDB anagram.DB
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
f, err := os.Open("/usr/share/dict/british-english-huge")
|
f, err := os.Open("wordlist.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error opening word list: %v", err)
|
log.Fatalf("Error opening word list: %v", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
grepDB, err = grep.Load(f)
|
matchDB = match.New()
|
||||||
if err != nil {
|
anagramDB = anagram.New()
|
||||||
log.Fatalf("Error loading grep database: %v", err)
|
sc := bufio.NewScanner(f)
|
||||||
|
for sc.Scan() {
|
||||||
|
s := sc.Text()
|
||||||
|
matchDB.Add(s)
|
||||||
|
anagramDB.Add(s)
|
||||||
}
|
}
|
||||||
f.Seek(0, 0)
|
if err := sc.Err(); err != nil {
|
||||||
anagramDB, err = anagram.Load(f)
|
log.Fatalf("Error loading databases: %v", err)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error loading anagram database: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := server.New("./assets", grepDB, anagramDB)
|
s := server.New("./assets", matchDB, anagramDB)
|
||||||
address := ":8000"
|
address := ":8000"
|
||||||
log.Printf("Listening on %s", address)
|
log.Printf("Listening on %s", address)
|
||||||
if err := http.ListenAndServe(address, s); err != nil {
|
if err := http.ListenAndServe(address, s); err != nil {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package grep
|
package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -9,6 +9,20 @@ import (
|
||||||
|
|
||||||
type DB interface {
|
type DB interface {
|
||||||
FindMatches(s string) []string
|
FindMatches(s string) []string
|
||||||
|
Add(s string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrefixTreeImpl struct {
|
||||||
|
Root *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() PrefixTreeImpl {
|
||||||
|
return PrefixTreeImpl{Root: &Node{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db PrefixTreeImpl) Add(s string) {
|
||||||
|
xs := util.LowerCaseAlpha(s)
|
||||||
|
db.Root.add(xs, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
@ -17,7 +31,7 @@ type Node struct {
|
||||||
Results []string
|
Results []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Add(xs []byte, s string) {
|
func (n *Node) add(xs []byte, s string) {
|
||||||
if len(xs) == 0 {
|
if len(xs) == 0 {
|
||||||
n.Results = append(n.Results, s)
|
n.Results = append(n.Results, s)
|
||||||
return
|
return
|
||||||
|
@ -34,16 +48,14 @@ func (n *Node) Add(xs []byte, s string) {
|
||||||
child = &Node{Value: x}
|
child = &Node{Value: x}
|
||||||
n.Children = append(n.Children, child)
|
n.Children = append(n.Children, child)
|
||||||
}
|
}
|
||||||
child.Add(xs[1:], s)
|
child.add(xs[1:], s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load(r io.Reader) (DB, error) {
|
func Load(r io.Reader) (DB, error) {
|
||||||
db := &Node{}
|
db := New()
|
||||||
sc := bufio.NewScanner(r)
|
sc := bufio.NewScanner(r)
|
||||||
for sc.Scan() {
|
for sc.Scan() {
|
||||||
s := sc.Text()
|
db.Add(sc.Text())
|
||||||
xs := util.LowerCaseAlpha(s)
|
|
||||||
db.Add(xs, s)
|
|
||||||
}
|
}
|
||||||
if err := sc.Err(); err != nil {
|
if err := sc.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -51,8 +63,8 @@ func Load(r io.Reader) (DB, error) {
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) FindMatches(s string) []string {
|
func (db PrefixTreeImpl) FindMatches(s string) []string {
|
||||||
return n.find(util.LowerCaseAlphaOrDot(s))
|
return db.Root.find(util.LowerCaseAlphaOrDot(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) find(xs []byte) []string {
|
func (n *Node) find(xs []byte) []string {
|
|
@ -8,13 +8,16 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/ray1729/puzzle-solver/anagram"
|
"github.com/ray1729/puzzle-solver/anagram"
|
||||||
"github.com/ray1729/puzzle-solver/grep"
|
"github.com/ray1729/puzzle-solver/match"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(assetsPath string, grepDB grep.DB, anagramDB anagram.DB) http.Handler {
|
func New(assetsPath string, matchDB match.DB, anagramDB anagram.DB) http.Handler {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir(assetsPath))))
|
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir(assetsPath))))
|
||||||
mux.HandleFunc("/", handler(grepDB, anagramDB))
|
mux.HandleFunc("/search", searchHandler(matchDB, anagramDB))
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
renderTemplate(w, home, nil)
|
||||||
|
})
|
||||||
return withRequestLogger(mux)
|
return withRequestLogger(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ func withRequestLogger(h http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(grepDB grep.DB, anagramDB anagram.DB) func(w http.ResponseWriter, r *http.Request) {
|
func searchHandler(matchDB match.DB, anagramDB anagram.DB) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
log.Printf("error parsing form: %v", err)
|
log.Printf("error parsing form: %v", err)
|
||||||
|
@ -34,13 +37,13 @@ func handler(grepDB grep.DB, anagramDB anagram.DB) func(w http.ResponseWriter, r
|
||||||
}
|
}
|
||||||
switch r.Form.Get("mode") {
|
switch r.Form.Get("mode") {
|
||||||
case "match":
|
case "match":
|
||||||
params := matchResults(grepDB, r.Form.Get("pattern"))
|
params := matchResults(matchDB, r.Form.Get("pattern"))
|
||||||
renderTemplate(w, results, params)
|
renderTemplate(w, results, params)
|
||||||
case "anagrams":
|
case "anagrams":
|
||||||
params := anagramResults(anagramDB, r.Form.Get("pattern"))
|
params := anagramResults(anagramDB, r.Form.Get("pattern"))
|
||||||
renderTemplate(w, results, params)
|
renderTemplate(w, results, params)
|
||||||
default:
|
default:
|
||||||
renderTemplate(w, home, nil)
|
renderTemplate(w, results, ResultParams{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +60,7 @@ func anagramResults(db anagram.DB, pattern string) ResultParams {
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchResults(db grep.DB, pattern string) ResultParams {
|
func matchResults(db match.DB, pattern string) ResultParams {
|
||||||
var params ResultParams
|
var params ResultParams
|
||||||
params.Results = db.FindMatches(pattern)
|
params.Results = db.FindMatches(pattern)
|
||||||
if len(params.Results) > 0 {
|
if len(params.Results) > 0 {
|
||||||
|
|
|
@ -19,11 +19,12 @@ var home = template.Must(template.New("home").Parse(`
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<form action="/" method="get" hx-boost="true" hx-target="#results" hx-replace="innerHTML" hx-on::after-request="this.reset()">
|
<form action="/search" method="post" hx-boost="true" hx-target="#results">
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<input type="text" name="pattern" required></input>
|
<input type="text" name="pattern" required></input>
|
||||||
<button name="mode" value="match">Match</button>
|
<button name="mode" value="match">Match</button>
|
||||||
<button name="mode" value="anagrams">Anagrams</button>
|
<button name="mode" value="anagrams">Anagrams</button>
|
||||||
|
<button type="reset">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="results">
|
<div id="results">
|
||||||
|
|
240732
wordlist.txt
Normal file
240732
wordlist.txt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue