all repos — bibel @ 963a6d5d678e7b9a92c39e6a1d018000b974d92e

Unnamed repository; edit this file 'description' to name the repository.

internal/bookmark.go (view raw)

  1package bible
  2
  3import (
  4	"fmt"
  5	"os"
  6	"path/filepath"
  7
  8	"github.com/pelletier/go-toml/v2"
  9)
 10
 11const (
 12	defaultBookmarkFile = "bookmark.toml"
 13)
 14
 15// BookmarkManager handles reading and writing bookmarks
 16type BookmarkManager struct {
 17	FilePath string
 18}
 19
 20// NewBookmarkManager creates a new bookmark manager
 21func NewBookmarkManager(filePath string) *BookmarkManager {
 22	if filePath == "" {
 23		filePath = defaultBookmarkFile
 24	}
 25	return &BookmarkManager{FilePath: filePath}
 26}
 27
 28// ReadBookmark reads a bookmark from the file
 29func (bm *BookmarkManager) ReadBookmark() (*Bookmark, error) {
 30	data, err := os.ReadFile(bm.FilePath)
 31	if err != nil {
 32		if os.IsNotExist(err) {
 33			// Return default bookmark if file doesn't exist
 34			return &Bookmark{
 35				Book:        Matthew,
 36				Chapter:     1,
 37				FirstVerse:  1,
 38				SecondVerse: 12,
 39			}, nil
 40		}
 41		return nil, err
 42	}
 43	
 44	var bookmark Bookmark
 45	if err := toml.Unmarshal(data, &bookmark); err != nil {
 46		return nil, fmt.Errorf("failed to parse bookmark file: %w", err)
 47	}
 48	
 49	// Validate bookmark values
 50	if bookmark.Book < Matthew || bookmark.Book > John {
 51		bookmark.Book = Matthew
 52	}
 53	if bookmark.Chapter < 1 {
 54		bookmark.Chapter = 1
 55	}
 56	if bookmark.FirstVerse < 1 {
 57		bookmark.FirstVerse = 1
 58	}
 59	if bookmark.SecondVerse < bookmark.FirstVerse {
 60		bookmark.SecondVerse = bookmark.FirstVerse
 61	}
 62	
 63	return &bookmark, nil
 64}
 65
 66// WriteBookmark writes a bookmark to the file
 67func (bm *BookmarkManager) WriteBookmark(bookmark *Bookmark) error {
 68	data, err := toml.Marshal(bookmark)
 69	if err != nil {
 70		return fmt.Errorf("failed to marshal bookmark: %w", err)
 71	}
 72	
 73	// Create directory if it doesn't exist
 74	dir := filepath.Dir(bm.FilePath)
 75	if dir != "" && dir != "." {
 76		if err := os.MkdirAll(dir, 0755); err != nil {
 77			return fmt.Errorf("failed to create directory: %w", err)
 78		}
 79	}
 80	
 81	if err := os.WriteFile(bm.FilePath, data, 0644); err != nil {
 82		return fmt.Errorf("failed to write bookmark file: %w", err)
 83	}
 84	
 85	return nil
 86}
 87
 88// AdjustForLookahead adjusts the bookmark if less than 12 verses remain after it
 89func (bm *BookmarkManager) AdjustForLookahead(bookmark *Bookmark, bible *Bible) *Bookmark {
 90	versesInChapter := bible.CountVersesInChapter(int(bookmark.Book), bookmark.Chapter)
 91	versesRemaining := versesInChapter - bookmark.SecondVerse
 92	
 93	// If we're not at the end of the chapter and less than 12 verses remain for NEXT snippet
 94	if versesRemaining > 0 && versesRemaining < 12 {
 95		return &Bookmark{
 96			Book:        bookmark.Book,
 97			Chapter:     bookmark.Chapter,
 98			FirstVerse:  bookmark.FirstVerse,
 99			SecondVerse: versesInChapter,
100		}
101	}
102	
103	// Otherwise return the original bookmark
104	return bookmark
105}
106
107// AdvanceBookmark calculates the next bookmark based on the current one
108func (bm *BookmarkManager) AdvanceBookmark(current *Bookmark, bible *Bible) *Bookmark {
109	versesInChapter := bible.CountVersesInChapter(int(current.Book), current.Chapter)
110	
111	// Case 1: We are at the end of the chapter, move to the next chapter
112	if current.SecondVerse >= versesInChapter {
113		nextChapter := current.Chapter + 1
114		
115		// Check if next chapter exists in current book
116		if bible.CountVersesInChapter(int(current.Book), nextChapter) > 0 {
117			return &Bookmark{
118				Book:        current.Book,
119				Chapter:     nextChapter,
120				FirstVerse:  1,
121				SecondVerse: min(12, bible.CountVersesInChapter(int(current.Book), nextChapter)),
122			}
123		}
124		
125		// End of book, move to next book or loop to Matthew
126		nextBookInt := int(current.Book) + 1
127		if nextBookInt > int(John) {
128			nextBookInt = int(Matthew)
129		}
130		
131		nextBook := BookIndex(nextBookInt)
132		return &Bookmark{
133			Book:        nextBook,
134			Chapter:     1,
135			FirstVerse:  1,
136			SecondVerse: min(12, bible.CountVersesInChapter(int(nextBook), 1)),
137		}
138	}
139	
140	// Case 2: We are in the middle of a chapter, advance verses
141	newFirstVerse := current.SecondVerse + 1
142	newSecondVerse := min(newFirstVerse+11, versesInChapter)
143	
144	return &Bookmark{
145		Book:        current.Book,
146		Chapter:     current.Chapter,
147		FirstVerse:  newFirstVerse,
148		SecondVerse: newSecondVerse,
149	}
150}
151
152func min(a, b int) int {
153	if a < b {
154		return a
155	}
156	return b
157}