ref(tags): improved frontmatter parse use

This commit is contained in:
Kristian Borgwarth 2026-04-02 13:00:49 +02:00
parent 18c2af7928
commit 7ff9788d95
11 changed files with 131 additions and 89 deletions

View file

@ -1,7 +1,6 @@
package main package main
import ( import (
"database/sql"
"log/slog" "log/slog"
"os" "os"

2
core/files/doc.go Normal file
View file

@ -0,0 +1,2 @@
// Package files provides utilities for working with files and directories.
package files

View file

@ -0,0 +1,19 @@
package files
import "os"
func WriteToFile(path string, data []byte) (filePath string, err error) {
if checkIfFileExists(path) {
return path, nil
}
err = os.WriteFile(path, data, 0o644)
if err != nil {
return "", err
}
return path, nil
}
func checkIfFileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

View file

@ -16,7 +16,7 @@ type FrontMatter struct {
Author string Author string
} }
func ParseFrontMatter(r io.Reader) (map[string]string, error) { func parseFrontMatter(r io.Reader) (map[string]string, error) {
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
frontMatter := make(map[string]string) frontMatter := make(map[string]string)
@ -41,13 +41,19 @@ func ParseFrontMatter(r io.Reader) (map[string]string, error) {
return frontMatter, nil return frontMatter, nil
} }
func ExtractTags(frontMatter map[string]string) ([]string, error) { func ExtractTags(file []byte ) ([]string, error) {
r := bytes.NewReader(file)
frontMatter, err := parseFrontMatter(r)
if err != nil {
return nil, err
}
tagsStr, ok := frontMatter["tags"] tagsStr, ok := frontMatter["tags"]
if !ok { if !ok {
return nil, errors.New("tags not found in front matter") return nil, nil
} }
var tags []string var tags []string
err := json.Unmarshal([]byte(tagsStr), &tags) err = json.Unmarshal([]byte(tagsStr), &tags)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -7,8 +7,8 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"github.com/KristianJBorgwarth/dendrite.daemon/core/files"
"github.com/KristianJBorgwarth/dendrite.daemon/core/frontmatter" "github.com/KristianJBorgwarth/dendrite.daemon/core/frontmatter"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories" "github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
) )
@ -27,26 +27,21 @@ func NewCreateNoteHandler(uow *repositories.UnitOfWork) *CreateNoteHandler {
return &CreateNoteHandler{uow: uow} return &CreateNoteHandler{uow: uow}
} }
func (h CreateNoteHandler) Handle(ctx context.Context, params []byte) (*rpc.Response) { func (h CreateNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
var cmd createNoteCommand var cmd createNoteCommand
if err := json.Unmarshal(params, &cmd); err != nil { if err := json.Unmarshal(raw, &cmd); err != nil {
return nil, h.ReturnError(err) return nil, err
} }
data, err := os.ReadFile(cmd.TemplatePath) data, err := h.getTemplate(cmd.TemplatePath)
if err != nil { if err != nil {
return nil, h.ReturnError(err) return nil, err
} }
feMatter, err := frontmatter.ParseFrontMatter(bytes.NewReader(data)) tags, err := frontmatter.ExtractTags(data)
if err != nil { if err != nil {
return nil, h.ReturnError(err) return nil, err
}
tags, err := frontmatter.ExtractTags(feMatter)
if err != nil {
return nil, h.ReturnError(err)
} }
err = h.uow.Execute(ctx, func(tx *sql.Tx) error { err = h.uow.Execute(ctx, func(tx *sql.Tx) error {
@ -63,14 +58,20 @@ func (h CreateNoteHandler) Handle(ctx context.Context, params []byte) (*rpc.Resp
return nil return nil
}) })
if err != nil { if err != nil {
return nil, h.ReturnError(err) return nil, err
} }
return &rpc.Response{Jsonrpc: "2.0", Result: cmd.Path}, nil return cmd.Path, nil
} }
func (h *CreateNoteHandler) ReturnError(err error) *rpc.Error { func (h *CreateNoteHandler) getTemplate(templatePath string) ([]byte, error) {
return &rpc.Error{Code: -1, Message: err.Error()} if templatePath == "" {
return nil, nil
}
data, err := os.ReadFile(templatePath)
if err != nil {
return nil, err
}
return data, nil
} }

View file

@ -3,10 +3,8 @@ package handlers
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
) )
type Handler interface { type Handler interface {
Handle(ctx context.Context, raw json.RawMessage) (*rpc.Response) Handle(ctx context.Context, raw json.RawMessage) (any, error)
} }

View file

@ -3,36 +3,26 @@ package handlers
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence" "github.com/KristianJBorgwarth/dendrite.daemon/persistence"
) )
type initializeCommand struct { type initializeCommand struct {
VaultPath string `json:"vaultPath"` VaultPath string `json:"vaultPath"`
} }
type InitializeHandler struct{} type InitializeHandler struct{}
func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any, *rpc.Error) { func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
var params initializeCommand var params initializeCommand
if err := json.Unmarshal(raw, &params); err != nil { if err := json.Unmarshal(raw, &params); err != nil {
return nil, &rpc.Error{ return nil, err
Code: -32602,
Message: "invalid params: " + err.Error(),
}
} }
err := persistence.InitializeIndex(params.VaultPath) err := persistence.InitializeIndex(params.VaultPath)
if err != nil { if err != nil {
return nil, &rpc.Error{ return nil, err
Code: -1,
Message: "failed to initialize index: " + err.Error(),
}
} }
return nil, nil return nil, nil
} }

