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
import (
"database/sql"
"log/slog"
"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
}
func ParseFrontMatter(r io.Reader) (map[string]string, error) {
func parseFrontMatter(r io.Reader) (map[string]string, error) {
scanner := bufio.NewScanner(r)
frontMatter := make(map[string]string)
@ -41,13 +41,19 @@ func ParseFrontMatter(r io.Reader) (map[string]string, error) {
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"]
if !ok {
return nil, errors.New("tags not found in front matter")
return nil, nil
}
var tags []string
err := json.Unmarshal([]byte(tagsStr), &tags)
err = json.Unmarshal([]byte(tagsStr), &tags)
if err != nil {
return nil, err
}

View file

@ -7,8 +7,8 @@ import (
"encoding/json"
"os"
"github.com/KristianJBorgwarth/dendrite.daemon/core/files"
"github.com/KristianJBorgwarth/dendrite.daemon/core/frontmatter"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
)
@ -27,26 +27,21 @@ func NewCreateNoteHandler(uow *repositories.UnitOfWork) *CreateNoteHandler {
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
if err := json.Unmarshal(params, &cmd); err != nil {
return nil, h.ReturnError(err)
if err := json.Unmarshal(raw, &cmd); err != nil {
return nil, err
}
data, err := os.ReadFile(cmd.TemplatePath)
data, err := h.getTemplate(cmd.TemplatePath)
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 {
return nil, h.ReturnError(err)
}
tags, err := frontmatter.ExtractTags(feMatter)
if err != nil {
return nil, h.ReturnError(err)
return nil, err
}
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
})
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 {
return &rpc.Error{Code: -1, Message: err.Error()}
func (h *CreateNoteHandler) getTemplate(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

@ -3,10 +3,8 @@ package handlers
import (
"context"
"encoding/json"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
)
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 (
"context"
"encoding/json"
"github.com/KristianJBorgwarth/dendrite.daemon/core/rpc"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
)
type initializeCommand struct {
VaultPath string `json:"vaultPath"`
VaultPath string `json:"vaultPath"`
}
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
if err := json.Unmarshal(raw, &params); err != nil {
return nil, &rpc.Error{
Code: -32602,
Message: "invalid params: " + err.Error(),
}
return nil, err
}
err := persistence.InitializeIndex(params.VaultPath)
if err != nil {
return nil, &rpc.Error{
Code: -1,
Message: "failed to initialize index: " + err.Error(),
}
return nil, err
}
return nil, nil
}

View file

@ -4,7 +4,6 @@ package server
import (
"bufio"
"context"
"database/sql"
"encoding/json"
"fmt"
"io"
@ -14,7 +13,6 @@ import (
)
type Server struct {
Db *sql.DB
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) {
s.handlers[method] = handler
}
@ -58,24 +52,42 @@ func (s *Server) handle(w io.Writer, req rpc.Request) {
handler, ok := s.handlers[req.Method]
if !ok {
s.write(w, rpc.Response{
Jsonrpc: "2.0",
ID: req.ID,
Error: &rpc.Error{Code: -32601, Message: "method not found"},
s.respond(w, req.ID, nil, &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 {
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) {
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) {
// Arrange
uow := repositories.NewUnitOfWork(Fixture.Db)
uow := repositories.NewUnitOfWork(Fixture.DB)
handler := handlers.NewCreateNoteHandler(uow)
@ -30,18 +30,14 @@ func TestCreateNoteHandlerOnSucess(t *testing.T) {
request := CreateTestRequest("createNote", 1, requestParamsBytes)
// Act
response := handler.Handle(Fixture.TestContext, request.Params)
response, err := handler.Handle(Fixture.TestContext, request.Params)
// Assert
if response.Error != nil {
t.Fatalf("expected no error, got: %v", response.Error)
if err != nil {
t.Fatalf("handler returned an error: %v", err)
}
if response.Result == nil {
t.Fatal("expected result, got nil")
}
if *response.ID != 1 {
t.Fatal("expected non-empty ID in response result")
if response != nil {
t.Fatalf("expected response to be nil, got: %v", response)
}
}

View file

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