internal/loader.go (view raw)
1package bible
2
3import (
4 "encoding/json"
5 "os"
6 "strings"
7)
8
9// Bible represents the complete Bible data
10type Bible struct {
11 Metadata struct {
12 Name string `json:"name"`
13 Lang string `json:"lang_short"`
14 } `json:"metadata"`
15 Verses []Verse `json:"verses"`
16
17 // Index for quick lookups
18 byBookChapterVerse map[int]map[int]map[int]*Verse
19}
20
21// LoadBible loads Bible data from a JSON file
22func LoadBible(filePath string, verbose bool) (*Bible, error) {
23 logger := NewVerboseLogger(verbose)
24 logger.Info("Loading Bible data from file")
25 logger.Path(filePath)
26
27 data, err := os.ReadFile(filePath)
28 if err != nil {
29 logger.Warning("Failed to read Bible file: %v", err)
30 return nil, err
31 }
32
33 var bible Bible
34 if err := json.Unmarshal(data, &bible); err != nil {
35 return nil, err
36 }
37
38 bible.buildIndex()
39 logger.Success("Bible data loaded successfully")
40 logger.WithFields(map[string]any{
41 "metadata.name": bible.Metadata.Name,
42 "metadata.lang": bible.Metadata.Lang,
43 "verse_count": len(bible.Verses),
44 }, "Bible metadata")
45
46 return &bible, nil
47}
48
49// buildIndex creates a lookup index for verses
50func (b *Bible) buildIndex() {
51 b.byBookChapterVerse = make(map[int]map[int]map[int]*Verse)
52
53 for i := range b.Verses {
54 verse := &b.Verses[i]
55
56 if b.byBookChapterVerse[verse.Book] == nil {
57 b.byBookChapterVerse[verse.Book] = make(map[int]map[int]*Verse)
58 }
59
60 if b.byBookChapterVerse[verse.Book][verse.Chapter] == nil {
61 b.byBookChapterVerse[verse.Book][verse.Chapter] = make(map[int]*Verse)
62 }
63
64 b.byBookChapterVerse[verse.Book][verse.Chapter][verse.VerseNum] = verse
65 }
66}
67
68// GetVerse retrieves a specific verse
69func (b *Bible) GetVerse(book, chapter, verse int) (*Verse, bool) {
70 if chapterMap, ok := b.byBookChapterVerse[book]; ok {
71 if verseMap, ok := chapterMap[chapter]; ok {
72 if verse, ok := verseMap[verse]; ok {
73 return verse, true
74 }
75 }
76 }
77 return nil, false
78}
79
80// GetChapterText retrieves the full text of a chapter
81func (b *Bible) GetChapterText(book, chapter int) string {
82 if chapterMap, ok := b.byBookChapterVerse[book]; ok {
83 if verseMap, ok := chapterMap[chapter]; ok {
84 var text strings.Builder
85 // We need to get verses in order
86 // Since we don't know the max verse number, we'll iterate
87 // This could be optimized but works for now
88 for i := 1; ; i++ {
89 if verse, ok := verseMap[i]; ok {
90 text.WriteString(verse.Text + " ")
91 } else {
92 break
93 }
94 }
95 return text.String()
96 }
97 }
98 return ""
99}
100
101// CountVersesInChapter returns the number of verses in a chapter
102func (b *Bible) CountVersesInChapter(book, chapter int) int {
103 if chapterMap, ok := b.byBookChapterVerse[book]; ok {
104 if verseMap, ok := chapterMap[chapter]; ok {
105 return len(verseMap)
106 }
107 }
108 return 0
109}
110
111// GetVerseRange retrieves verses in a range
112func (b *Bible) GetVerseRange(book, chapter, firstVerse, lastVerse int) []*Verse {
113 var verses []*Verse
114
115 if chapterMap, ok := b.byBookChapterVerse[book]; ok {
116 if verseMap, ok := chapterMap[chapter]; ok {
117 for i := firstVerse; i <= lastVerse; i++ {
118 if verse, ok := verseMap[i]; ok {
119 verses = append(verses, verse)
120 }
121 }
122 }
123 }
124
125 return verses
126}
127