zk/core/note/index.go

165 lines
4.0 KiB
Go
Raw Normal View History

2021-01-03 16:09:10 +00:00
package note
2021-01-03 13:43:27 +00:00
import (
"crypto/sha256"
"fmt"
"io/ioutil"
"path/filepath"
2021-01-03 13:43:27 +00:00
"strings"
"time"
2021-01-03 16:09:10 +00:00
"github.com/mickael-menu/zk/core/zk"
2021-01-03 13:43:27 +00:00
"github.com/mickael-menu/zk/util"
"github.com/mickael-menu/zk/util/errors"
2021-01-09 12:01:41 +00:00
"github.com/mickael-menu/zk/util/paths"
2021-01-17 11:47:05 +00:00
strutil "github.com/mickael-menu/zk/util/strings"
2021-01-03 13:43:27 +00:00
"gopkg.in/djherbis/times.v1"
)
2021-01-03 16:09:10 +00:00
// Metadata holds information about a particular note.
type Metadata struct {
Path string
Title string
Lead string
Body string
RawContent string
WordCount int
2021-01-25 20:44:44 +00:00
Links []Link
Created time.Time
Modified time.Time
Checksum string
2021-01-10 11:29:57 +00:00
}
2021-01-17 11:47:05 +00:00
// IndexingStats holds metrics about an indexing process.
type IndexingStats struct {
SourceCount int
AddedCount int
ModifiedCount int
RemovedCount int
Duration time.Duration
}
// String implements Stringer
func (s IndexingStats) String() string {
return fmt.Sprintf(`Indexed %d %v in %v
+ %d added
~ %d modified
- %d removed`,
s.SourceCount,
strutil.Pluralize("note", s.SourceCount),
s.Duration.Round(500*time.Millisecond),
s.AddedCount, s.ModifiedCount, s.RemovedCount,
)
}
2021-01-03 16:09:10 +00:00
// Indexer persists the notes index.
2021-01-03 13:43:27 +00:00
type Indexer interface {
2021-01-03 16:09:10 +00:00
// Indexed returns the list of indexed note file metadata.
2021-01-09 12:01:41 +00:00
Indexed() (<-chan paths.Metadata, error)
2021-01-03 16:09:10 +00:00
// Add indexes a new note from its metadata.
2021-01-25 20:44:44 +00:00
Add(metadata Metadata) (int64, error)
2021-01-03 16:09:10 +00:00
// Update updates the metadata of an already indexed note.
Update(metadata Metadata) error
// Remove deletes a note from the index.
Remove(path string) error
2021-01-03 13:43:27 +00:00
}
2021-01-25 20:44:44 +00:00
// Index indexes the content of the notes in the given slip box.
func Index(zk *zk.Zk, force bool, parser Parser, indexer Indexer, logger util.Logger, callback func(change paths.DiffChange)) (IndexingStats, error) {
2021-01-17 10:59:45 +00:00
wrap := errors.Wrapper("indexing failed")
2021-01-03 13:43:27 +00:00
2021-01-17 11:47:05 +00:00
stats := IndexingStats{}
startTime := time.Now()
2021-01-25 20:44:44 +00:00
source := paths.Walk(zk.Path, zk.Config.Extension, logger)
2021-01-03 13:43:27 +00:00
target, err := indexer.Indexed()
if err != nil {
2021-01-17 11:47:05 +00:00
return stats, wrap(err)
2021-01-03 13:43:27 +00:00
}
2021-01-17 11:47:05 +00:00
count, err := paths.Diff(source, target, force, func(change paths.DiffChange) error {
2021-01-17 10:59:45 +00:00
callback(change)
2021-01-03 13:43:27 +00:00
switch change.Kind {
2021-01-09 12:01:41 +00:00
case paths.DiffAdded:
2021-01-17 11:47:05 +00:00
stats.AddedCount += 1
2021-01-25 20:44:44 +00:00
metadata, err := metadata(change.Path, zk, parser)
2021-01-03 13:43:27 +00:00
if err == nil {
2021-01-25 20:44:44 +00:00
_, err = indexer.Add(metadata)
2021-01-03 13:43:27 +00:00
}
logger.Err(err)
2021-01-09 12:01:41 +00:00
case paths.DiffModified:
2021-01-17 11:47:05 +00:00
stats.ModifiedCount += 1
2021-01-25 20:44:44 +00:00
metadata, err := metadata(change.Path, zk, parser)
2021-01-03 13:43:27 +00:00
if err == nil {
err = indexer.Update(metadata)
}
logger.Err(err)
2021-01-09 12:01:41 +00:00
case paths.DiffRemoved:
2021-01-17 11:47:05 +00:00
stats.RemovedCount += 1
2021-01-03 16:09:10 +00:00
err := indexer.Remove(change.Path)
logger.Err(err)
2021-01-03 13:43:27 +00:00
}
return nil
})
2021-01-03 16:09:10 +00:00
2021-01-17 11:47:05 +00:00
stats.SourceCount = count
stats.Duration = time.Since(startTime)
return stats, wrap(err)
2021-01-03 13:43:27 +00:00
}
2021-01-03 16:09:10 +00:00
// metadata retrieves note metadata for the given file.
2021-01-25 20:44:44 +00:00
func metadata(path string, zk *zk.Zk, parser Parser) (Metadata, error) {
2021-01-03 16:09:10 +00:00
metadata := Metadata{
2021-01-25 20:44:44 +00:00
Path: path,
Links: make([]Link, 0),
2021-01-03 13:43:27 +00:00
}
2021-01-25 20:44:44 +00:00
absPath := filepath.Join(zk.Path, path)
content, err := ioutil.ReadFile(absPath)
2021-01-03 13:43:27 +00:00
if err != nil {
return metadata, err
}
contentStr := string(content)
2021-01-16 18:37:10 +00:00
contentParts, err := parser.Parse(contentStr)
if err != nil {
return metadata, err
}
metadata.Title = contentParts.Title.String()
metadata.Lead = contentParts.Lead.String()
2021-01-16 18:37:10 +00:00
metadata.Body = contentParts.Body.String()
metadata.RawContent = contentStr
2021-01-03 13:43:27 +00:00
metadata.WordCount = len(strings.Fields(contentStr))
2021-01-25 20:44:44 +00:00
metadata.Links = make([]Link, 0)
2021-01-03 13:43:27 +00:00
metadata.Checksum = fmt.Sprintf("%x", sha256.Sum256(content))
2021-01-25 20:44:44 +00:00
for _, link := range contentParts.Links {
if !strutil.IsURL(link.Href) {
// Make the href relative to the slip box root.
href := filepath.Join(filepath.Dir(absPath), link.Href)
link.Href, err = zk.RelPath(href)
if err != nil {
return metadata, err
}
}
metadata.Links = append(metadata.Links, link)
}
times, err := times.Stat(absPath)
2021-01-03 13:43:27 +00:00
if err != nil {
return metadata, err
}
2021-01-12 19:33:55 +00:00
metadata.Modified = times.ModTime().UTC()
2021-01-03 13:43:27 +00:00
if times.HasBirthTime() {
2021-01-12 19:33:55 +00:00
metadata.Created = times.BirthTime().UTC()
2021-01-03 13:43:27 +00:00
} else {
2021-01-12 19:33:55 +00:00
metadata.Created = time.Now().UTC()
2021-01-03 13:43:27 +00:00
}
return metadata, nil
}