Move Diff to zk package and add Dir walking

pull/6/head
Mickaël Menu 3 years ago
parent 75d1a143e7
commit b4f2c841eb
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -1,22 +1,5 @@
package note
import (
"time"
)
// Path holds a note path relative to its slip box.
type Path struct {
Dir string
Filename string
}
// FileMetadata holds information about a note file.
type FileMetadata struct {
Path Path
Created time.Time
Modified time.Time
}
// Metadata holds information about a particular note.
type Metadata struct {
Title string

@ -1,4 +1,4 @@
package note
package zk
// DiffChange represents a note change made in a slip box directory.
type DiffChange struct {
@ -20,35 +20,32 @@ const (
//
// Warning: The FileMetadata have to be sorted by their Path for the diffing to
// work properly.
func Diff(source, target <-chan FileMetadata) <-chan DiffChange {
c := make(chan DiffChange)
go func() {
defer close(c)
func Diff(source, target <-chan FileMetadata, callback func(DiffChange) error) error {
var err error
var sourceFile, targetFile FileMetadata
var sourceOpened, targetOpened bool = true, true
pair := diffPair{}
pair := diffPair{}
var sourceFile, targetFile FileMetadata
var sourceOpened, targetOpened bool = true, true
for sourceOpened || targetOpened {
if pair.source == nil {
sourceFile, sourceOpened = <-source
if sourceOpened {
pair.source = &sourceFile
}
}
if pair.target == nil {
targetFile, targetOpened = <-target
if targetOpened {
pair.target = &targetFile
}
for err == nil && (sourceOpened || targetOpened) {
if pair.source == nil {
sourceFile, sourceOpened = <-source
if sourceOpened {
pair.source = &sourceFile
}
change := pair.diff()
if change != nil {
c <- *change
}
if pair.target == nil {
targetFile, targetOpened = <-target
if targetOpened {
pair.target = &targetFile
}
}
}()
return c
change := pair.diff()
if change != nil {
err = callback(*change)
}
}
return err
}
// diffPair holds the current two files to be diffed.

@ -1,6 +1,7 @@
package note
package zk
import (
"errors"
"testing"
"time"
@ -194,9 +195,48 @@ func TestDiffEmptyTarget(t *testing.T) {
})
}
func TestDiffCancellation(t *testing.T) {
source := []FileMetadata{
{
Path: Path{Dir: "a", Filename: "1"},
Modified: date1,
},
{
Path: Path{Dir: "a", Filename: "2"},
Modified: date2,
},
}
target := []FileMetadata{}
received := make([]DiffChange, 0)
err := Diff(toChannel(source), toChannel(target), func(change DiffChange) error {
received = append(received, change)
if len(received) == 1 {
return errors.New("cancelled")
} else {
return nil
}
})
assert.Equal(t, received, []DiffChange{
{
Path: Path{Dir: "a", Filename: "1"},
Kind: DiffAdded,
},
})
assert.Err(t, err, "cancelled")
}
func test(t *testing.T, source, target []FileMetadata, expected []DiffChange) {
actual := toSlice(Diff(toChannel(source), toChannel(target)))
assert.Equal(t, actual, expected)
received := make([]DiffChange, 0)
err := Diff(toChannel(source), toChannel(target), func(change DiffChange) error {
received = append(received, change)
return nil
})
assert.Nil(t, err)
assert.Equal(t, received, expected)
}
func toChannel(fm []FileMetadata) <-chan FileMetadata {
@ -209,11 +249,3 @@ func toChannel(fm []FileMetadata) <-chan FileMetadata {
}()
return c
}
func toSlice(c <-chan DiffChange) []DiffChange {
s := make([]DiffChange, 0)
for i := range c {
s = append(s, i)
}
return s
}

@ -0,0 +1,82 @@
package zk
import (
"os"
"path/filepath"
"strings"
"time"
"github.com/mickael-menu/zk/util"
)
// Dir represents a directory inside a slip box.
type Dir struct {
// Name of the directory, which is the path relative to the slip box's root.
Name string
// Absolute path to the directory.
Path string
// User configuration for this directory.
Config DirConfig
}
// FileMetadata holds information about a note file.
type FileMetadata struct {
Path Path
Modified time.Time
}
// Walk emits the metadata of each note stored in the directory.
func (d Dir) Walk(logger util.Logger) <-chan FileMetadata {
c := make(chan FileMetadata, 50)
go func() {
defer close(c)
err := filepath.Walk(d.Path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
filename := info.Name()
isHidden := strings.HasPrefix(filename, ".")
if info.IsDir() {
if isHidden {
return filepath.SkipDir
}
} else {
// FIXME: Customize extension in config
if isHidden || filepath.Ext(filename) != ".md" {
return nil
}
path, err := filepath.Rel(d.Path, path)
if err != nil {
logger.Println(err)
return nil
}
curDir := filepath.Dir(path)
if curDir == "." {
curDir = ""
}
c <- FileMetadata{
Path: Path{
Dir: filepath.Join(d.Name, curDir),
Filename: filename,
},
Modified: info.ModTime(),
}
}
return nil
})
if err != nil {
logger.Println(err)
}
}()
return c
}

@ -0,0 +1,87 @@
package zk
import (
"path/filepath"
"testing"
"time"
"github.com/mickael-menu/zk/util"
"github.com/mickael-menu/zk/util/assert"
"github.com/mickael-menu/zk/util/fixtures"
)
var root = fixtures.Path("walk")
func TestWalkRootDir(t *testing.T) {
dir := Dir{Name: "", Path: root}
res := toSlice(dir.Walk(&util.NullLogger))
assert.Equal(t, res, []FileMetadata{
{
Path: Path{Dir: "", Filename: "a.md"},
Modified: date("2021-01-03T11:30:26.069257899+01:00"),
},
{
Path: Path{Dir: "", Filename: "b.md"},
Modified: date("2021-01-03T11:30:27.545667767+01:00"),
},
{
Path: Path{Dir: "dir1", Filename: "a.md"},
Modified: date("2021-01-03T11:31:18.961628888+01:00"),
},
{
Path: Path{Dir: "dir1", Filename: "b.md"},
Modified: date("2021-01-03T11:31:24.692881103+01:00"),
},
{
Path: Path{Dir: "dir1/dir1", Filename: "a.md"},
Modified: date("2021-01-03T11:31:27.900472856+01:00"),
},
{
Path: Path{Dir: "dir2", Filename: "a.md"},
Modified: date("2021-01-03T11:31:51.001827456+01:00"),
},
})
}
func TestWalkSubDir(t *testing.T) {
dir := Dir{Name: "dir1", Path: filepath.Join(root, "dir1")}
res := toSlice(dir.Walk(&util.NullLogger))
assert.Equal(t, res, []FileMetadata{
{
Path: Path{Dir: "dir1", Filename: "a.md"},
Modified: date("2021-01-03T11:31:18.961628888+01:00"),
},
{
Path: Path{Dir: "dir1", Filename: "b.md"},
Modified: date("2021-01-03T11:31:24.692881103+01:00"),
},
{
Path: Path{Dir: "dir1/dir1", Filename: "a.md"},
Modified: date("2021-01-03T11:31:27.900472856+01:00"),
},
})
}
func TestWalkSubSubDir(t *testing.T) {
dir := Dir{Name: "dir1/dir1", Path: filepath.Join(root, "dir1/dir1")}
res := toSlice(dir.Walk(&util.NullLogger))
assert.Equal(t, res, []FileMetadata{
{
Path: Path{Dir: "dir1/dir1", Filename: "a.md"},
Modified: date("2021-01-03T11:31:27.900472856+01:00"),
},
})
}
func date(s string) time.Time {
date, _ := time.Parse(time.RFC3339, s)
return date
}
func toSlice(c <-chan FileMetadata) []FileMetadata {
s := make([]FileMetadata, 0)
for fm := range c {
s = append(s, fm)
}
return s
}

@ -24,14 +24,10 @@ type Zk struct {
Config Config
}
// Dir represents a directory inside a slip box.
type Dir struct {
// Name of the directory, which is the path relative to the slip box's root.
Name string
// Absolute path to the directory.
Path string
// User configuration for this directory.
Config DirConfig
// Path holds a file path relative to a slip box.
type Path struct {
Dir string
Filename string
}
// Open locates a slip box at the given path and parses its configuration.

@ -38,7 +38,10 @@ func toJSON(t *testing.T, obj interface{}) string {
}
func Err(t *testing.T, err error, expected string) {
if !strings.Contains(err.Error(), expected) {
switch {
case err == nil:
t.Errorf("Expected error `%v`, received nil", expected)
case !strings.Contains(err.Error(), expected):
t.Errorf("Expected error `%v`, received `%v`", expected, err.Error())
}
}

Loading…
Cancel
Save