added search by id, tree slice and filter
This commit is contained in:
parent
d252680e45
commit
8ea36234a9
5 changed files with 160 additions and 69 deletions
12
README.md
12
README.md
|
@ -41,6 +41,8 @@ Meta data separated by lines and in format `* name: value`. By default library s
|
||||||
During populating tree it adds missing `id` in `abcex` format and empty meta data lines and saves in `source` directory then copy
|
During populating tree it adds missing `id` in `abcex` format and empty meta data lines and saves in `source` directory then copy
|
||||||
to `destination` directory.
|
to `destination` directory.
|
||||||
|
|
||||||
|
There is possibility to add custom default value. That is passed with custom function what returns string.
|
||||||
|
|
||||||
### File structure
|
### File structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -54,10 +56,10 @@ Hear goes content. It can be written in html, markdown or whatever what can be p
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Notes
|
## Usage
|
||||||
|
|
||||||
### Future plans
|
There are two main commands: `PopulateTree` to prepare source (format, add metadata and add Id) and copy from source to destination and `BuildTree` to get object of tree.
|
||||||
|
|
||||||
|
`Tree` object has methods: `FileById` to get `File` by Id, `Slice` to get sub-tree of given path and `Filter` to filter tree by filter.
|
||||||
|
Filter contains array of meta key and searching value. `tag` key is searched as equal and other meta values of keys can contain part.
|
||||||
|
|
||||||
* add meta as function, ex. today date
|
|
||||||
* add meta with default value that should be used if other value is not set, ex. license
|
|
||||||
* add search on Tree
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module g.arns.lt/zordsdavini/zord-tree
|
module g.arns.lt/zordsdavini/zord-tree
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
g.arns.lt/zordsdavini/abcex v1.0.0
|
g.arns.lt/zordsdavini/abcex v1.0.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,7 +1,5 @@
|
||||||
g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0E=
|
g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0E=
|
||||||
g.arns.lt/zordsdavini/abcex v1.0.0/go.mod h1:YRcJgts3XZwI+LEkngpfUab3DkUAW387Irpr43hIym8=
|
g.arns.lt/zordsdavini/abcex v1.0.0/go.mod h1:YRcJgts3XZwI+LEkngpfUab3DkUAW387Irpr43hIym8=
|
||||||
github.com/otiai10/copy v1.5.1 h1:a/cs2E1/1V0az8K5nblbl+ymEa4E11AfaOLMar8V34w=
|
|
||||||
github.com/otiai10/copy v1.5.1/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
|
||||||
github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ=
|
github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ=
|
||||||
github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
|
83
tree.go
83
tree.go
|
@ -2,6 +2,7 @@ package zord_tree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"g.arns.lt/zordsdavini/abcex"
|
"g.arns.lt/zordsdavini/abcex"
|
||||||
cp "github.com/otiai10/copy"
|
cp "github.com/otiai10/copy"
|
||||||
|
@ -28,11 +29,79 @@ type Tree struct {
|
||||||
Files []File
|
Files []File
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Tree) FileById(id string) (File, error) {
|
||||||
|
for _, f := range t.Files {
|
||||||
|
if f.Id == id {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t2 := range t.Dirs {
|
||||||
|
f, err := t2.FileById(id)
|
||||||
|
if err == nil {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return File{}, errors.New("file was not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tree) Slice(path string) (Tree, error) {
|
||||||
|
if t.Path == path {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t2 := range t.Dirs {
|
||||||
|
t3, err := t2.Slice(path)
|
||||||
|
if err == nil {
|
||||||
|
return t3, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tree{}, errors.New("tree was not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tree) Filter(filter map[string]string) (Tree, bool) {
|
||||||
|
filtered := Tree{}
|
||||||
|
filtered.Path = t.Path
|
||||||
|
found := false
|
||||||
|
|
||||||
|
for _, f := range t.Files {
|
||||||
|
addFile := false
|
||||||
|
for option, value := range filter {
|
||||||
|
if option == "tag" {
|
||||||
|
for _, tag := range f.Tags {
|
||||||
|
if tag == value {
|
||||||
|
addFile = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(f.Meta[option], value) {
|
||||||
|
addFile = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if addFile {
|
||||||
|
found = true
|
||||||
|
filtered.Files = append(filtered.Files, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t2 := range t.Dirs {
|
||||||
|
filteredChild, foundChild := t2.Filter(filter)
|
||||||
|
if foundChild {
|
||||||
|
found = true
|
||||||
|
filtered.Dirs = append(filtered.Dirs, filteredChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered, found
|
||||||
|
}
|
||||||
|
|
||||||
func BuildTree(dirPath string, meta []string) (Tree, error) {
|
func BuildTree(dirPath string, meta []string) (Tree, error) {
|
||||||
return readPath(dirPath, []string{}, meta)
|
return readPath(dirPath, []string{}, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PopulateTree(sourcePath string, destPath string, meta []string) error {
|
func PopulateTree(sourcePath string, destPath string, meta []string, customMeta map[string]func() string) error {
|
||||||
err := fixFormat(sourcePath)
|
err := fixFormat(sourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -50,7 +119,7 @@ func PopulateTree(sourcePath string, destPath string, meta []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = addMissingMeta(sourcePath, meta)
|
err = addMissingMeta(sourcePath, meta, customMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +179,7 @@ func fixFormat(dir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMissingMeta(dir string, meta []string) error {
|
func addMissingMeta(dir string, meta []string, customMeta map[string]func() string) error {
|
||||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, e error) error {
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, e error) error {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return e
|
||||||
|
@ -138,7 +207,11 @@ func addMissingMeta(dir string, meta []string) error {
|
||||||
if line == "---" {
|
if line == "---" {
|
||||||
for option, process := range check {
|
for option, process := range check {
|
||||||
if !process {
|
if !process {
|
||||||
err = addMeta(path, option, "")
|
defaultValue := ""
|
||||||
|
if _, ok := customMeta[option]; ok {
|
||||||
|
defaultValue = customMeta[option]()
|
||||||
|
}
|
||||||
|
err = addMeta(path, option, defaultValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -247,7 +320,7 @@ func getMaxId(dir string) (int64, error) {
|
||||||
func readPath(dirPath string, category []string, meta []string) (Tree, error) {
|
func readPath(dirPath string, category []string, meta []string) (Tree, error) {
|
||||||
tree := Tree{}
|
tree := Tree{}
|
||||||
|
|
||||||
tree.Path = dirPath
|
tree.Path = strings.TrimPrefix(dirPath, "./")
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dirPath)
|
files, err := ioutil.ReadDir(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
130
tree_test.go
130
tree_test.go
|
@ -8,6 +8,19 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func prepare(t *testing.T) {
|
||||||
|
err := os.Remove("./testdata/sunny1")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
err = os.Remove("./testdata/sunny2")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Couldn't prepare data for testing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFromNotExistingDirectory(t *testing.T) {
|
func TestFromNotExistingDirectory(t *testing.T) {
|
||||||
_, err := BuildTree("./testing/i_dont_exist", []string{})
|
_, err := BuildTree("./testing/i_dont_exist", []string{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -16,13 +29,8 @@ func TestFromNotExistingDirectory(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSunny(t *testing.T) {
|
func TestSunny(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
tree, err := BuildTree("./testdata/sunny1", []string{})
|
tree, err := BuildTree("./testdata/sunny1", []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Got error: %v", err)
|
t.Errorf("Got error: %v", err)
|
||||||
|
@ -48,18 +56,9 @@ func TestSunny(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFixFormat(t *testing.T) {
|
func TestFixFormat(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = os.Remove("./testdata/sunny2")
|
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{})
|
err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Population ended in err:", err)
|
t.Fatal("Population ended in err:", err)
|
||||||
}
|
}
|
||||||
|
@ -79,13 +78,8 @@ func TestFixFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMeta(t *testing.T) {
|
func TestMeta(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
meta := []string{"option1", "option2"}
|
meta := []string{"option1", "option2"}
|
||||||
tree, err := BuildTree("./testdata/sunny1", meta)
|
tree, err := BuildTree("./testdata/sunny1", meta)
|
||||||
|
|
||||||
|
@ -103,18 +97,9 @@ func TestMeta(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestId(t *testing.T) {
|
func TestId(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = os.Remove("./testdata/sunny2")
|
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{})
|
err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Population ended in err:", err)
|
t.Fatal("Population ended in err:", err)
|
||||||
}
|
}
|
||||||
|
@ -139,18 +124,13 @@ func TestId(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMissingOptions(t *testing.T) {
|
func TestMissingOptions(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = os.Remove("./testdata/sunny2")
|
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{"option1", "option2"})
|
customMeta := make(map[string]func() string)
|
||||||
|
customMeta["option2"] = func() string {
|
||||||
|
return "customDefaultValue"
|
||||||
|
}
|
||||||
|
err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{"option1", "option2"}, customMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Population ended in err:", err)
|
t.Fatal("Population ended in err:", err)
|
||||||
}
|
}
|
||||||
|
@ -163,7 +143,7 @@ func TestMissingOptions(t *testing.T) {
|
||||||
if !strings.Contains(str, "\n* option1: test option\n") {
|
if !strings.Contains(str, "\n* option1: test option\n") {
|
||||||
t.Fatal("Changed old value for 'option1' in file 'file1.md'.")
|
t.Fatal("Changed old value for 'option1' in file 'file1.md'.")
|
||||||
}
|
}
|
||||||
if !strings.Contains(str, "\n* option2: \n") {
|
if !strings.Contains(str, "\n* option2: customDefaultValue\n") {
|
||||||
t.Fatal("'option2' has not been added to file 'file1.md'.")
|
t.Fatal("'option2' has not been added to file 'file1.md'.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +155,7 @@ func TestMissingOptions(t *testing.T) {
|
||||||
if !strings.Contains(str, "\n* option1: \n") {
|
if !strings.Contains(str, "\n* option1: \n") {
|
||||||
t.Fatal("'option1' has not been added to file 'file2.md'.")
|
t.Fatal("'option1' has not been added to file 'file2.md'.")
|
||||||
}
|
}
|
||||||
if !strings.Contains(str, "\n* option2: \n") {
|
if !strings.Contains(str, "\n* option2: customDefaultValue\n") {
|
||||||
t.Fatal("'option2' has not been added to file 'file2.md'.")
|
t.Fatal("'option2' has not been added to file 'file2.md'.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,13 +173,7 @@ func TestMissingOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadingFileContent(t *testing.T) {
|
func TestReadingFileContent(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
prepare(t)
|
||||||
if err != nil {
|
|
||||||
}
|
|
||||||
err = cp.Copy("./testdata/sunny", "./testdata/sunny1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Couldn't prepare data for testing")
|
|
||||||
}
|
|
||||||
|
|
||||||
tree, err := BuildTree("./testdata/sunny1", []string{})
|
tree, err := BuildTree("./testdata/sunny1", []string{})
|
||||||
|
|
||||||
|
@ -216,3 +190,47 @@ func TestReadingFileContent(t *testing.T) {
|
||||||
t.Error("File content is wrong...")
|
t.Error("File content is wrong...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSlice(t *testing.T) {
|
||||||
|
prepare(t)
|
||||||
|
|
||||||
|
err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Population ended in err:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := BuildTree("./testdata/sunny2", []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree2, err := tree.Slice("testdata/sunny2/subcategory")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error during search the tree: %v", err)
|
||||||
|
}
|
||||||
|
if tree2.Files[0].Name != "file3.md" {
|
||||||
|
t.Errorf("Got frong tree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFileById(t *testing.T) {
|
||||||
|
prepare(t)
|
||||||
|
|
||||||
|
err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Population ended in err:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := BuildTree("./testdata/sunny2", []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := tree.FileById("3")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error during search: %v", err)
|
||||||
|
}
|
||||||
|
if file.Name != "file3.md" {
|
||||||
|
t.Errorf("Got frong file: %s", file.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue