Compare commits
10 Commits
92e8e14c08
...
b2a51e65b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2a51e65b0 | ||
|
|
f75dcb3f7a | ||
|
|
d4466598b4 | ||
|
|
3ba3b78afc | ||
|
|
f2e3e6f505 | ||
|
|
1ce120a647 | ||
|
|
bfdeebb7a2 | ||
|
|
39345e5e2a | ||
|
|
bfcec9d1c5 | ||
|
|
3e1f58c7b9 |
@@ -24,7 +24,7 @@ func TestConfigTargets(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"one file": {
|
"one file": {
|
||||||
setup: func(t *testing.T, d string) {
|
setup: func(t *testing.T, d string) {
|
||||||
touch(t, "x")
|
touch(t, path.Join(d, "x"))
|
||||||
},
|
},
|
||||||
given: "x",
|
given: "x",
|
||||||
want: []string{"x"},
|
want: []string{"x"},
|
||||||
|
|||||||
104
cmd/dump.go
104
cmd/dump.go
@@ -1,13 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gogs.inhome.blapointe.com/bel/pttodo/pttodo"
|
"gogs.inhome.blapointe.com/bel/pttodo/pttodo"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
@@ -18,103 +13,32 @@ func dump(config config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _dump(writer io.Writer, filepaths []string, tags []string, search, rootDisplay string) error {
|
func _dump(writer io.Writer, filepaths []string, tags []string, search, rootDisplay string) error {
|
||||||
var root pttodo.Root
|
root, err := pttodo.NewRootFromFiles(filepaths...)
|
||||||
|
if err != nil {
|
||||||
for _, p := range filepaths {
|
return err
|
||||||
results, err := filepath.Glob(p + ".*")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, result := range results {
|
|
||||||
if result == p {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filepaths = append(filepaths, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, filepath := range filepaths {
|
for _, x := range []*[]pttodo.Todo{
|
||||||
reader, err := filePathReader(filepath)
|
&root.Todo,
|
||||||
if err != nil {
|
&root.Scheduled,
|
||||||
return err
|
&root.Done,
|
||||||
}
|
} {
|
||||||
|
y := pttodo.Todos(*x)
|
||||||
b, err := ioutil.ReadAll(reader)
|
y = y.LikeTags(tags)
|
||||||
if err != nil {
|
y = y.LikeSearch(search)
|
||||||
return err
|
*x = y
|
||||||
}
|
|
||||||
|
|
||||||
var root2, root2post pttodo.Root
|
|
||||||
if err := yaml.Unmarshal(b, &root2); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := yaml.Unmarshal(b, &root2post); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
root2.MoveScheduledToTodo()
|
|
||||||
|
|
||||||
if !root2.Equals(root2post) {
|
|
||||||
log.Printf("refreshing %s", filepath)
|
|
||||||
b3, err := yaml.Marshal(root2)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(filepath, b3, os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//log.Printf("not refreshing %s", filepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
root.MergeIn(root2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root.MoveScheduledToTodo()
|
|
||||||
|
|
||||||
var v interface{} = root
|
var v interface{} = root
|
||||||
switch rootDisplay {
|
switch rootDisplay {
|
||||||
case DUMP_ALL:
|
|
||||||
case DUMP_TODO:
|
case DUMP_TODO:
|
||||||
v = root.Todo
|
v = root.Todo
|
||||||
case DUMP_SCHEDULED:
|
case DUMP_SCHEDULED:
|
||||||
v = root.Scheduled
|
v = root.Scheduled
|
||||||
case DUMP_DONE:
|
case DUMP_DONE:
|
||||||
v = root.Done
|
v = root.Done
|
||||||
}
|
default:
|
||||||
if todos, ok := v.([]pttodo.Todo); ok {
|
|
||||||
if len(tags) > 0 {
|
|
||||||
result := make([]pttodo.Todo, 0, len(todos))
|
|
||||||
for _, todo := range todos {
|
|
||||||
skip := false
|
|
||||||
for _, tag := range tags {
|
|
||||||
positiveTag := strings.TrimLeft(tag, "-")
|
|
||||||
hasTag := strings.Contains(todo.Tags, positiveTag)
|
|
||||||
wantToHaveTag := !strings.HasPrefix(tag, "-")
|
|
||||||
skip = skip || !(hasTag == wantToHaveTag)
|
|
||||||
}
|
|
||||||
if !skip {
|
|
||||||
result = append(result, todo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
todos = result
|
|
||||||
}
|
|
||||||
if len(search) > 0 {
|
|
||||||
result := make([]pttodo.Todo, 0, len(todos))
|
|
||||||
for _, todo := range todos {
|
|
||||||
if strings.Contains(strings.ToLower(fmt.Sprint(todo)), strings.ToLower(search)) {
|
|
||||||
result = append(result, todo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
todos = result
|
|
||||||
}
|
|
||||||
v = todos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b2, err := yaml.Marshal(v)
|
return yaml.NewEncoder(writer).Encode(v)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(writer, "%s\n", b2)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package pttodo
|
package pttodo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
@@ -12,7 +13,24 @@ type Root struct {
|
|||||||
Done []Todo
|
Done []Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewRootFromFiles(p ...string) (Root, error) {
|
||||||
|
var result Root
|
||||||
|
for _, p := range p {
|
||||||
|
subroot, err := NewRootFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return Root{}, err
|
||||||
|
}
|
||||||
|
result.MergeIn(subroot)
|
||||||
|
}
|
||||||
|
result.AutoMove()
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewRootFromFile(p string) (Root, error) {
|
func NewRootFromFile(p string) (Root, error) {
|
||||||
|
if b, err := os.ReadFile(p); err == nil && len(bytes.TrimSpace(b)) == 0 {
|
||||||
|
return Root{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
f, err := os.Open(p)
|
f, err := os.Open(p)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return Root{}, nil
|
return Root{}, nil
|
||||||
@@ -24,7 +42,11 @@ func NewRootFromFile(p string) (Root, error) {
|
|||||||
|
|
||||||
var result Root
|
var result Root
|
||||||
if err := yaml.NewDecoder(f).Decode(&result); err != nil {
|
if err := yaml.NewDecoder(f).Decode(&result); err != nil {
|
||||||
return Root{}, err
|
todos, err2 := NewTodosFromFile(p)
|
||||||
|
if err2 != nil {
|
||||||
|
return Root{}, err
|
||||||
|
}
|
||||||
|
result.Todo = todos
|
||||||
}
|
}
|
||||||
|
|
||||||
result.AutoMove()
|
result.AutoMove()
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -180,3 +182,78 @@ done:
|
|||||||
t.Fatalf("want\n\t%q, got\n\t%q", want, string(got))
|
t.Fatalf("want\n\t%q, got\n\t%q", want, string(got))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRootFromFile(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
given string
|
||||||
|
want Root
|
||||||
|
}{
|
||||||
|
"empty": {},
|
||||||
|
"happy": {
|
||||||
|
given: `{"todo":["a", "b"],"scheduled":["c"], "done":[{"todo": "d"}]}`,
|
||||||
|
want: Root{
|
||||||
|
Todo: []Todo{
|
||||||
|
Todo{Todo: "a"},
|
||||||
|
Todo{Todo: "b"},
|
||||||
|
},
|
||||||
|
Scheduled: []Todo{
|
||||||
|
Todo{Todo: "c"},
|
||||||
|
},
|
||||||
|
Done: []Todo{
|
||||||
|
Todo{Todo: "d"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"todos": {
|
||||||
|
given: `["a", {"todo": "b"}]`,
|
||||||
|
want: Root{
|
||||||
|
Todo: []Todo{
|
||||||
|
{Todo: "a"},
|
||||||
|
{Todo: "b"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
d := t.TempDir()
|
||||||
|
p := path.Join(d, "input.yaml")
|
||||||
|
if err := os.WriteFile(p, []byte(c.given), os.ModePerm); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got, err := NewRootFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) {
|
||||||
|
t.Errorf("want\n\t%+v, got\n\t%+v", c.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRootFromFiles(t *testing.T) {
|
||||||
|
d := t.TempDir()
|
||||||
|
ps := []string{
|
||||||
|
path.Join(d, "a"),
|
||||||
|
path.Join(d, "b"),
|
||||||
|
}
|
||||||
|
os.WriteFile(ps[0], []byte(`["a"]`), os.ModePerm)
|
||||||
|
os.WriteFile(ps[1], []byte(`["b"]`), os.ModePerm)
|
||||||
|
|
||||||
|
got, err := NewRootFromFiles(ps...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
want := Root{
|
||||||
|
Todo: []Todo{
|
||||||
|
{Todo: "a"},
|
||||||
|
{Todo: "b"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", want) {
|
||||||
|
t.Errorf("want\n\t%+v, got \n\t%+v", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
36
pttodo/todos.go
Normal file
36
pttodo/todos.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package pttodo
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type Todos []Todo
|
||||||
|
|
||||||
|
func (todos Todos) LikeSearch(search string) Todos {
|
||||||
|
return todos.Like(func(todo Todo) bool {
|
||||||
|
return strings.Contains(
|
||||||
|
strings.ToLower(todo.Todo),
|
||||||
|
strings.ToLower(search),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todos Todos) LikeTags(tags []string) Todos {
|
||||||
|
return todos.Like(func(todo Todo) bool {
|
||||||
|
matches := true
|
||||||
|
for _, tag := range tags {
|
||||||
|
str := strings.TrimLeft(tag, "-")
|
||||||
|
want := !strings.HasPrefix(tag, "-")
|
||||||
|
matches = matches && strings.Contains(todo.Tags, str) == want
|
||||||
|
}
|
||||||
|
return matches
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todos Todos) Like(like func(Todo) bool) Todos {
|
||||||
|
result := make(Todos, 0)
|
||||||
|
for i := range todos {
|
||||||
|
if like(todos[i]) {
|
||||||
|
result = append(result, todos[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
17
pttodo/todos_test.go
Normal file
17
pttodo/todos_test.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package pttodo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTodosLikeTags(t *testing.T) {
|
||||||
|
todos := Todos{
|
||||||
|
{Todo: "a", Tags: "x"},
|
||||||
|
{Todo: "b", Tags: "x,y"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := todos.LikeTags([]string{"x", "-y"})
|
||||||
|
if len(result) != 1 {
|
||||||
|
t.Error(result)
|
||||||
|
} else if result[0].Todo != "a" {
|
||||||
|
t.Error(result[0].Todo)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user