summaryrefslogtreecommitdiff
path: root/setup.go
diff options
context:
space:
mode:
authorDavid Schlachter <t480-debian-git@schlachter.ca>2026-01-11 23:10:26 -0500
committerDavid Schlachter <t480-debian-git@schlachter.ca>2026-01-11 23:10:26 -0500
commit068ef1f9ac1fe551b97b9d5aec224369ebe015fd (patch)
tree9efc20ddfc2471e2096c0de14203a6e58abdfb57 /setup.go
parent18a0b77981bc1590f558341870f8d35f8aec23c9 (diff)
Move dictionary preparation into the bubbletea app
Diffstat (limited to 'setup.go')
-rw-r--r--setup.go234
1 files changed, 114 insertions, 120 deletions
diff --git a/setup.go b/setup.go
index 23c8bbe..1a69d45 100644
--- a/setup.go
+++ b/setup.go
@@ -5,43 +5,42 @@ import (
"database/sql"
"fmt"
"html/template"
- "log"
"os"
"strings"
+ tea "github.com/charmbracelet/bubbletea"
"github.com/goccy/go-json"
)
-func setupDatabase(rawDictionary string, db *sql.DB) error {
+func setupTables(db *sql.DB) error {
_, err := db.Exec("create table IF NOT EXISTS words (word text not null, definition text);")
if err != nil {
return fmt.Errorf("creating table: %s", err)
}
- row := db.QueryRow(`SELECT count(*) as count from words`)
- var count int
- err = row.Scan(&count)
- if err != nil {
- return fmt.Errorf("counting rows: %s", err)
- }
-
- // Only populate the database if it is empty.
- if count > 0 {
- return nil
- }
-
// Faster import performance.
_, err = db.Exec("PRAGMA synchronous = OFF;")
if err != nil {
return fmt.Errorf("setting risky writes: %s", err)
}
- if err = populateDictionary(rawDictionary, db); err != nil {
- return fmt.Errorf("failed to prepare dictionary: %s", err)
- }
return nil
}
+func isDatabaseEmpty(db *sql.DB) tea.Cmd {
+ return func() tea.Msg {
+ row := db.QueryRow(`SELECT count(*) as count from words`)
+ var count int
+ err := row.Scan(&count)
+ if err != nil {
+ return errMsg(fmt.Errorf("counting rows: %s", err))
+ }
+
+ // Only populate the database if it is empty.
+ return isDBEmptyMsg(count == 0)
+ }
+}
+
type rawDictionaryEntry struct {
Word string `json:"word"`
LangCode string `json:"lang_code"`
@@ -79,131 +78,126 @@ type SenseForDictionaryEntry struct {
Example string
}
-func populateDictionary(rawDictionary string, db *sql.DB) error {
- log.Printf("preparing sqlite database from raw dictionary data...")
-
- // Set up the template
- tmpl, err := template.New("entry").Parse(
- `<p>{{ .Word }} {{ .Sound }} <i>{{ .POS }} {{ .Gender }}</i></p>
+func populateDictionary(rawDictionary string, db *sql.DB) tea.Cmd {
+ return func() tea.Msg {
+ // Set up the template
+ tmpl, err := template.New("entry").Parse(
+ `<p>{{ .Word }} {{ .Sound }} <i>{{ .POS }} {{ .Gender }}</i></p>
<ol>{{ range .Senses}}
<li class=sense>{{ .Sense }}<br>
{{ if .Example }}<ul><li><i>{{ .Example }}</i></li></ul></li>{{ end }}
{{ end }}</ol>
{{ if .Etymology }}<p><i>Étymologie: {{ .Etymology }}</i>{{ end }}`)
- if err != nil {
- panic(err)
- }
+ if err != nil {
+ panic(err)
+ }
- tx, err := db.Begin()
- if err != nil {
- return fmt.Errorf("starting transaction: %w", err)
- }
+ tx, err := db.Begin()
+ if err != nil {
+ return errMsg(fmt.Errorf("starting transaction: %w", err))
+ }
- // Set up a prepared statement
- stmt, err := tx.Prepare("insert into words(word, definition) values(?, ?)")
- if err != nil {
- return fmt.Errorf("preparing statement: %w", err)
- }
- defer stmt.Close()
+ // Set up a prepared statement
+ stmt, err := tx.Prepare("insert into words(word, definition) values(?, ?)")
+ if err != nil {
+ return errMsg(fmt.Errorf("preparing statement: %w", err))
+ }
+ defer stmt.Close()
- file, err := os.Open(rawDictionary)
- if err != nil {
- return fmt.Errorf("opening: %w", err)
- }
- defer file.Close()
+ file, err := os.Open(rawDictionary)
+ if err != nil {
+ return errMsg(fmt.Errorf("opening: %w", err))
+ }
+ defer file.Close()
- var wordsAdded int
- scanner := bufio.NewScanner(file)
+ var wordsAdded int
+ scanner := bufio.NewScanner(file)
- maxCapacity := 2_000_000
- buf := make([]byte, maxCapacity)
- scanner.Buffer(buf, maxCapacity)
+ maxCapacity := 2_000_000
+ buf := make([]byte, maxCapacity)
+ scanner.Buffer(buf, maxCapacity)
- for scanner.Scan() {
- var result rawDictionaryEntry
- json.Unmarshal([]byte(scanner.Text()), &result)
- if result.LangCode != "fr" {
- continue
- }
+ for scanner.Scan() {
+ var result rawDictionaryEntry
+ json.Unmarshal([]byte(scanner.Text()), &result)
+ if result.LangCode != "fr" {
+ continue
+ }
- // Clean up the word. Replace apostrophes (common in phrases) with
- // single quotes (more likely to be typed by a user).
- result.Word = strings.ReplaceAll(result.Word, `’`, `'`)
+ // Clean up the word. Replace apostrophes (common in phrases) with
+ // single quotes (more likely to be typed by a user).
+ result.Word = strings.ReplaceAll(result.Word, `’`, `'`)
- // Create the definition text.
- entry := templateReadyDictionaryEntry{
- Word: result.Word,
- POS: strings.ToLower(result.POS),
- }
- if len(result.Etymology) > 0 {
- entry.Etymology = result.Etymology[0]
- }
- if len(result.Sounds) > 0 {
- entry.Sound = result.Sounds[0].IPA
- }
+ // Create the definition text.
+ entry := templateReadyDictionaryEntry{
+ Word: result.Word,
+ POS: strings.ToLower(result.POS),
+ }
+ if len(result.Etymology) > 0 {
+ entry.Etymology = result.Etymology[0]
+ }
+ if len(result.Sounds) > 0 {
+ entry.Sound = result.Sounds[0].IPA
+ }
- var genders, numbers []string
- for _, r := range result.Tags {
- switch r {
- case "masculine":
- genders = append(genders, "masculin")
- case "feminine":
- genders = append(genders, "féminin")
- case "plural":
- numbers = append(numbers, "pluriel")
- case "singular":
- numbers = append(numbers, "singulier")
+ var genders, numbers []string
+ for _, r := range result.Tags {
+ switch r {
+ case "masculine":
+ genders = append(genders, "masculin")
+ case "feminine":
+ genders = append(genders, "féminin")
+ case "plural":
+ numbers = append(numbers, "pluriel")
+ case "singular":
+ numbers = append(numbers, "singulier")
+ }
}
- }
- entry.Gender = strings.Join(
- []string{
- strings.Join(genders, " / "),
- strings.Join(numbers, " et "),
- },
- " ",
- )
-
- for _, s := range result.Senses {
- var example string
- if len(s.Examples) > 0 {
- example = s.Examples[0].Text
+ entry.Gender = strings.Join(
+ []string{
+ strings.Join(genders, " / "),
+ strings.Join(numbers, " et "),
+ },
+ " ",
+ )
+
+ for _, s := range result.Senses {
+ var example string
+ if len(s.Examples) > 0 {
+ example = s.Examples[0].Text
+ }
+ sense := strings.Join(s.Glosses, "; ")
+ entry.Senses = append(entry.Senses, SenseForDictionaryEntry{Sense: sense, Example: example})
}
- sense := strings.Join(s.Glosses, "; ")
- entry.Senses = append(entry.Senses, SenseForDictionaryEntry{Sense: sense, Example: example})
- }
- formattedDefinition := strings.Builder{}
- err := tmpl.Execute(&formattedDefinition, entry)
- if err != nil {
- return fmt.Errorf("failed to render: %w", err)
- }
+ formattedDefinition := strings.Builder{}
+ err := tmpl.Execute(&formattedDefinition, entry)
+ if err != nil {
+ return errMsg(fmt.Errorf("failed to render: %w", err))
+ }
- // Insert the entry
- _, err = stmt.Exec(entry.Word, formattedDefinition.String())
- if err != nil {
- return fmt.Errorf("inserting '%s': %w", entry.Word, err)
- }
+ // Insert the entry
+ _, err = stmt.Exec(entry.Word, formattedDefinition.String())
+ if err != nil {
+ return errMsg(fmt.Errorf("inserting '%s': %w", entry.Word, err))
+ }
- wordsAdded++
- if wordsAdded%100_000 == 0 && wordsAdded > 1 {
- log.Printf("processed %d lines (most recent word was '%s')", wordsAdded, entry.Word)
+ wordsAdded++
+
+ }
+ if err := scanner.Err(); err != nil {
+ return errMsg(fmt.Errorf("scanning: %w", err))
}
- }
- if err := scanner.Err(); err != nil {
- return fmt.Errorf("scanning: %w", err)
- }
+ if err := tx.Commit(); err != nil {
+ return errMsg(fmt.Errorf("committing: %w", err))
+ }
- if err := tx.Commit(); err != nil {
- return fmt.Errorf("committing: %w", err)
- }
+ _, err = db.Exec("create index wordindex on words(word);")
+ if err != nil {
+ return errMsg(fmt.Errorf("creating index: %s", err))
+ }
- _, err = db.Exec("create index wordindex on words(word);")
- if err != nil {
- return fmt.Errorf("creating index: %s", err)
+ return isDBEmptyMsg(false)
}
-
- log.Printf("prepared %d dictionary entries", wordsAdded)
-
- return nil
}