View file

@ -4,7 +4,6 @@ package server
import ( import (
"bufio" "bufio"
"context" "context"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -14,7 +13,6 @@ import (
) )
type Server struct { type Server struct {
Db *sql.DB
handlers map[string]handlers.Handler handlers map[string]handlers.Handler
} }
@ -24,10 +22,6 @@ func NewServer() *Server {
} }
} }
func (s *Server) InitDatabase(db *sql.DB) {
s.Db = db
}
func (s *Server) Register(method string, handler handlers.Handler) { func (s *Server) Register(method string, handler handlers.Handler) {
s.handlers[method] = handler s.handlers[method] = handler
} }
@ -58,24 +52,42 @@ func (s *Server) handle(w io.Writer, req rpc.Request) {
handler, ok := s.handlers[req.Method] handler, ok := s.handlers[req.Method]
if !ok { if !ok {
s.write(w, rpc.Response{ s.respond(w, req.ID, nil, &rpc.Error{
Jsonrpc: "2.0", Code: -32601,
ID: req.ID, Message: "method not found",
Error: &rpc.Error{Code: -32601, Message: "method not found"},
}) })
return return
} }
result := handler.Handle(ctx, req.Params) result, err := handler.Handle(ctx, req.Params)
if err != nil {
s.respond(w, req.ID, nil, &rpc.Error{
Code: -32000,
Message: err.Error(),
})
return
}
if req.ID == nil { if req.ID == nil {
return return
} }
s.write(w, *result) s.respond(w, req.ID, result, nil)
}
func (s *Server) respond(w io.Writer, id *int, result any, err *rpc.Error) {
resultJSON, _ := json.Marshal(result)
resp := rpc.Response{
Jsonrpc: "2.0",
ID: id,
Result: resultJSON,
Error: err,
}
s.write(w, resp)
} }
func (s *Server) write(w io.Writer, resp rpc.Response) { func (s *Server) write(w io.Writer, resp rpc.Response) {
data, _ := json.Marshal(resp) data, _ := json.Marshal(resp)
fmt.Fprintln(w, string(data)) fmt.Fprintln(w, string(data))
} }

View file

@ -0,0 +1,17 @@
package template
import (
"os"
)
func GenerateTemplate(templatePath string) ([]byte, error) {
if templatePath == "" {
return nil, nil
}
data, err := os.ReadFile(templatePath)
if err != nil {
return nil, err
}
return data, nil
}

View file

@ -10,7 +10,7 @@ import (
func TestCreateNoteHandlerOnSucess(t *testing.T) { func TestCreateNoteHandlerOnSucess(t *testing.T) {
// Arrange // Arrange
uow := repositories.NewUnitOfWork(Fixture.Db) uow := repositories.NewUnitOfWork(Fixture.DB)
handler := handlers.NewCreateNoteHandler(uow) handler := handlers.NewCreateNoteHandler(uow)
@ -30,18 +30,14 @@ func TestCreateNoteHandlerOnSucess(t *testing.T) {
request := CreateTestRequest("createNote", 1, requestParamsBytes) request := CreateTestRequest("createNote", 1, requestParamsBytes)
// Act // Act
response := handler.Handle(Fixture.TestContext, request.Params) response, err := handler.Handle(Fixture.TestContext, request.Params)
// Assert // Assert
if response.Error != nil { if err != nil {
t.Fatalf("expected no error, got: %v", response.Error) t.Fatalf("handler returned an error: %v", err)
} }
if response.Result == nil { if response != nil {
t.Fatal("expected result, got nil") t.Fatalf("expected response to be nil, got: %v", response)
}
if *response.ID != 1 {
t.Fatal("expected non-empty ID in response result")
} }
} }

View file

@ -5,44 +5,48 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"os" "os"
"path/filepath"
"testing" "testing"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc" "github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence" "github.com/KristianJBorgwarth/dendrite.daemon/persistence"
_ "modernc.org/sqlite"
) )
type TestFixture struct { type DBFixture struct {
Db *sql.DB DB *sql.DB
DbPath string DBPath string
TestContext context.Context TestContext context.Context
} }
func NewTestFixture() (*TestFixture, error) { func NewDBFixture() *DBFixture {
dbPath := os.TempDir() + "/dendrite_test_vault" vaultPath := os.TempDir()
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, err
}
return &TestFixture{ err := persistence.InitializeIndex(vaultPath)
Db: db,
DbPath: dbPath,
TestContext: context.Background(),
}, nil
}
var Fixture, err = NewTestFixture()
func TestMain(m *testing.M) {
err := persistence.InitializeIndex(Fixture.DbPath)
if err != nil { if err != nil {
panic(err) panic(err)
} }
dbPath := filepath.Join(os.TempDir(), ".index", "index.db")
db, err := sql.Open("sqlite", dbPath)
if err != nil {
panic(err)
}
return &DBFixture{
DB: db,
DBPath: dbPath,
TestContext: context.Background(),
}
}
var Fixture = NewDBFixture()
func TestMain(m *testing.M) {
code := m.Run() code := m.Run()
os.RemoveAll(Fixture.DbPath) os.RemoveAll(Fixture.DBPath)
os.Exit(code) os.Exit(code)
} }
@ -55,5 +59,3 @@ func CreateTestRequest(method string, ID int, params json.RawMessage) *rpc.Reque
Params: params, Params: params,
} }
} }