diff --git a/dict/main.go b/dict/main.go
index 5234d26..8b4328b 100644
--- a/dict/main.go
+++ b/dict/main.go
@@ -69,11 +69,10 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry {
}
}
-func Search(query string) queryResult {
+func Search(query string) (exactResults []Entry, otherResults []Entry, truncated bool) {
query = strings.TrimSpace(query)
- exactResults := make([]Entry, 0)
- otherResults := make([]Entry, 0)
- truncated := false
+ exactResults = make([]Entry, 0)
+ otherResults = make([]Entry, 0)
count := 0
for _, jmdictEntry := range dict.Entries {
exactMatch := false
@@ -106,8 +105,18 @@ func Search(query string) queryResult {
break
}
}
- return queryResult{
- Query: query,
+ return
+}
+
+type searchTemplateData struct {
+ ExactResults []Entry
+ OtherResults []Entry
+ Truncated bool
+ Count int
+}
+
+func initSearchTemplateData(exactResults []Entry, otherResults []Entry, truncated bool) searchTemplateData {
+ return searchTemplateData{
ExactResults: exactResults,
OtherResults: otherResults,
Truncated: truncated,
@@ -115,26 +124,6 @@ func Search(query string) queryResult {
}
}
-func Lookup(word string) *Entry {
- for _, jmdictEntry := range dict.Entries {
- entry := ParseEntry(jmdictEntry)
- if entry.Kanji == word {
- return &entry
- }
- }
- return nil
-}
-
-type queryResult struct {
- // Fields must be capitalized
- // to be accessible in templates
- Query string
- ExactResults []Entry
- OtherResults []Entry
- Truncated bool
- Count int
-}
-
func main() {
err := LoadDict()
if err != nil {
@@ -144,80 +133,51 @@ func main() {
fmt.Println("JMdict loaded!")
r := mux.NewRouter()
r.HandleFunc("/", httputils.GenerateHandler(
+ "index.html",
func(w http.ResponseWriter, r *http.Request) bool { return true },
- httputils.NewTemplateSet("index.html"),
- func(w http.ResponseWriter, r *http.Request) (string, any) { return "index.html", nil },
+ func(w http.ResponseWriter, r *http.Request) any { return nil },
[]string{http.MethodGet},
))
- rawSearchHandler := func(w http.ResponseWriter, r *http.Request) {
- r.ParseMultipartForm(0)
- q := r.FormValue("q")
- var redirect string
- if q == "" {
- redirect = "/"
- } else {
- redirect = "/search/" + q
- }
- http.Redirect(w, r, redirect, http.StatusMovedPermanently)
+ redirectToHome := func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "/", http.StatusPermanentRedirect)
}
- r.HandleFunc("/search", rawSearchHandler)
- r.HandleFunc("/search/", rawSearchHandler)
+ r.HandleFunc("/search", redirectToHome)
+ r.HandleFunc("/search/", redirectToHome)
r.HandleFunc("/search/{query}", httputils.GenerateHandler(
- // handler whether or not to use template
+ "index.html",
+ func(w http.ResponseWriter, r *http.Request) bool {
+ return true
+ },
+ func(w http.ResponseWriter, r *http.Request) any {
+ query := mux.Vars(r)["query"]
+ return struct {
+ Query string
+ Results searchTemplateData
+ }{
+ Query: query,
+ Results: initSearchTemplateData(Search(query)),
+ }
+ },
+ []string{http.MethodGet},
+ ))
+ r.HandleFunc("/api/search", httputils.GenerateHandler(
+ "search.html",
func(w http.ResponseWriter, r *http.Request) bool {
- // If Accept: applicaiton/json we'll use the template
if r.Header.Get("Accept") != "application/json" {
return true
}
-
- // Otherwise, let's send JSON
- query := mux.Vars(r)["query"]
- result := Search(query)
- jsonBytes, _ := json.Marshal(append(result.ExactResults, result.OtherResults...))
-
w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ r.ParseMultipartForm(0)
+ query := r.FormValue("q")
+ exactResults, otherResults, _ := Search(query)
+ jsonBytes, _ := json.Marshal(append(exactResults, otherResults...))
fmt.Fprint(w, string(jsonBytes))
-
return false
},
- httputils.NewTemplateSet("index.html", "search.html"),
- // template data
- func(w http.ResponseWriter, r *http.Request) (template string, data any) {
- if r.Header.Get("HX-Request") == "" {
- template = "search.html"
- } else {
- template = "search"
- }
- // Only runs if handler returns true
- query := mux.Vars(r)["query"]
- data = Search(query)
- return
- },
- []string{http.MethodGet},
- ))
- rawWordHandler := func(w http.ResponseWriter, r *http.Request) {
- fmt.Println("Redirecting raw word handler")
- http.Redirect(w, r, "/", http.StatusMovedPermanently)
- }
- r.HandleFunc("/word", rawWordHandler)
- r.HandleFunc("/word/", rawWordHandler)
- r.HandleFunc("/word/{word}", httputils.GenerateHandler(
- func(w http.ResponseWriter, r *http.Request) bool { return true },
- // Order matters
- // word.html overrided the results block in index.html
- // so should be loaded second
- httputils.NewTemplateSet("index.html", "word.html"),
- func(w http.ResponseWriter, r *http.Request) (template string, data any) {
- template = "word.html"
- query := mux.Vars(r)["word"]
- data = struct {
- Query any
- Entry *Entry
- }{
- Query: nil,
- Entry: Lookup(query),
- }
- return
+ func(w http.ResponseWriter, r *http.Request) any {
+ r.ParseMultipartForm(0)
+ query := r.FormValue("q")
+ return initSearchTemplateData(Search(query))
},
[]string{http.MethodGet},
))
diff --git a/dict/templates/partials/definition.html b/dict/templates/definition.html
similarity index 100%
rename from dict/templates/partials/definition.html
rename to dict/templates/definition.html
diff --git a/dict/templates/index.html b/dict/templates/index.html
index ea33ea8..ca01362 100644
--- a/dict/templates/index.html
+++ b/dict/templates/index.html
@@ -1,42 +1,33 @@
-{{- define "index" -}}
- {{ block "title" . }}{{ template "sitetitle" . }}{{ end }}
+ jidict
-
+
- {{ block "results" . }}{{ if .Query }}{{ template "search" . }}{{ end }}{{ end }}
+ {{ with .Results }}{{ template "search" . }}{{ end }}
-
-{{- end -}}
-{{- template "index" . -}}
\ No newline at end of file
+
\ No newline at end of file
diff --git a/dict/templates/partials/entry.html b/dict/templates/partials/entry.html
deleted file mode 100644
index 9d74569..0000000
--- a/dict/templates/partials/entry.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{{ define "entry" }}
-
-
- {{- if .Kanji -}}
- {{- .Kanji -}}( {{- .Reading -}} )
- {{- else -}}
- {{- .Reading -}}
- {{- end -}}
-
- {{- $count := len .Definitions -}}
- {{ if eq $count 1 -}}
-
{{- template "definition" (index .Definitions 0) -}}
- {{- else if ne $count 0 -}}
-
- {{- range .Definitions }}
-
- {{ template "definition" . }}
-
- {{- end }}
-
- {{- end }}
-
-{{ end }}
\ No newline at end of file
diff --git a/dict/templates/partials/entryfull.html b/dict/templates/partials/entryfull.html
deleted file mode 100644
index 8488cbc..0000000
--- a/dict/templates/partials/entryfull.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{{ define "entryfull" }}
-
-
- {{- if .Kanji -}}
- {{- .Kanji -}}( {{- .Reading -}} )
- {{- else -}}
- {{- .Reading -}}
- {{- end -}}
-
- {{- $count := len .Definitions -}}
- {{ if eq $count 1 -}}
-
{{- template "definition" (index .Definitions 0) -}}
- {{- else if ne $count 0 -}}
-
- {{- range .Definitions }}
-
- {{ template "definition" . }}
-
- {{- end }}
-
- {{- end }}
-
-{{ end }}
\ No newline at end of file
diff --git a/dict/templates/partials/search.html b/dict/templates/partials/search.html
deleted file mode 100644
index 2c6b809..0000000
--- a/dict/templates/partials/search.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{{- define "search" -}}
-{{ if .Truncated }}Truncated results, showing first {{ .Count }}{{ else }}{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}{{ end }}.
-{{ range .ExactResults -}}
-{{- template "entry" . -}}
-{{- end }}
-{{ if and (ne (len .ExactResults) 0) (ne (len .OtherResults) 0) }} {{ end }}
-{{ range .OtherResults -}}
-{{ template "entry" . }}
-{{- end -}}
-{{- end -}}
-{{- template "search" . -}}
\ No newline at end of file
diff --git a/dict/templates/partials/sitetitle.html b/dict/templates/partials/sitetitle.html
deleted file mode 100644
index 67e6c0a..0000000
--- a/dict/templates/partials/sitetitle.html
+++ /dev/null
@@ -1 +0,0 @@
-{{ define "sitetitle" }}jidict{{ end }}
\ No newline at end of file
diff --git a/dict/templates/search.html b/dict/templates/search.html
index ffa8680..90053b1 100644
--- a/dict/templates/search.html
+++ b/dict/templates/search.html
@@ -1,9 +1,11 @@
-{{- define "title" }}{{ .Query }} search - {{ template "sitetitle" . }}{{- end -}}
-
-{{- define "value" }}{{ .Query }}{{- end -}}
-
-{{- define "results" -}}
-{{- template "entryfull" .Entry -}}
+{{- define "search" -}}
+{{ if .Truncated }}Truncated results, showing first {{ .Count }}{{ else }}{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}{{ end }}.
+{{- range .ExactResults -}}
+{{- template "word" . -}}
+{{- end }}
+
+{{ range .OtherResults -}}
+{{ template "word" . }}
{{- end -}}
-
-{{- template "index" . -}}
\ No newline at end of file
+{{- end -}}
+{{- template "search" . -}}
\ No newline at end of file
diff --git a/dict/templates/word.html b/dict/templates/word.html
index 245c10b..40d56f8 100644
--- a/dict/templates/word.html
+++ b/dict/templates/word.html
@@ -1,5 +1,22 @@
-{{- define "title" }}{{ .Entry.Kanji }} - {{ template "sitetitle" . }}{{- end -}}
-{{- define "results" -}}
-{{- template "entryfull" .Entry -}}
-{{- end -}}
-{{- template "index" . -}}
\ No newline at end of file
+{{ define "word" }}
+
+
+ {{- if .Kanji -}}
+ {{- .Kanji -}}( {{- .Reading -}} )
+ {{- else -}}
+ {{- .Reading -}}
+ {{- end -}}
+
+ {{ if le (len .Definitions) 2 -}}
+
{{- template "definition" (index .Definitions 0) -}}
+ {{- else -}}
+
+ {{- range .Definitions }}
+
+ {{ template "definition" . }}
+
+ {{- end }}
+
+ {{- end }}
+
+{{ end }}
\ No newline at end of file
diff --git a/httputils/handler.go b/httputils/handler.go
index d1f152b..a98c763 100644
--- a/httputils/handler.go
+++ b/httputils/handler.go
@@ -1,21 +1,25 @@
package httputils
import (
- "bytes"
"fmt"
"net/http"
"os"
"path/filepath"
- "reflect"
"strings"
+ "text/template"
"time"
)
type Handler = func(http.ResponseWriter, *http.Request)
-func getPartials() ([]string, map[string]time.Time, error) {
+const templateFolder = "templates"
+
+var templatePaths, templateModTimes, _ = getTemplates()
+var templates *template.Template = template.Must(template.ParseFiles(templatePaths...))
+
+func getTemplates() ([]string, map[string]time.Time, error) {
var modTimes map[string]time.Time = make(map[string]time.Time)
- err := filepath.Walk(partialsFolder, func(path string, info os.FileInfo, err error) error {
+ err := filepath.Walk(templateFolder, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
@@ -33,15 +37,35 @@ func getPartials() ([]string, map[string]time.Time, error) {
return paths, modTimes, err
}
+func reloadTemplateIfModified(path string) {
+ fileInfo, _ := os.Stat(path)
+ modTime := fileInfo.ModTime()
+ if modTime.After(templateModTimes[path]) {
+ fmt.Printf("Reloading template %s...\n", path)
+ templates.ParseFiles(path)
+ templateModTimes[path] = modTime
+ }
+}
+
+func reloadTemplatesIfModified() {
+ for _, path := range templatePaths {
+ reloadTemplateIfModified(path)
+ }
+}
+
const reloadTemplates = true
func GenerateHandler(
+ file string,
handler func(http.ResponseWriter, *http.Request) bool,
- templateSet TemplateSet,
- template func(http.ResponseWriter, *http.Request) (template string, data any),
+ data func(http.ResponseWriter, *http.Request) any,
methods []string,
) Handler {
return func(w http.ResponseWriter, r *http.Request) {
+ // All templates must be reloaded in case of dependencies
+ if reloadTemplates {
+ reloadTemplatesIfModified()
+ }
for _, method := range methods {
if method == r.Method {
goto ok
@@ -51,18 +75,9 @@ func GenerateHandler(
return
ok:
renderTemplate := handler(w, r)
- if renderTemplate {
- file_path, data := template(w, r)
- buf := &bytes.Buffer{}
- err := templateSet.ExecuteTemplate(buf, file_path, reflect.ValueOf(data))
- if err != nil {
- fmt.Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- fmt.Fprint(w, "500 Internal Server Error")
- return
- }
+ if renderTemplate && file != "" {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- fmt.Fprint(w, buf.String())
+ templates.ExecuteTemplate(w, file, data(w, r))
}
}
}
diff --git a/httputils/templates.go b/httputils/templates.go
deleted file mode 100644
index c4e8eb2..0000000
--- a/httputils/templates.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package httputils
-
-import (
- "fmt"
- "io"
- "os"
- "path/filepath"
- "strings"
- "text/template"
- "time"
-)
-
-type TemplateSet struct {
- templates *template.Template
- paths []string
- modTimes map[string]time.Time
-}
-
-func newTemplateSet(partials *TemplateSet, paths ...string) TemplateSet {
- var partialPaths []string
- if partials == nil {
- partialPaths = make([]string, 0)
- } else {
- partialPaths = partials.paths
- }
- allPaths := append(partialPaths, paths...)
- modTimes := make(map[string]time.Time)
- for _, path := range allPaths {
- fileInfo, _ := os.Stat(path)
- modTimes[path] = fileInfo.ModTime()
- }
- templates := template.Must(template.ParseFiles(allPaths...))
- return TemplateSet{
- templates: templates,
- paths: allPaths,
- modTimes: modTimes,
- }
-}
-
-func NewTemplateSet(paths ...string) TemplateSet {
- for i, path := range paths {
- paths[i] = fmt.Sprintf("%s/%s", templateFolder, path)
- }
- return newTemplateSet(&partials, paths...)
-}
-
-func (templateSet *TemplateSet) ExecuteTemplate(wr io.Writer, name string, data any) error {
- templateSet.reloadTemplatesIfModified()
- return templateSet.templates.ExecuteTemplate(wr, name, data)
-}
-
-func (templateSet *TemplateSet) reloadTemplateIfModified(path string) {
- fileInfo, _ := os.Stat(path)
- modTime := fileInfo.ModTime()
- if modTime.After(templateSet.modTimes[path]) {
- fmt.Printf("Reloading template %s...\n", path)
- templateSet.templates.ParseFiles(path)
- templateSet.modTimes[path] = modTime
- }
-}
-
-func (templateSet *TemplateSet) reloadTemplatesIfModified() {
- for _, path := range templateSet.paths {
- templateSet.reloadTemplateIfModified(path)
- }
-}
-
-func getTemplatePathsInDirectory(directory string) (paths []string, err error) {
- paths = make([]string, 0)
- err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if !info.IsDir() && strings.HasSuffix(path, ".html") {
- paths = append(paths, path)
- }
- return nil
- })
- return
-}
-
-const templateFolder = "templates"
-const partialsFolder = templateFolder + "/partials"
-
-var paths, _ = getTemplatePathsInDirectory(partialsFolder)
-var partials = newTemplateSet(nil, paths...)