/
filesystem.go
114 lines (104 loc) · 2.51 KB
/
filesystem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package util
import (
"encoding/json"
"errors"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)
var (
ErrIrregularFileFound = errors.New("filetree contains an irregular file")
)
// The wire representation of a filetree:
// UTF-8 encoded JSON.
// File names are Unicode and UTF-8 encoded.
// File contents are byte streams which are base64 encoded and UTF-8 encoded.
// Go's json.Marshal already does this, but you
// can implement an en/decoder in any language.
type Node struct {
// Go's os package represents filenames as strings,
// so we assume that all OSes can convert their filenames
// to Go strings.
Name *string
Data []byte
Children []*Node
}
func (node Node) Encode() ([]byte, error) {
return json.Marshal(node)
}
func (node Node) String() string {
return node.string(0)
}
func (node Node) string(indent int) string {
stringBuilder := &strings.Builder{}
stringBuilder.WriteString(strings.Repeat(" ", indent))
stringBuilder.WriteString(*node.Name)
if node.IsDir() {
stringBuilder.WriteString("/")
for _, child := range node.Children {
stringBuilder.WriteString("\n")
stringBuilder.WriteString(child.string(indent + 2))
}
} else {
stringBuilder.WriteString(" - ")
stringBuilder.WriteString(string(node.Data))
}
return stringBuilder.String()
}
func (node Node) IsDir() bool {
return node.Data == nil
}
func ConvertFilesToNode(path string) (*Node, error) {
fileInfo, err := os.Stat(path)
if err != nil {
return nil, err
}
if fileInfo.Mode().IsRegular() {
contents, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
name := fileInfo.Name()
return &Node{&name, contents, nil}, nil
} else if fileInfo.Mode().IsDir() {
file, err := os.Open(path)
if err != nil {
return nil, err
}
fileNames, err := file.Readdirnames(-1)
if err != nil {
return nil, err
}
err = file.Close()
if err != nil {
return nil, err
}
children := []*Node{}
for _, fileName := range fileNames {
childPath := filepath.Join(path, fileName)
node, err := ConvertFilesToNode(childPath)
if err != nil {
return nil, err
}
children = append(children, node)
}
sort.Slice(children, func(i, j int) bool {
a := children[i]
b := children[j]
if a.IsDir() && !b.IsDir() {
return true
} else if !a.IsDir() && b.IsDir() {
return false
} else {
result := strings.Compare(*a.Name, *b.Name)
return result == -1
}
})
name := fileInfo.Name()
return &Node{&name, nil, children}, nil
} else {
return nil, ErrIrregularFileFound
}
}