commit f3d6a21d8f5a704819507b6c650a6380f9501009 Author: dengqn <434500374@qq.com> Date: Thu Aug 29 11:47:32 2024 +0800 cmt diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2bfce7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# list version + +manage file version caches \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2a259d7 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.dengqn.com/dqn/listversion + +go 1.20 diff --git a/list_version.go b/list_version.go new file mode 100644 index 0000000..721b578 --- /dev/null +++ b/list_version.go @@ -0,0 +1,143 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "time" + + "git.dengqn.com/dqn/listversion/storage" + "git.dengqn.com/dqn/listversion/util" +) + +func main() { + + op := "h" + if len(os.Args) > 1 { + op = os.Args[1] + } + // for idx, arg := range os.Args { + // fmt.Printf("%d: %s\n", idx, arg) + // } + + switch op { + case "h": + h() + break + case "l": + ls() + break + case "a": + add() + break + case "d": + del() + case "r": + rec() + break + } +} + +func h() { + fmt.Println("________________________________________________________") + fmt.Println("[h] print this") + fmt.Println("[l] list verisons. [lv l xx.txt]") + fmt.Println("[a] add file to cache. [lv a xx.txt xxx]") + fmt.Println("[d] remove version from cache. [lv d xx.txt 1]") + fmt.Println("[r] extract file version from cache. [lv r xx.txt 1]") + fmt.Println("--------------------------------------------------------") +} + +func rec() { + // 获取绝对路径 + // 获取绝对路径 + fullPath, _ := filepath.Abs(os.Args[2]) + ver := os.Args[3] + + version, _ := strconv.ParseInt(ver, 10, 32) + + meta, err := storage.GetVersionList(util.ToHashHex(fullPath)) + if err != nil { + fmt.Println("err: ", err.Error()) + return + } + + for _, v := range meta.Versions { + func(vv storage.Version) { + if v.Version == version { + storage.Extract(fullPath, vv) + } + }(v) + } +} + +func ls() { + // 获取绝对路径 + fullPath, _ := filepath.Abs(os.Args[2]) + + meta, err := storage.GetVersionList(util.ToHashHex(fullPath)) + if err != nil { + fmt.Println("err: ", err.Error()) + return + } + fmt.Printf("[%s]\n______________________\n", meta.FileName) + for _, v := range meta.Versions { + fmt.Printf("[%d].%s\t%s\n", v.Version, time.UnixMilli(v.Created).Format("2006-01-02 15:04:05"), v.Desc) + } +} + +func add() { + // xxx add xx "asdasd" + fullPath, _ := filepath.Abs(os.Args[2]) + msg := os.Args[3] + + meta, err := storage.GetVersionList(util.ToHashHex(fullPath)) + if err != nil { + // 新的 + meta = storage.NewMeta(fullPath) + } + + max := int64(0) + for _, v := range meta.Versions { + if v.Version > int64(max) { + max = v.Version + } + } + + meta.Versions = append(meta.Versions, storage.Version{ + Desc: msg, + Created: time.Now().UnixMilli(), + Version: max + 1, + }) + storage.SaveMeta(util.ToHashHex(fullPath), meta) + storage.CopyData(fullPath, meta.Versions[len(meta.Versions)-1]) + fmt.Println("version copied") +} + +func del() { + // 获取绝对路径 + fullPath, _ := filepath.Abs(os.Args[2]) + ver := os.Args[3] + + version, _ := strconv.ParseInt(ver, 10, 32) + + meta, err := storage.GetVersionList(util.ToHashHex(fullPath)) + if err != nil { + fmt.Println("err: ", err.Error()) + return + } + + tmp := make([]storage.Version, 0) + for _, v := range meta.Versions { + func(vv storage.Version) { + if vv.Version != version { + tmp = append(tmp, vv) + } else { + storage.DeleteData(fullPath, vv) + } + }(v) + } + meta.Versions = tmp + storage.SaveMeta(util.ToHashHex(fullPath), meta) +} diff --git a/storage/version.go b/storage/version.go new file mode 100644 index 0000000..775f431 --- /dev/null +++ b/storage/version.go @@ -0,0 +1,149 @@ +package storage + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path" + "strconv" + + "git.dengqn.com/dqn/listversion/util" +) + +/** +VersionMeta + +data root --> file path bash / + / file version1 + / file version2 + / meta.json --> VersionMeta[] + --> other config.json +*/ + +type FileMeta struct { + FileName string `json:"fileName"` + AbsolutePath string `json:"absolutePath"` + NameHash string `json:"nameHash"` + Versions []Version `json:"versions"` +} + +type Version struct { + Version int64 `json:"version"` + Created int64 `json:"created"` + Desc string `json:"desc"` +} + +func Extract(fullPath string, version Version) { + root, _ := os.UserConfigDir() + pathHex := util.ToHashHex(fullPath) + os.Remove(fullPath) + origin, _ := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY, os.ModePerm) + target, _ := os.OpenFile(path.Join(root, "list-version", pathHex, strconv.FormatInt(version.Version, 10)), os.O_RDONLY, os.ModePerm) + io.Copy(origin, target) + origin.Close() + target.Close() +} + +func SaveMeta(pathHex string, meta FileMeta) { + root, _ := os.UserConfigDir() + + appRoot, err := os.Open(path.Join(root, "list-version")) + if err != nil { + fmt.Println("%s", "打开文件失败0"+err.Error()) + if errors.Is(err, os.ErrNotExist) { + os.Mkdir(path.Join(root, "list-version"), os.ModePerm) + } + } + + defer func() { + if appRoot != nil { + appRoot.Close() + } + }() + + _, err = os.Open(path.Join(root, "list-version", pathHex)) + // not saved yet + if err != nil { + if errors.Is(err, os.ErrNotExist) { + os.Mkdir(path.Join(root, "list-version", pathHex), os.ModePerm) + } + } + + os.Remove(path.Join(root, "list-version", pathHex, "meta.json.bak")) + os.Rename(path.Join(root, "list-version", pathHex, "meta.json"), path.Join(root, "list-version", pathHex, "meta.json.bak")) + // meta.json + metaJsonFile, err := os.OpenFile(path.Join(root, "list-version", pathHex, "meta.json"), os.O_WRONLY, os.ModePerm) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // 直接写 + metaJsonFile, err = os.Create(path.Join(root, "list-version", pathHex, "meta.json")) + } + } + buf, err := json.Marshal(meta) + if err != nil { + fmt.Println("%s", "json"+err.Error()) + } + metaJsonFile.Write(buf) + metaJsonFile.Sync() + metaJsonFile.Close() +} + +func DeleteData(originFile string, version Version) { + root, _ := os.UserConfigDir() + pathHex := util.ToHashHex(originFile) + os.Remove(path.Join(root, "list-version", pathHex, strconv.FormatInt(version.Version, 10))) +} + +func CopyData(originFile string, version Version) { + root, _ := os.UserConfigDir() + pathHex := util.ToHashHex(originFile) + + origin, _ := os.OpenFile(originFile, os.O_RDONLY, os.ModePerm) + target, _ := os.Create(path.Join(root, "list-version", pathHex, strconv.FormatInt(version.Version, 10))) + io.Copy(target, origin) + origin.Close() + target.Close() +} + +func GetVersionList(pathHex string) (meta FileMeta, err error) { + + root, _ := os.UserConfigDir() + + appRoot, err := os.Open(path.Join(root, "list-version")) + if errors.Is(err, os.ErrNotExist) { + os.Mkdir(path.Join(root, "list-version"), os.ModePerm) + } + defer func() { + if appRoot != nil { + appRoot.Close() + } + }() + + fileMeta, err := os.Open(path.Join(root, "list-version", pathHex, "meta.json")) + // not saved yet + if errors.Is(err, os.ErrNotExist) { + return meta, errors.New("no versions") + } + defer fileMeta.Close() + // read meta.json + + buf, _ := io.ReadAll(fileMeta) + json.Unmarshal(buf, &meta) + + return meta, nil +} + +func NewMeta(filePath string) (meta FileMeta) { + + meta.AbsolutePath = filePath + + st, _ := os.Stat(filePath) + + meta.FileName = st.Name() + meta.NameHash = util.ToHashHex(filePath) + meta.Versions = make([]Version, 0) + + return meta +} diff --git a/util/hash.go b/util/hash.go new file mode 100644 index 0000000..abc2229 --- /dev/null +++ b/util/hash.go @@ -0,0 +1,12 @@ +package util + +import ( + "crypto/sha1" + "encoding/hex" +) + +func ToHashHex(filePath string) string { + s1 := sha1.New() + buf := s1.Sum([]byte(filePath)) + return hex.EncodeToString(buf) +}