Want to contribute? Fork me on Codeberg.org!
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

187 lines
4.3 KiB

2 years ago
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strings"
2 years ago
"foosoft.net/projects/jmdict"
"git.elnu.com/ElnuDev/jichanorg/httputils"
"github.com/gorilla/mux"
)
var dict jmdict.Jmdict
func LoadDict() error {
const jmdictFile = "JMdict.xml"
reader, err := os.Open(jmdictFile)
if err != nil {
return err
}
dict, _, err = jmdict.LoadJmdict(reader)
if err != nil {
return err
}
return nil
}
type Entry struct {
Kanji string
Reading string
Definitions []Definition
}
type Definition struct {
Definition string
PartOfSpeech []string
2 years ago
}
func ParseEntry(entry jmdict.JmdictEntry) Entry {
kanji := ""
if len(entry.Kanji) > 0 {
kanji = entry.Kanji[0].Expression
}
reading := ""
if len(entry.Readings) > 0 {
reading = entry.Readings[0].Reading
}
var definitions []Definition
definitions = make([]Definition, len(entry.Sense))
for i, sense := range entry.Sense {
definition := sense.Glossary[0].Content
if len(sense.Glossary) > 1 {
for _, glossary := range sense.Glossary[1:] {
definition += "; " + glossary.Content
}
2 years ago
}
definitions[i] = Definition{
Definition: definition,
PartOfSpeech: sense.PartsOfSpeech,
}
2 years ago
}
return Entry{
Kanji: kanji,
Reading: reading,
Definitions: definitions,
}
}
func Search(query string) (exactResults []Entry, otherResults []Entry, truncated bool) {
query = strings.TrimSpace(query)
exactResults = make([]Entry, 0)
otherResults = make([]Entry, 0)
count := 0
2 years ago
for _, jmdictEntry := range dict.Entries {
exactMatch := false
2 years ago
for _, kanji := range jmdictEntry.Kanji {
if kanji.Expression == query {
exactMatch = true
goto match
}
if strings.Contains(kanji.Expression, query) {
2 years ago
goto match
}
}
// TODO: Skip if query contains kanji
2 years ago
for _, reading := range jmdictEntry.Readings {
if strings.Contains(reading.Reading, query) {
2 years ago
goto match
}
}
continue
match:
entry := ParseEntry(jmdictEntry)
if exactMatch {
exactResults = append(exactResults, entry)
} else {
otherResults = append(otherResults, entry)
}
count++
if count >= 500 {
truncated = true
break
}
2 years ago
}
return
2 years ago
}
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,
Count: len(exactResults) + len(otherResults),
}
}
2 years ago
func main() {
err := LoadDict()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("JMdict loaded!")
r := mux.NewRouter()
r.HandleFunc("/", httputils.GenerateHandler(
"index.html",
func(w http.ResponseWriter, r *http.Request) bool { return true },
func(w http.ResponseWriter, r *http.Request) any { return nil },
[]string{http.MethodGet},
))
redirectToHome := func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusPermanentRedirect)
}
r.HandleFunc("/search", redirectToHome)
r.HandleFunc("/search/", redirectToHome)
r.HandleFunc("/search/{query}", httputils.GenerateHandler(
"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},
))
2 years ago
r.HandleFunc("/api/search", httputils.GenerateHandler(
"search.html",
func(w http.ResponseWriter, r *http.Request) bool {
if r.Header.Get("Accept") != "application/json" {
return true
}
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...))
2 years ago
fmt.Fprint(w, string(jsonBytes))
return false
},
func(w http.ResponseWriter, r *http.Request) any {
r.ParseMultipartForm(0)
query := r.FormValue("q")
return initSearchTemplateData(Search(query))
2 years ago
},
[]string{http.MethodGet},
2 years ago
))
r.Handle("/", http.FileServer(http.Dir("static")))
log.Fatal(http.ListenAndServe(":3334", r))
}