180 lines
3.2 KiB
Go
180 lines
3.2 KiB
Go
|
|
package document
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"path"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
func getDocumentFilepath(owner string) string {
|
||
|
|
return fmt.Sprintf("./docs/%s.json", owner)
|
||
|
|
}
|
||
|
|
|
||
|
|
type fileDocument struct {
|
||
|
|
owner string
|
||
|
|
root map[string]interface{}
|
||
|
|
dirty bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// LoadFileDocument : 파일로 도큐먼트 로딩
|
||
|
|
func LoadFileDocument(owner string) (Document, error) {
|
||
|
|
bt, err := os.ReadFile(getDocumentFilepath(owner))
|
||
|
|
if err != nil && !os.IsNotExist(err) {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(bt) == 0 {
|
||
|
|
return &fileDocument{
|
||
|
|
owner: owner,
|
||
|
|
root: make(map[string]interface{}),
|
||
|
|
dirty: false,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var doc map[string]interface{}
|
||
|
|
err = json.Unmarshal(bt, &doc)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return &fileDocument{
|
||
|
|
owner: owner,
|
||
|
|
root: doc,
|
||
|
|
dirty: false,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func findNodeInterface(doc map[string]interface{}, path string) (interface{}, error) {
|
||
|
|
if doc == nil {
|
||
|
|
return nil, ErrDocumentNotExist
|
||
|
|
}
|
||
|
|
|
||
|
|
parent := doc
|
||
|
|
for {
|
||
|
|
idx := strings.IndexRune(path, '/')
|
||
|
|
var nodename string
|
||
|
|
if idx < 0 {
|
||
|
|
nodename = path
|
||
|
|
path = ""
|
||
|
|
} else {
|
||
|
|
nodename = path[:idx]
|
||
|
|
path = path[idx+1:]
|
||
|
|
}
|
||
|
|
child, ok := parent[nodename]
|
||
|
|
|
||
|
|
if !ok {
|
||
|
|
return nil, ErrDocumentPathNotExist
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(path) == 0 {
|
||
|
|
return child, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if parent, ok = child.(map[string]interface{}); !ok {
|
||
|
|
return nil, ErrDocumentPathTypeMismatch
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func findEdgeContainer(doc map[string]interface{}, path string) (map[string]interface{}, string) {
|
||
|
|
parent := doc
|
||
|
|
for {
|
||
|
|
idx := strings.IndexRune(path, '/')
|
||
|
|
var nodename string
|
||
|
|
if idx < 0 {
|
||
|
|
nodename = path
|
||
|
|
path = ""
|
||
|
|
} else {
|
||
|
|
nodename = path[:idx]
|
||
|
|
path = path[idx+1:]
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(path) == 0 {
|
||
|
|
return parent, nodename
|
||
|
|
}
|
||
|
|
|
||
|
|
child, ok := parent[nodename]
|
||
|
|
if !ok {
|
||
|
|
child = make(map[string]interface{})
|
||
|
|
parent[nodename] = child
|
||
|
|
}
|
||
|
|
|
||
|
|
parent = child.(map[string]interface{})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadBool :
|
||
|
|
func (doc *fileDocument) ReadBool(path string) (bool, error) {
|
||
|
|
val, err := findNodeInterface(doc.root, path)
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
|
||
|
|
out, ok := val.(bool)
|
||
|
|
if !ok {
|
||
|
|
return false, ErrDocumentPathTypeMismatch
|
||
|
|
}
|
||
|
|
|
||
|
|
return out, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadString :
|
||
|
|
func (doc *fileDocument) ReadString(path string) (string, error) {
|
||
|
|
val, err := findNodeInterface(doc.root, path)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
|
||
|
|
out, ok := val.(string)
|
||
|
|
if !ok {
|
||
|
|
return "", ErrDocumentPathTypeMismatch
|
||
|
|
}
|
||
|
|
|
||
|
|
return out, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read :
|
||
|
|
func (doc *fileDocument) Read(path string) (interface{}, error) {
|
||
|
|
return findNodeInterface(doc.root, path)
|
||
|
|
}
|
||
|
|
|
||
|
|
// WriteBool :
|
||
|
|
func (doc *fileDocument) Write(path string, val interface{}) {
|
||
|
|
container, edge := findEdgeContainer(doc.root, path)
|
||
|
|
container[edge] = val
|
||
|
|
doc.dirty = true
|
||
|
|
}
|
||
|
|
|
||
|
|
// Serialize :
|
||
|
|
func (doc *fileDocument) Serialize() error {
|
||
|
|
if !doc.dirty {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
bt, err := json.Marshal(doc.root)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
filepath := getDocumentFilepath(doc.owner)
|
||
|
|
if err := os.WriteFile(filepath, bt, 0644); err != nil {
|
||
|
|
if _, patherr := err.(*os.PathError); !patherr {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
dir := path.Dir(filepath)
|
||
|
|
if err = os.MkdirAll(dir, 0755); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
if err = os.WriteFile(filepath, bt, 0644); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
doc.dirty = false
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|