diff --git a/README.md b/README.md index 1219f54..5b73dcf 100644 --- a/README.md +++ b/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 to `destination` directory. +There is possibility to add custom default value. That is passed with custom function what returns string. + ### 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 diff --git a/go.mod b/go.mod index 5798637..f20ab11 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module g.arns.lt/zordsdavini/zord-tree -go 1.16 +go 1.18 require ( g.arns.lt/zordsdavini/abcex v1.0.0 diff --git a/go.sum b/go.sum index 58025c5..22f158e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0E= 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/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= diff --git a/tree.go b/tree.go index 0259709..06087e7 100644 --- a/tree.go +++ b/tree.go @@ -2,6 +2,7 @@ package zord_tree import ( "bufio" + "errors" "fmt" "g.arns.lt/zordsdavini/abcex" cp "github.com/otiai10/copy" @@ -28,11 +29,79 @@ type Tree struct { 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) { 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) if err != nil { return err @@ -50,7 +119,7 @@ func PopulateTree(sourcePath string, destPath string, meta []string) error { return err } - err = addMissingMeta(sourcePath, meta) + err = addMissingMeta(sourcePath, meta, customMeta) if err != nil { return err } @@ -110,7 +179,7 @@ func fixFormat(dir string) error { 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 { if e != nil { return e @@ -138,7 +207,11 @@ func addMissingMeta(dir string, meta []string) error { if line == "---" { for option, process := range check { if !process { - err = addMeta(path, option, "") + defaultValue := "" + if _, ok := customMeta[option]; ok { + defaultValue = customMeta[option]() + } + err = addMeta(path, option, defaultValue) if err != nil { return err } @@ -247,7 +320,7 @@ func getMaxId(dir string) (int64, error) { func readPath(dirPath string, category []string, meta []string) (Tree, error) { tree := Tree{} - tree.Path = dirPath + tree.Path = strings.TrimPrefix(dirPath, "./") files, err := ioutil.ReadDir(dirPath) if err != nil { diff --git a/tree_test.go b/tree_test.go index 8daa4a5..02af4a2 100644 --- a/tree_test.go +++ b/tree_test.go @@ -8,6 +8,19 @@ import ( "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) { _, err := BuildTree("./testing/i_dont_exist", []string{}) if err == nil { @@ -16,13 +29,8 @@ func TestFromNotExistingDirectory(t *testing.T) { } func TestSunny(t *testing.T) { - err := os.Remove("./testdata/sunny1") - if err != nil { - } - err = cp.Copy("./testdata/sunny", "./testdata/sunny1") - if err != nil { - t.Fatal("Couldn't prepare data for testing") - } + prepare(t) + tree, err := BuildTree("./testdata/sunny1", []string{}) if err != nil { t.Errorf("Got error: %v", err) @@ -48,18 +56,9 @@ func TestSunny(t *testing.T) { } func TestFixFormat(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") - } + prepare(t) - err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}) + err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil) if err != nil { t.Fatal("Population ended in err:", err) } @@ -79,13 +78,8 @@ func TestFixFormat(t *testing.T) { } func TestMeta(t *testing.T) { - err := os.Remove("./testdata/sunny1") - if err != nil { - } - err = cp.Copy("./testdata/sunny", "./testdata/sunny1") - if err != nil { - t.Fatal("Couldn't prepare data for testing") - } + prepare(t) + meta := []string{"option1", "option2"} tree, err := BuildTree("./testdata/sunny1", meta) @@ -103,18 +97,9 @@ func TestMeta(t *testing.T) { } func TestId(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") - } + prepare(t) - err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}) + err := PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{}, nil) if err != nil { t.Fatal("Population ended in err:", err) } @@ -139,18 +124,13 @@ func TestId(t *testing.T) { } func TestMissingOptions(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") - } + prepare(t) - 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 { 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") { 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'.") } @@ -175,7 +155,7 @@ func TestMissingOptions(t *testing.T) { if !strings.Contains(str, "\n* option1: \n") { 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'.") } @@ -193,13 +173,7 @@ func TestMissingOptions(t *testing.T) { } func TestReadingFileContent(t *testing.T) { - err := os.Remove("./testdata/sunny1") - if err != nil { - } - err = cp.Copy("./testdata/sunny", "./testdata/sunny1") - if err != nil { - t.Fatal("Couldn't prepare data for testing") - } + prepare(t) tree, err := BuildTree("./testdata/sunny1", []string{}) @@ -216,3 +190,47 @@ func TestReadingFileContent(t *testing.T) { 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) + } +}