|
package _123Link |
|
|
|
import ( |
|
"fmt" |
|
url2 "net/url" |
|
stdpath "path" |
|
"strconv" |
|
"strings" |
|
"time" |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func BuildTree(text string) (*Node, error) { |
|
lines := strings.Split(text, "\n") |
|
var root = &Node{Level: -1, Name: "root"} |
|
stack := []*Node{root} |
|
for _, line := range lines { |
|
|
|
indent := 0 |
|
for i := 0; i < len(line); i++ { |
|
if line[i] != ' ' { |
|
break |
|
} |
|
indent++ |
|
} |
|
|
|
if indent%2 != 0 { |
|
return nil, fmt.Errorf("the line '%s' is not a multiple of 2", line) |
|
} |
|
|
|
level := indent / 2 |
|
line = strings.TrimSpace(line[indent:]) |
|
|
|
if line == "" { |
|
continue |
|
} |
|
|
|
|
|
for level <= stack[len(stack)-1].Level { |
|
|
|
stack = stack[:len(stack)-1] |
|
} |
|
|
|
if isFolder(line) { |
|
|
|
node := &Node{ |
|
Level: level, |
|
Name: strings.TrimSuffix(line, ":"), |
|
} |
|
|
|
stack[len(stack)-1].Children = append(stack[len(stack)-1].Children, node) |
|
|
|
stack = append(stack, node) |
|
} else { |
|
|
|
|
|
node, err := parseFileLine(line) |
|
if err != nil { |
|
return nil, err |
|
} |
|
node.Level = level |
|
|
|
stack[len(stack)-1].Children = append(stack[len(stack)-1].Children, node) |
|
} |
|
} |
|
return root, nil |
|
} |
|
|
|
func isFolder(line string) bool { |
|
return strings.HasSuffix(line, ":") |
|
} |
|
|
|
|
|
|
|
func parseFileLine(line string) (*Node, error) { |
|
|
|
if !strings.Contains(line, "http://") && !strings.Contains(line, "https://") { |
|
return nil, fmt.Errorf("invalid line: %s, because url is required for file", line) |
|
} |
|
index := strings.Index(line, "http://") |
|
if index == -1 { |
|
index = strings.Index(line, "https://") |
|
} |
|
url := line[index:] |
|
info := line[:index] |
|
node := &Node{ |
|
Url: url, |
|
} |
|
name := stdpath.Base(url) |
|
unescape, err := url2.PathUnescape(name) |
|
if err == nil { |
|
name = unescape |
|
} |
|
node.Name = name |
|
if index > 0 { |
|
if !strings.HasSuffix(info, ":") { |
|
return nil, fmt.Errorf("invalid line: %s, because file info must end with ':'", line) |
|
} |
|
info = info[:len(info)-1] |
|
if info == "" { |
|
return nil, fmt.Errorf("invalid line: %s, because file name can't be empty", line) |
|
} |
|
infoParts := strings.Split(info, ":") |
|
size, err := strconv.ParseInt(infoParts[0], 10, 64) |
|
if err != nil { |
|
return nil, fmt.Errorf("invalid line: %s, because file size must be an integer", line) |
|
} |
|
node.Size = size |
|
if len(infoParts) > 1 { |
|
modified, err := strconv.ParseInt(infoParts[1], 10, 64) |
|
if err != nil { |
|
return nil, fmt.Errorf("invalid line: %s, because file modified must be an unix timestamp", line) |
|
} |
|
node.Modified = modified |
|
} else { |
|
node.Modified = time.Now().Unix() |
|
} |
|
} |
|
return node, nil |
|
} |
|
|
|
func splitPath(path string) []string { |
|
if path == "/" { |
|
return []string{"root"} |
|
} |
|
parts := strings.Split(path, "/") |
|
parts[0] = "root" |
|
return parts |
|
} |
|
|
|
func GetNodeFromRootByPath(root *Node, path string) *Node { |
|
return root.getByPath(splitPath(path)) |
|
} |
|
|