Compare commits
No commits in common. "2d1ac09ba1324ff3394e852e6a18fb6049157022" and "a9e6c702b9525ee3ac1be8cbfd6b8ad139303191" have entirely different histories.
2d1ac09ba1
...
a9e6c702b9
6 changed files with 13 additions and 199 deletions
63
README.md
63
README.md
|
@ -1,67 +1,6 @@
|
||||||
# zord-tree
|
# zord-tree
|
||||||
|
|
||||||
Library for golang to build articles tree from file system. The file should be in specific format to hold meta data, id and
|
Library for golang to build articles tree from file system.
|
||||||
content.
|
|
||||||
|
|
||||||
Process should go in two steps:
|
|
||||||
|
|
||||||
* populate existing `source` directory with formatting files, add missing `id` and meta data
|
|
||||||
* build `Tree` to operate on object for your app
|
|
||||||
* read file content without meta data by path
|
|
||||||
|
|
||||||
|
|
||||||
## File and tree format
|
|
||||||
|
|
||||||
### Category
|
|
||||||
|
|
||||||
During building `Tree` directory path is converted into array of categories. So, file in path `linux/command/video` will get
|
|
||||||
categories: `linux`, `command`, `video`.
|
|
||||||
|
|
||||||
### Split line
|
|
||||||
|
|
||||||
File meta should be split by `---` line. If there is markdown formating to mark header, first such case (`---`) will be formatted
|
|
||||||
to contain empty line. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
Text
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
will become
|
|
||||||
|
|
||||||
```
|
|
||||||
Text
|
|
||||||
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
### Meta data
|
|
||||||
|
|
||||||
Meta data separated by lines and in format `name: value`. By default library supports `tags` as comma separated array.
|
|
||||||
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.
|
|
||||||
|
|
||||||
### File structure
|
|
||||||
|
|
||||||
```
|
|
||||||
tags: linux,command,video
|
|
||||||
meta1: example
|
|
||||||
meta2: some long description
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Hear goes content. It can be written in html, markdown or whatever what can be processed later
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
### Future plans
|
|
||||||
|
|
||||||
* 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
|
|
||||||
|
|
||||||
### TODO
|
|
||||||
|
|
||||||
* test to read file content
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -4,5 +4,5 @@ go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
g.arns.lt/zordsdavini/abcex v1.0.0
|
g.arns.lt/zordsdavini/abcex v1.0.0
|
||||||
github.com/otiai10/copy v1.6.0
|
github.com/otiai10/copy v1.5.1
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -2,8 +2,6 @@ g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0
|
||||||
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 h1:a/cs2E1/1V0az8K5nblbl+ymEa4E11AfaOLMar8V34w=
|
||||||
github.com/otiai10/copy v1.5.1/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
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=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||||
|
|
2
testdata/sunny/file1.md
vendored
2
testdata/sunny/file1.md
vendored
|
@ -1,7 +1,7 @@
|
||||||
tags: t1,t2
|
tags: t1,t2
|
||||||
|
|
||||||
description: Če test file __va__
|
description: Če test file __va__
|
||||||
option1: test option
|
option1: test option
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Če kažkas torietom būtė
|
# Če kažkas torietom būtė
|
||||||
|
|
111
tree.go
111
tree.go
|
@ -33,13 +33,7 @@ func BuildTree(dirPath string, meta []string) (Tree, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PopulateTree(sourcePath string, destPath string, meta []string) error {
|
func PopulateTree(sourcePath string, destPath string, meta []string) error {
|
||||||
err := fixFormat(sourcePath)
|
id, err := getMaxId(sourcePath)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var id int64 = 0
|
|
||||||
id, err = getMaxId(sourcePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -60,56 +54,6 @@ func PopulateTree(sourcePath string, destPath string, meta []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixFormat(dir string) error {
|
|
||||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, e error) error {
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Mode().IsRegular() {
|
|
||||||
b, err := ioutil.ReadFile(path) // just pass the file name
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
osf, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all empty lines and separate split line
|
|
||||||
content := ""
|
|
||||||
format := true
|
|
||||||
scanner := bufio.NewScanner(osf)
|
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if line == "---" {
|
|
||||||
format = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if format {
|
|
||||||
line = strings.Trim(line, " ")
|
|
||||||
if line != "" {
|
|
||||||
content = content + "\n" + line
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content = content + "\n" + line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(path, []byte(content), 0644)
|
|
||||||
|
|
||||||
// format split line
|
|
||||||
str := string(b)
|
|
||||||
str = strings.Replace(str, "\n---\n", "\n\n---\n", 1)
|
|
||||||
err = ioutil.WriteFile(path, []byte(str), 0644)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func addMissingMeta(dir string, meta []string) error {
|
func addMissingMeta(dir string, meta []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 {
|
||||||
|
@ -151,8 +95,11 @@ func addMissingMeta(dir string, meta []string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMissingId(dir string, id int64) error {
|
func addMissingId(dir string, id int64) error {
|
||||||
|
@ -190,20 +137,20 @@ func addMissingId(dir string, id int64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMeta(path string, option string, value string) error {
|
func addMeta(path string, option string, value string) error {
|
||||||
b, err := ioutil.ReadFile(path) // just pass the file name
|
b, err := ioutil.ReadFile(path) // just pass the file name
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
str := string(b)
|
str := string(b)
|
||||||
str = strings.Replace(str, "\n\n---\n", fmt.Sprintf("\n%s: %s\n\n---\n", option, value), 1)
|
str = strings.Replace(str, "\n\n---\n", fmt.Sprintf("\n%s: %s\n\n---\n", option, value), 1)
|
||||||
err = ioutil.WriteFile(path, []byte(str), 0644)
|
err = ioutil.WriteFile(path, []byte(str), 0644)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMaxId(dir string) (int64, error) {
|
func getMaxId(dir string) (int64, error) {
|
||||||
|
@ -315,50 +262,12 @@ func readFile(file fs.FileInfo, fullPath string, category []string, meta []strin
|
||||||
}
|
}
|
||||||
for _, option := range meta {
|
for _, option := range meta {
|
||||||
if strings.HasPrefix(line, option) {
|
if strings.HasPrefix(line, option) {
|
||||||
line = strings.TrimPrefix(line, option+":")
|
line = strings.TrimPrefix(line, option + ":")
|
||||||
f.Meta[option] = strings.Trim(line, " ")
|
f.Meta[option] = strings.Trim(line, " ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = osf.Close()
|
osf.Close()
|
||||||
|
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadFileContent(file File) (string, error) {
|
|
||||||
osf, err := os.Open(file.FullPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
content := ""
|
|
||||||
removeEmptyLine := true
|
|
||||||
isMetaPart := true
|
|
||||||
scanner := bufio.NewScanner(osf)
|
|
||||||
scanner.Split(bufio.ScanLines)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if line == "---" {
|
|
||||||
isMetaPart = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isMetaPart {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if removeEmptyLine {
|
|
||||||
line = strings.Trim(line, " ")
|
|
||||||
if line == "" {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
removeEmptyLine = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content + "\n" + line
|
|
||||||
}
|
|
||||||
_ = osf.Close()
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
}
|
|
||||||
|
|
32
tree_test.go
32
tree_test.go
|
@ -47,38 +47,6 @@ func TestSunny(t *testing.T) {
|
||||||
t.Log(tree)
|
t.Log(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = PopulateTree("./testdata/sunny1", "./testdata/sunny2", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Population ended in err:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadFile("./testdata/sunny2/file1.md")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("No destination file 'file1.md'.")
|
|
||||||
}
|
|
||||||
str := string(b)
|
|
||||||
if !strings.Contains(str, "tags: t1,t2\ndescription: Če test file __va__\n") {
|
|
||||||
t.Fatal("Empty lines has not been removed in file 'file1.md'.")
|
|
||||||
}
|
|
||||||
if !strings.Contains(str, "\n\n---\n") {
|
|
||||||
t.Fatal("Split line has not been formatted in file 'file1.md'.")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log(tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMeta(t *testing.T) {
|
func TestMeta(t *testing.T) {
|
||||||
err := os.Remove("./testdata/sunny1")
|
err := os.Remove("./testdata/sunny1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue