diff --git a/core/models/note_tag.go b/core/models/note_tag.go index c7e6090..d9b132f 100644 --- a/core/models/note_tag.go +++ b/core/models/note_tag.go @@ -12,7 +12,7 @@ func NewNoteTag(noteID, tagID string) *NoteTag { } } -func NewNoteTags(noteID string, tagIDs []string) []*NoteTag { +func CreateNoteTags(noteID string, tagIDs []string) []*NoteTag { noteTags := make([]*NoteTag, len(tagIDs)) for i, tagID := range tagIDs { noteTags[i] = NewNoteTag(noteID, tagID) diff --git a/dendrite b/dendrite index a42ce71..a53eca9 100755 Binary files a/dendrite and b/dendrite differ diff --git a/persistence/db_dir.go b/persistence/db_dir.go new file mode 100644 index 0000000..c9ecfd7 --- /dev/null +++ b/persistence/db_dir.go @@ -0,0 +1,19 @@ +package persistence + +import ( + "os" + "path/filepath" +) + +func getIndexDBPath(vaultPath string) (string, error) { + dir := os.Getenv("XDG_DATA_HOME") + if dir == "" { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + dir = filepath.Join(home, ".local", "share") + } + + return filepath.Join(dir, "dendrite", vaultPath), nil +} diff --git a/persistence/db_migrator.go b/persistence/db_migrator.go index 1ddc700..37da0c3 100644 --- a/persistence/db_migrator.go +++ b/persistence/db_migrator.go @@ -13,9 +13,13 @@ import ( var migrationsFS embed.FS func InitializeIndex(vaultPath string) (*sql.DB, error) { - indexDir := filepath.Join(vaultPath, ".index") + indexDir, err := getIndexDBPath(vaultPath) + if err != nil { + slog.Error("failed to get index directory path", "error", err) + return nil, err + } - if err := os.MkdirAll(indexDir, 0755); err != nil { + if err = os.MkdirAll(indexDir, 0755); err != nil { slog.Error("failed to create index directory", "error", err) return nil, err } diff --git a/persistence/services/doc.go b/persistence/services/doc.go new file mode 100644 index 0000000..829c45f --- /dev/null +++ b/persistence/services/doc.go @@ -0,0 +1,2 @@ +// Package peristenceServices provides services for data persistence, including database interactions and storage management. +package services diff --git a/persistence/services/index_rebuilder.go b/persistence/services/index_rebuilder.go new file mode 100644 index 0000000..9bd90de --- /dev/null +++ b/persistence/services/index_rebuilder.go @@ -0,0 +1,116 @@ +package services + +import ( + "database/sql" + "io/fs" + "path/filepath" + + filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling" + "github.com/KristianJBorgwarth/dendrite.daemon/core/models" +) + +type IIndexRebuilder interface { + RebuildIndex(vaultRoot string) error +} + +type IndexRebuilder struct { + db *sql.DB +} + +func (r *IndexRebuilder) RebuildIndex(vaultRoot string) error { + files, err := r.ReadFiles(vaultRoot) + if err != nil { + return err + } + + notes, links, tags, noteTags := r.buildDBModels(files) + + tx, err := r.db.Begin() + if err != nil { + return err + } + + if err = r.wipeIndex(tx); err != nil { + tx.Rollback() + return err + } + + if err = r.InsertData(tx, notes, links, tags, noteTags); err != nil { + tx.Rollback() + return err + } + + tx.Commit() + + return nil +} + +func (r *IndexRebuilder) InsertData( + tx *sql.Tx, + notes []*models.Note, + links []*models.Link, + tags []*models.Tag, + noteTags []*models.NoteTag) error { + panic("not implemented") +} + +func (r *IndexRebuilder) ReadFiles(vault string) ([]*filehandling.File, error) { + var files []*filehandling.File + + err := filepath.WalkDir(vault, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() || filepath.Ext(path) != ".md" { + return nil + } + pendingFile, err := filehandling.ReadFile(path) + if err != nil { + return err + } + + files = append(files, pendingFile) + + return nil + }) + if err != nil { + return nil, err + } + + return files, nil +} + +func (r *IndexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.Note, []*models.Link, []*models.Tag, []*models.NoteTag) { + var notes []*models.Note + var links []*models.Link + tagMap := make(map[string]*models.Tag) + var noteTags []*models.NoteTag + + for _, file := range files { + note := models.CreateNote(file.Path, file.Title, file.Slug) + notes = append(notes, note) + for _, t := range models.CreateTags(file.FrontMatter.Tags) { + tagMap[t.Name()] = t + } + noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...) + links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...) + } + + tags := make([]*models.Tag, 0, len(tagMap)) + for _, tag := range tagMap { + tags = append(tags, tag) + } + + return notes, links, tags, noteTags +} + +func (r *IndexRebuilder) wipeIndex(tx *sql.Tx) error { + cmd := `DELETE FROM notes; + DELETE FROM tags; + DELETE FROM note_tags; + DELETE FROM link;` + + _, err := tx.Exec(cmd) + return err +}