# HG changeset patch # User anatofuz # Date 1608095238 -32400 # Node ID aa664e434d43d18f48b641cc874cec201f86cc7a init backup project diff -r 000000000000 -r aa664e434d43 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Wed Dec 16 14:07:18 2020 +0900 @@ -0,0 +1,27 @@ +syntax:glob + +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +# End of https://www.toptal.com/developers/gitignore/api/go diff -r 000000000000 -r aa664e434d43 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Wed Dec 16 14:07:18 2020 +0900 @@ -0,0 +1,3 @@ +all: + go build ./cmd/growibackup + diff -r 000000000000 -r aa664e434d43 backup.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backup.go Wed Dec 16 14:07:18 2020 +0900 @@ -0,0 +1,120 @@ +package growibackup + +import ( + "bufio" + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "golang.org/x/xerrors" +) + +// Revision is growy entry struct +type Revision struct { + ID string `json:"_id"` + Format string `json:"format"` + CreatedAt time.Time `json:"createdAt"` + Path string `json:"path"` + Body string `json:"body"` + Author string `json:"author"` + HasDiffToPrev bool `json:"hasDiffToPrev"` + V int `json:"__v"` +} + +// Revisions is as equal as revision.json +type Revisions []Revision + +func convertStructFromJSON(jsonPATH string) (*Revisions, error) { + file, err := ioutil.ReadFile(jsonPATH) + if err != nil { + return nil, xerrors.Errorf("[error] failed open %s, %+w", jsonPATH, err) + } + + revs := Revisions{} + err = json.Unmarshal([]byte(file), &revs) + + if err != nil { + return nil, xerrors.Errorf("[error] failed unmarshal json %s, %+w", jsonPATH, err) + } + + return &revs, nil +} + +func (revs *Revisions) createUniqRevisions() *Revisions { + path2Revision := make(map[string]Revision) + for _, rev := range *revs { + if prevRev, ok := path2Revision[rev.Path]; ok { + if prevRev.CreatedAt.After(rev.CreatedAt) { + path2Revision[rev.Path] = rev + } + continue + } + path2Revision[rev.Path] = rev + } + + newRevs := make(Revisions, 0, len(path2Revision)) + for _, rev := range path2Revision { + newRevs = append(newRevs, rev) + } + return &newRevs +} + +func (revs *Revisions) backup(outputPATH string) error { + var sbuilder strings.Builder + path2Exists := make(map[string]bool) + + oPATH, err := getAbsRootPATH(outputPATH) + if err != nil { + return xerrors.Errorf("[error] failed get abs at %s %+w", outputPATH, err) + } + + for _, rev := range *revs { + sbuilder.WriteString(rev.Path) + sbuilder.WriteString(".md") + path := sbuilder.String() + sbuilder.Reset() + + mdPATH := filepath.Join(oPATH, path) + dirPATH := filepath.Dir(mdPATH) + if _, ok := path2Exists[dirPATH]; !ok { + err := checkAfterMkdir(dirPATH) + if err != nil { + return err + } + } + + f, err := os.Create(mdPATH) + if err != nil { + return xerrors.Errorf("[error] failed create %s, %+w", mdPATH, err) + } + w := bufio.NewWriter(f) + w.WriteString(rev.Body) + w.Flush() + f.Close() + } + return nil +} + +func checkAfterMkdir(dirpath string) error { + if _, err := os.Stat(dirpath); os.IsNotExist(err) { + err := os.MkdirAll(dirpath, 0755) + if err != nil { + return err + } + } + return nil +} + +func getAbsRootPATH(outputPath string) (string, error) { + if !filepath.IsAbs(outputPath) { + oPATH, err := filepath.Abs(outputPath) + if err != nil { + return "", err + } + return oPATH, nil + } + return outputPath, nil +} diff -r 000000000000 -r aa664e434d43 go.sum --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/go.sum Wed Dec 16 14:07:18 2020 +0900 @@ -0,0 +1,2 @@ +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff -r 000000000000 -r aa664e434d43 run.go --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/run.go Wed Dec 16 14:07:18 2020 +0900 @@ -0,0 +1,12 @@ +package growibackup + +import "golang.org/x/xerrors" + +func Run(jsonPATH, outputRootPATH string) error { + revisions, err := convertStructFromJSON(jsonPATH) + if err != nil { + return xerrors.Errorf("[error] fialed convert struct from json %+w", err) + } + revisions = revisions.createUniqRevisions() + return revisions.backup(outputRootPATH) +}