summaryrefslogtreecommitdiff
path: root/internal/vocab/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/vocab/reader.go')
-rw-r--r--internal/vocab/reader.go111
1 files changed, 111 insertions, 0 deletions
diff --git a/internal/vocab/reader.go b/internal/vocab/reader.go
new file mode 100644
index 0000000..f1506ba
--- /dev/null
+++ b/internal/vocab/reader.go
@@ -0,0 +1,111 @@
+// Package vocab reads language-agnostic vocabulary files.
+package vocab
+
+import (
+ "fmt"
+ "os"
+ "strings"
+)
+
+// CardType identifies the semantic shape of a vocabulary entry.
+type CardType string
+
+const (
+ // CardTypeTranslation marks a standard word-to-translation entry.
+ CardTypeTranslation CardType = "translation"
+ // CardTypeDefinition marks a same-language word-to-definition entry.
+ CardTypeDefinition CardType = "definition"
+)
+
+// WordEntry represents a vocabulary entry with an optional translation.
+type WordEntry struct {
+ Word string
+ Translation string
+ NeedsTranslation bool
+ CardType CardType
+}
+
+// ReadVocabularyFile reads vocabulary entries from a file and returns the parsed slice.
+func ReadVocabularyFile(filename string) ([]WordEntry, error) {
+ content, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, fmt.Errorf("read vocabulary file: %w", err)
+ }
+
+ normalized := strings.ReplaceAll(string(content), "\r\n", "\n")
+ lines := strings.Split(normalized, "\n")
+ entries := make([]WordEntry, 0, len(lines))
+ for _, line := range lines {
+ if entry := parseLine(line); entry != nil {
+ entries = append(entries, *entry)
+ }
+ }
+
+ if len(entries) == 0 {
+ return nil, nil
+ }
+
+ return entries, nil
+}
+
+func parseLine(line string) *WordEntry {
+ trimmed := strings.TrimSpace(line)
+ if trimmed == "" {
+ return nil
+ }
+
+ if strings.Contains(trimmed, "==") {
+ parts := strings.SplitN(trimmed, "==", 2)
+ if len(parts) != 2 {
+ return nil
+ }
+
+ word := strings.TrimSpace(parts[0])
+ translation := strings.TrimSpace(parts[1])
+ if word == "" || translation == "" {
+ return nil
+ }
+
+ return &WordEntry{
+ Word: word,
+ Translation: translation,
+ NeedsTranslation: false,
+ CardType: CardTypeDefinition,
+ }
+ }
+
+ if strings.Contains(trimmed, "=") {
+ parts := strings.SplitN(trimmed, "=", 2)
+ if len(parts) != 2 {
+ return nil
+ }
+
+ word := strings.TrimSpace(parts[0])
+ translation := strings.TrimSpace(parts[1])
+ if word == "" && translation != "" {
+ return &WordEntry{
+ Word: "",
+ Translation: translation,
+ NeedsTranslation: true,
+ CardType: CardTypeTranslation,
+ }
+ }
+ if word != "" && translation != "" {
+ return &WordEntry{
+ Word: word,
+ Translation: translation,
+ NeedsTranslation: false,
+ CardType: CardTypeTranslation,
+ }
+ }
+
+ return nil
+ }
+
+ return &WordEntry{
+ Word: trimmed,
+ Translation: "",
+ NeedsTranslation: false,
+ CardType: CardTypeTranslation,
+ }
+}