zk/core/note/create.go

144 lines
3.3 KiB
Go
Raw Normal View History

2020-12-28 15:00:48 +00:00
package note
import (
"fmt"
"path/filepath"
"github.com/mickael-menu/zk/core/templ"
2020-12-28 15:00:48 +00:00
"github.com/mickael-menu/zk/core/zk"
"github.com/mickael-menu/zk/util/errors"
"github.com/mickael-menu/zk/util/opt"
"github.com/mickael-menu/zk/util/paths"
"github.com/mickael-menu/zk/util/rand"
)
// CreateOpts holds the options to create a new note.
type CreateOpts struct {
// Parent directory for the new note.
Dir zk.Dir
// Title of the note.
Title opt.String
// Initial content of the note, which will be injected in the template.
Content opt.String
}
2020-12-29 17:13:46 +00:00
// Create generates a new note from the given options.
2021-01-01 17:24:15 +00:00
// Returns the path of the newly created note.
func Create(
opts CreateOpts,
templateLoader templ.Loader,
2021-01-01 17:24:15 +00:00
) (string, error) {
wrap := errors.Wrapperf("new note")
2020-12-28 15:00:48 +00:00
2021-01-01 17:24:15 +00:00
filenameTemplate, err := templateLoader.Load(opts.Dir.Config.FilenameTemplate)
2020-12-28 15:00:48 +00:00
if err != nil {
2021-01-01 17:24:15 +00:00
return "", err
2020-12-28 15:00:48 +00:00
}
2021-01-01 17:24:15 +00:00
var bodyTemplate templ.Renderer = templ.NullRenderer
2021-01-01 17:24:15 +00:00
if templatePath := opts.Dir.Config.BodyTemplatePath.Unwrap(); templatePath != "" {
bodyTemplate, err = templateLoader.LoadFile(templatePath)
if err != nil {
return "", wrap(err)
}
2020-12-28 15:00:48 +00:00
}
2021-01-01 17:24:15 +00:00
createdNote, err := create(opts, createDeps{
filenameTemplate: filenameTemplate,
bodyTemplate: bodyTemplate,
genId: rand.NewIDGenerator(opts.Dir.Config.IDOptions),
validatePath: validatePath,
})
2020-12-28 15:00:48 +00:00
if err != nil {
return "", wrap(err)
}
2021-01-01 17:24:15 +00:00
err = paths.WriteString(createdNote.path, createdNote.content)
if err != nil {
return "", wrap(err)
2020-12-28 15:00:48 +00:00
}
2021-01-01 17:24:15 +00:00
return createdNote.path, nil
}
func validatePath(path string) (bool, error) {
exists, err := paths.Exists(path)
return !exists, err
}
type createdNote struct {
path string
content string
2020-12-28 15:00:48 +00:00
}
// renderContext holds the placeholder values which will be expanded in the templates.
type renderContext struct {
2021-01-01 17:24:15 +00:00
ID string `handlebars:"id"`
2020-12-28 15:00:48 +00:00
Title string
Content string
2021-01-01 17:24:15 +00:00
Dir string
2020-12-28 15:00:48 +00:00
Filename string
FilenameStem string `handlebars:"filename-stem"`
Extra map[string]string
}
2021-01-01 17:24:15 +00:00
type createDeps struct {
filenameTemplate templ.Renderer
bodyTemplate templ.Renderer
2021-01-01 17:24:15 +00:00
genId func() string
validatePath func(path string) (bool, error)
2020-12-29 15:07:12 +00:00
}
2021-01-01 17:24:15 +00:00
func create(
2020-12-29 15:07:12 +00:00
opts CreateOpts,
2021-01-01 17:24:15 +00:00
deps createDeps,
) (*createdNote, error) {
2020-12-29 15:07:12 +00:00
context := renderContext{
Title: opts.Title.OrDefault(opts.Dir.Config.DefaultTitle),
2020-12-29 15:07:12 +00:00
Content: opts.Content.Unwrap(),
2021-01-01 17:24:15 +00:00
Dir: opts.Dir.Name,
Extra: opts.Dir.Config.Extra,
2020-12-29 15:07:12 +00:00
}
2020-12-28 15:00:48 +00:00
path, context, err := genPath(context, opts.Dir, deps)
2021-01-01 17:24:15 +00:00
if err != nil {
return nil, err
}
2020-12-29 15:07:12 +00:00
2021-01-01 17:24:15 +00:00
content, err := deps.bodyTemplate.Render(context)
if err != nil {
return nil, err
}
2020-12-29 15:07:12 +00:00
2021-01-01 17:24:15 +00:00
return &createdNote{path: path, content: content}, nil
}
func genPath(
context renderContext,
dir zk.Dir,
2021-01-01 17:24:15 +00:00
deps createDeps,
) (string, renderContext, error) {
var path string
for i := 0; i < 50; i++ {
context.ID = deps.genId()
filename, err := deps.filenameTemplate.Render(context)
2020-12-29 15:07:12 +00:00
if err != nil {
2021-01-01 17:24:15 +00:00
return "", context, err
2020-12-29 15:07:12 +00:00
}
filename = filename + "." + dir.Config.Extension
path = filepath.Join(dir.Path, filename)
2021-01-01 17:24:15 +00:00
validPath, err := deps.validatePath(path)
if err != nil {
return "", context, err
} else if validPath {
context.Filename = filepath.Base(path)
context.FilenameStem = paths.FilenameStem(path)
return path, context, nil
2020-12-29 17:13:46 +00:00
}
2020-12-28 15:00:48 +00:00
}
2021-01-01 17:24:15 +00:00
return "", context, fmt.Errorf("%v: note already exists", path)
2020-12-28 15:00:48 +00:00
}