Renaming things

This commit is contained in:
Alex Yatskov 2015-06-19 17:51:10 +09:00
parent b6afe85166
commit 00d1c81a6c
7 changed files with 127 additions and 134 deletions

View File

@ -37,7 +37,7 @@ import (
type database struct { type database struct {
base string base string
vers versionList vers verList
} }
func newDatabase(dir string) (*database, error) { func newDatabase(dir string) (*database, error) {
@ -85,13 +85,13 @@ func (db *database) save() error {
return nil return nil
} }
func (db *database) buildVersions(base string) (versionList, error) { func (db *database) buildVersions(base string) (verList, error) {
nodes, err := ioutil.ReadDir(base) nodes, err := ioutil.ReadDir(base)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var vers versionList var vers verList
for _, node := range nodes { for _, node := range nodes {
if !node.IsDir() { if !node.IsDir() {
continue continue

56
dir.go
View File

@ -33,30 +33,30 @@ import (
) )
// //
// versionedDir // verDir
// //
type versionedDir struct { type verDir struct {
dirs map[string]*versionedDir dirs map[string]*verDir
files map[string]*versionedFile files map[string]*verFile
node *versionedNode node *verNode
inode uint64 inode uint64
parent *versionedDir parent *verDir
} }
func newVersionedDir(node *versionedNode, parent *versionedDir) *versionedDir { func newVerDir(node *verNode, parent *verDir) *verDir {
dirs := make(map[string]*versionedDir) dirs := make(map[string]*verDir)
files := make(map[string]*versionedFile) files := make(map[string]*verFile)
return &versionedDir{dirs, files, node, allocInode(), parent} return &verDir{dirs, files, node, allocInode(), parent}
} }
func (vd *versionedDir) version() error { func (vd *verDir) version() error {
if vd.node.flags&NodeFlagVer == NodeFlagVer { if vd.node.flags&NodeFlagVer == NodeFlagVer {
return nil return nil
} }
node := newVersionedNode(vd.node.path, vd.node.ver.db.lastVersion(), vd.node, NodeFlagDir|NodeFlagVer) node := newVerNode(vd.node.path, vd.node.ver.db.lastVersion(), vd.node, NodeFlagDir|NodeFlagVer)
if err := os.MkdirAll(node.rebasedPath(), 0755); err != nil { if err := os.MkdirAll(node.rebasedPath(), 0755); err != nil {
return err return err
@ -68,7 +68,7 @@ func (vd *versionedDir) version() error {
return nil return nil
} }
func (vd *versionedDir) createDir(name string) (*versionedDir, error) { func (vd *verDir) createDir(name string) (*verDir, error) {
if err := vd.version(); err != nil { if err := vd.version(); err != nil {
return nil, err return nil, err
} }
@ -78,15 +78,15 @@ func (vd *versionedDir) createDir(name string) (*versionedDir, error) {
return nil, err return nil, err
} }
node := newVersionedNode(childPath, vd.node.ver, nil, NodeFlagDir|NodeFlagVer) node := newVerNode(childPath, vd.node.ver, nil, NodeFlagDir|NodeFlagVer)
dir := newVersionedDir(node, vd) dir := newVerDir(node, vd)
vd.dirs[name] = dir vd.dirs[name] = dir
node.ver.meta.createNode(node.path) node.ver.meta.createNode(node.path)
return dir, nil return dir, nil
} }
func (vd *versionedDir) createFile(name string, flags int) (*versionedFile, error) { func (vd *verDir) createFile(name string, flags int) (*verFile, error) {
if err := vd.version(); err != nil { if err := vd.version(); err != nil {
return nil, err return nil, err
} }
@ -97,8 +97,8 @@ func (vd *versionedDir) createFile(name string, flags int) (*versionedFile, erro
return nil, err return nil, err
} }
node := newVersionedNode(childPath, vd.node.ver, nil, NodeFlagVer) node := newVerNode(childPath, vd.node.ver, nil, NodeFlagVer)
file := newVersionedFile(node, vd) file := newVerFile(node, vd)
file.handle = handle file.handle = handle
vd.files[name] = file vd.files[name] = file
@ -107,7 +107,7 @@ func (vd *versionedDir) createFile(name string, flags int) (*versionedFile, erro
} }
// Node // Node
func (vd *versionedDir) Attr(ctx context.Context, attr *fuse.Attr) error { func (vd *verDir) Attr(ctx context.Context, attr *fuse.Attr) error {
if err := vd.node.attr(attr); err != nil { if err := vd.node.attr(attr); err != nil {
return err return err
} }
@ -117,26 +117,26 @@ func (vd *versionedDir) Attr(ctx context.Context, attr *fuse.Attr) error {
} }
// NodeGetattrer // NodeGetattrer
func (vd *versionedDir) Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error { func (vd *verDir) Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error {
return vd.Attr(ctx, &resp.Attr) return vd.Attr(ctx, &resp.Attr)
} }
// NodeSetattrer // NodeSetattrer
func (vd *versionedDir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { func (vd *verDir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
vd.version() vd.version()
return vd.node.setAttr(req, resp) return vd.node.setAttr(req, resp)
} }
// NodeCreater // NodeCreater
func (vd *versionedDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fs.Node, handle fs.Handle, err error) { func (vd *verDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (node fs.Node, handle fs.Handle, err error) {
if req.Mode.IsDir() { if req.Mode.IsDir() {
var dir *versionedDir var dir *verDir
if dir, err = vd.createDir(req.Name); err == nil { if dir, err = vd.createDir(req.Name); err == nil {
node = dir node = dir
handle = dir handle = dir
} }
} else if req.Mode.IsRegular() { } else if req.Mode.IsRegular() {
var file *versionedFile var file *verFile
if file, err = vd.createFile(req.Name, int(req.Flags)); err == nil { if file, err = vd.createFile(req.Name, int(req.Flags)); err == nil {
node = file node = file
handle = file handle = file
@ -149,12 +149,12 @@ func (vd *versionedDir) Create(ctx context.Context, req *fuse.CreateRequest, res
} }
// NodeMkdirer // NodeMkdirer
func (vd *versionedDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { func (vd *verDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
return vd.createDir(req.Name) return vd.createDir(req.Name)
} }
// NodeRemover // NodeRemover
func (vd *versionedDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error { func (vd *verDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
if req.Dir { if req.Dir {
node := vd.dirs[req.Name].node node := vd.dirs[req.Name].node
ver := node.ver ver := node.ver
@ -189,7 +189,7 @@ func (vd *versionedDir) Remove(ctx context.Context, req *fuse.RemoveRequest) err
} }
// NodeRequestLookuper // NodeRequestLookuper
func (vd *versionedDir) Lookup(ctx context.Context, name string) (fs.Node, error) { func (vd *verDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
if dir, ok := vd.dirs[name]; ok { if dir, ok := vd.dirs[name]; ok {
return dir, nil return dir, nil
} }
@ -202,7 +202,7 @@ func (vd *versionedDir) Lookup(ctx context.Context, name string) (fs.Node, error
} }
// HandleReadDirAller // HandleReadDirAller
func (vd *versionedDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { func (vd *verDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
entries := []fuse.Dirent{{Inode: vd.inode, Name: ".", Type: fuse.DT_Dir}} entries := []fuse.Dirent{{Inode: vd.inode, Name: ".", Type: fuse.DT_Dir}}
if vd.parent != nil { if vd.parent != nil {
entry := fuse.Dirent{Inode: vd.parent.inode, Name: "..", Type: fuse.DT_Dir} entry := fuse.Dirent{Inode: vd.parent.inode, Name: "..", Type: fuse.DT_Dir}

36
file.go
View File

@ -32,28 +32,28 @@ import (
) )
// //
// versionedFile // verFile
// //
type versionedFile struct { type verFile struct {
node *versionedNode node *verNode
inode uint64 inode uint64
parent *versionedDir parent *verDir
handle *os.File handle *os.File
} }
func newVersionedFile(node *versionedNode, parent *versionedDir) *versionedFile { func newVerFile(node *verNode, parent *verDir) *verFile {
return &versionedFile{node, allocInode(), parent, nil} return &verFile{node, allocInode(), parent, nil}
} }
func (vf *versionedFile) version() error { func (vf *verFile) version() error {
if vf.node.flags&NodeFlagVer == NodeFlagVer { if vf.node.flags&NodeFlagVer == NodeFlagVer {
return nil return nil
} }
node := newVersionedNode(vf.node.path, vf.node.ver.db.lastVersion(), vf.node, NodeFlagVer) node := newVerNode(vf.node.path, vf.node.ver.db.lastVersion(), vf.node, NodeFlagVer)
if _, err := fileCopy(vf.node.rebasedPath(), node.rebasedPath()); err != nil { if _, err := copyFile(vf.node.rebasedPath(), node.rebasedPath()); err != nil {
return err return err
} }
@ -64,24 +64,24 @@ func (vf *versionedFile) version() error {
} }
// Node // Node
func (vf *versionedFile) Attr(ctx context.Context, attr *fuse.Attr) error { func (vf *verFile) Attr(ctx context.Context, attr *fuse.Attr) error {
vf.node.attr(attr) vf.node.attr(attr)
attr.Inode = vf.inode attr.Inode = vf.inode
return nil return nil
} }
// NodeGetattrer // NodeGetattrer
func (vf *versionedFile) Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error { func (vf *verFile) Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error {
return vf.Attr(ctx, &resp.Attr) return vf.Attr(ctx, &resp.Attr)
} }
// NodeSetattrer // NodeSetattrer
func (vf *versionedFile) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { func (vf *verFile) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
return vf.node.setAttr(req, resp) return vf.node.setAttr(req, resp)
} }
// NodeOpener // NodeOpener
func (vf *versionedFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { func (vf *verFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
if vf.handle != nil { if vf.handle != nil {
return nil, errors.New("attempted to open already opened file") return nil, errors.New("attempted to open already opened file")
} }
@ -102,7 +102,7 @@ func (vf *versionedFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *
} }
// HandleReleaser // HandleReleaser
func (vf *versionedFile) Release(ctx context.Context, req *fuse.ReleaseRequest) error { func (vf *verFile) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
if vf.handle == nil { if vf.handle == nil {
return errors.New("attempted to release unopened file") return errors.New("attempted to release unopened file")
} }
@ -114,7 +114,7 @@ func (vf *versionedFile) Release(ctx context.Context, req *fuse.ReleaseRequest)
} }
// NodeFsyncer // NodeFsyncer
func (vf *versionedFile) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { func (vf *verFile) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
if vf.handle == nil { if vf.handle == nil {
return errors.New("attempted to sync unopened file") return errors.New("attempted to sync unopened file")
} }
@ -123,7 +123,7 @@ func (vf *versionedFile) Fsync(ctx context.Context, req *fuse.FsyncRequest) erro
} }
// HandleReader // HandleReader
func (vf *versionedFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { func (vf *verFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
if vf.handle == nil { if vf.handle == nil {
return errors.New("attempted to read from unopened file") return errors.New("attempted to read from unopened file")
} }
@ -137,7 +137,7 @@ func (vf *versionedFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *
} }
// HandleReadAller // HandleReadAller
func (vf *versionedFile) ReadAll(ctx context.Context) ([]byte, error) { func (vf *verFile) ReadAll(ctx context.Context) ([]byte, error) {
if vf.handle == nil { if vf.handle == nil {
return nil, errors.New("attempted to read from unopened file") return nil, errors.New("attempted to read from unopened file")
} }
@ -156,7 +156,7 @@ func (vf *versionedFile) ReadAll(ctx context.Context) ([]byte, error) {
} }
// HandleWriter // HandleWriter
func (vf *versionedFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { func (vf *verFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
if vf.handle == nil { if vf.handle == nil {
return errors.New("attempted to write to unopened file") return errors.New("attempted to write to unopened file")
} }

20
meta.go
View File

@ -30,17 +30,17 @@ import (
) )
// //
// versionMetadata // verMeta
// //
type versionMetadata struct { type verMeta struct {
Deleted []string `json:"deleted"` Deleted []string `json:"deleted"`
path string path string
dirty bool dirty bool
} }
func newVersionMetadata(path string) (*versionMetadata, error) { func newVerMeta(path string) (*verMeta, error) {
meta := &versionMetadata{path: path} meta := &verMeta{path: path}
if err := meta.load(); err != nil { if err := meta.load(); err != nil {
return nil, err return nil, err
} }
@ -48,7 +48,7 @@ func newVersionMetadata(path string) (*versionMetadata, error) {
return meta, nil return meta, nil
} }
func (m *versionMetadata) filter(nodes versionedNodeMap) { func (m *verMeta) filter(nodes verNodeMap) {
for _, delPath := range m.Deleted { for _, delPath := range m.Deleted {
for name, node := range nodes { for name, node := range nodes {
if strings.HasPrefix(node.path, delPath) { if strings.HasPrefix(node.path, delPath) {
@ -58,20 +58,20 @@ func (m *versionMetadata) filter(nodes versionedNodeMap) {
} }
} }
func (m *versionMetadata) removeNode(path string) { func (m *verMeta) removeNode(path string) {
m.Deleted = append(m.Deleted, path) m.Deleted = append(m.Deleted, path)
m.dirty = true m.dirty = true
} }
func (m *versionMetadata) createNode(path string) { func (m *verMeta) createNode(path string) {
m.dirty = true m.dirty = true
} }
func (m *versionMetadata) modifyNode(path string) { func (m *verMeta) modifyNode(path string) {
m.dirty = true m.dirty = true
} }
func (m *versionMetadata) load() error { func (m *verMeta) load() error {
m.dirty = false m.dirty = false
if _, err := os.Stat(m.path); os.IsNotExist(err) { if _, err := os.Stat(m.path); os.IsNotExist(err) {
@ -90,7 +90,7 @@ func (m *versionMetadata) load() error {
return nil return nil
} }
func (m *versionMetadata) save() error { func (m *verMeta) save() error {
if !m.dirty { if !m.dirty {
return nil return nil
} }

24
node.go
View File

@ -31,7 +31,7 @@ import (
) )
// //
// versionedNode // verNode
// //
const ( const (
@ -39,18 +39,18 @@ const (
NodeFlagVer NodeFlagVer
) )
type versionedNode struct { type verNode struct {
path string path string
ver *version ver *version
parent *versionedNode parent *verNode
flags int flags int
} }
func newVersionedNode(path string, ver *version, parent *versionedNode, flags int) *versionedNode { func newVerNode(path string, ver *version, parent *verNode, flags int) *verNode {
return &versionedNode{path, ver, parent, flags} return &verNode{path, ver, parent, flags}
} }
func (n *versionedNode) setAttr(req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { func (n *verNode) setAttr(req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
if err := n.attr(&resp.Attr); err != nil { if err := n.attr(&resp.Attr); err != nil {
return err return err
} }
@ -92,24 +92,24 @@ func (n *versionedNode) setAttr(req *fuse.SetattrRequest, resp *fuse.SetattrResp
return nil return nil
} }
func (n *versionedNode) rebasedPath() string { func (n *verNode) rebasedPath() string {
return n.ver.rebasePath(n.path) return n.ver.rebasePath(n.path)
} }
func (n *versionedNode) owner(stat syscall.Stat_t) (gid, uid uint32) { func (n *verNode) owner(stat syscall.Stat_t) (gid, uid uint32) {
gid = stat.Gid gid = stat.Gid
uid = stat.Uid uid = stat.Uid
return return
} }
func (n *versionedNode) times(stat syscall.Stat_t) (atime, mtime, ctime time.Time) { func (n *verNode) times(stat syscall.Stat_t) (atime, mtime, ctime time.Time) {
atime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) atime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
mtime = time.Unix(int64(stat.Mtim.Sec), int64(stat.Mtim.Nsec)) mtime = time.Unix(int64(stat.Mtim.Sec), int64(stat.Mtim.Nsec))
ctime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) ctime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
return return
} }
func (n *versionedNode) attr(attr *fuse.Attr) error { func (n *verNode) attr(attr *fuse.Attr) error {
info, err := os.Stat(n.rebasedPath()) info, err := os.Stat(n.rebasedPath())
if err != nil { if err != nil {
return err return err
@ -129,7 +129,7 @@ func (n *versionedNode) attr(attr *fuse.Attr) error {
} }
// //
// versionedNodeMap // verNodeMap
// //
type versionedNodeMap map[string]*versionedNode type verNodeMap map[string]*verNode

44
util.go
View File

@ -23,22 +23,24 @@
package main package main
import ( import (
"errors"
"fmt"
"io" "io"
"os" "os"
"path"
"regexp"
"strconv"
"sync/atomic" "sync/atomic"
"time"
) )
//
// utilities
//
var inodeCnt uint64 var inodeCnt uint64
func allocInode() uint64 { func allocInode() uint64 {
return atomic.AddUint64(&inodeCnt, 1) return atomic.AddUint64(&inodeCnt, 1)
} }
func fileCopy(src, dst string) (int64, error) { func copyFile(src, dst string) (int64, error) {
srcFile, err := os.Open(src) srcFile, err := os.Open(src)
if err != nil { if err != nil {
return 0, err return 0, err
@ -53,3 +55,35 @@ func fileCopy(src, dst string) (int64, error) {
return io.Copy(srcFile, dstFile) return io.Copy(srcFile, dstFile)
} }
func buildNewVersion(base string) error {
name := buildVerName(time.Now())
if err := os.MkdirAll(path.Join(base, name, "root"), 0755); err != nil {
return err
}
return nil
}
func buildVerName(timestamp time.Time) string {
return fmt.Sprintf("ver_%.16x", timestamp.Unix())
}
func parseVerName(name string) (time.Time, error) {
re, err := regexp.Compile(`ver_([0-9a-f]+)$`)
if err != nil {
return time.Unix(0, 0), err
}
matches := re.FindStringSubmatch(name)
if len(matches) < 2 {
return time.Unix(0, 0), errors.New("invalid version identifier")
}
timestamp, err := strconv.ParseInt(matches[1], 16, 64)
if err != nil {
return time.Unix(0, 0), err
}
return time.Unix(timestamp, 0), nil
}

View File

@ -23,14 +23,9 @@
package main package main
import ( import (
"errors"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"time" "time"
"bazil.org/fuse/fs" "bazil.org/fuse/fs"
@ -44,13 +39,13 @@ type version struct {
base string base string
parent *version parent *version
timestamp time.Time timestamp time.Time
meta *versionMetadata meta *verMeta
root *versionedDir root *verDir
db *database db *database
} }
func newVersion(base string, timestamp time.Time, db *database) (*version, error) { func newVersion(base string, timestamp time.Time, db *database) (*version, error) {
meta, err := newVersionMetadata(filepath.Join(base, "meta.json")) meta, err := newVerMeta(filepath.Join(base, "meta.json"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -58,8 +53,8 @@ func newVersion(base string, timestamp time.Time, db *database) (*version, error
return &version{base, nil, timestamp, meta, nil, db}, nil return &version{base, nil, timestamp, meta, nil, db}, nil
} }
func (v *version) scanDir(path string) (versionedNodeMap, error) { func (v *version) scanDir(path string) (verNodeMap, error) {
var baseNodes versionedNodeMap var baseNodes verNodeMap
if v.parent != nil { if v.parent != nil {
var err error var err error
@ -71,7 +66,7 @@ func (v *version) scanDir(path string) (versionedNodeMap, error) {
v.meta.filter(baseNodes) v.meta.filter(baseNodes)
} }
ownNodes := make(versionedNodeMap) ownNodes := make(verNodeMap)
{ {
infos, err := ioutil.ReadDir(v.rebasePath(path)) infos, err := ioutil.ReadDir(v.rebasePath(path))
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
@ -88,7 +83,7 @@ func (v *version) scanDir(path string) (versionedNodeMap, error) {
childName := info.Name() childName := info.Name()
childPath := filepath.Join(path, childName) childPath := filepath.Join(path, childName)
ownNodes[childName] = newVersionedNode(childPath, v, nil, childFlags) ownNodes[childName] = newVerNode(childPath, v, nil, childFlags)
} }
} }
@ -107,7 +102,7 @@ func (v *version) scanDir(path string) (versionedNodeMap, error) {
return baseNodes, nil return baseNodes, nil
} }
func (v *version) buildDir(dir *versionedDir) error { func (v *version) buildDir(dir *verDir) error {
nodes, err := v.scanDir(dir.node.path) nodes, err := v.scanDir(dir.node.path)
if err != nil { if err != nil {
return err return err
@ -115,14 +110,14 @@ func (v *version) buildDir(dir *versionedDir) error {
for name, node := range nodes { for name, node := range nodes {
if node.flags&NodeFlagDir == NodeFlagDir { if node.flags&NodeFlagDir == NodeFlagDir {
subDir := newVersionedDir(node, dir) subDir := newVerDir(node, dir)
if err := v.buildDir(subDir); err != nil { if err := v.buildDir(subDir); err != nil {
return err return err
} }
dir.dirs[name] = subDir dir.dirs[name] = subDir
} else { } else {
dir.files[name] = newVersionedFile(node, dir) dir.files[name] = newVerFile(node, dir)
} }
} }
@ -130,8 +125,8 @@ func (v *version) buildDir(dir *versionedDir) error {
} }
func (v *version) resolve() error { func (v *version) resolve() error {
node := newVersionedNode("/", v, nil, NodeFlagDir) node := newVerNode("/", v, nil, NodeFlagDir)
root := newVersionedDir(node, nil) root := newVerDir(node, nil)
if err := v.buildDir(root); err != nil { if err := v.buildDir(root); err != nil {
return err return err
@ -163,55 +158,19 @@ func (v *version) Root() (fs.Node, error) {
} }
// //
// versionList // verList
// //
type versionList []*version type verList []*version
func (v versionList) Len() int { func (v verList) Len() int {
return len(v) return len(v)
} }
func (v versionList) Swap(i, j int) { func (v verList) Swap(i, j int) {
v[i], v[j] = v[j], v[i] v[i], v[j] = v[j], v[i]
} }
func (v versionList) Less(i, j int) bool { func (v verList) Less(i, j int) bool {
return v[i].timestamp.Unix() < v[j].timestamp.Unix() return v[i].timestamp.Unix() < v[j].timestamp.Unix()
} }
//
// version helpers
//
func buildNewVersion(base string) error {
name := buildVerName(time.Now())
if err := os.MkdirAll(path.Join(base, name, "root"), 0755); err != nil {
return err
}
return nil
}
func buildVerName(timestamp time.Time) string {
return fmt.Sprintf("ver_%.16x", timestamp.Unix())
}
func parseVerName(name string) (time.Time, error) {
re, err := regexp.Compile(`ver_([0-9a-f]+)$`)
if err != nil {
return time.Unix(0, 0), err
}
matches := re.FindStringSubmatch(name)
if len(matches) < 2 {
return time.Unix(0, 0), errors.New("invalid version identifier")
}
timestamp, err := strconv.ParseInt(matches[1], 16, 64)
if err != nil {
return time.Unix(0, 0), err
}
return time.Unix(timestamp, 0), nil
}