mirror of
https://github.com/rclone/rclone.git
synced 2025-01-24 12:56:36 +02:00
b2: factor version handling into lib/version
Standardizes the filename version tagging so that it can be used by any backend.
This commit is contained in:
parent
c1492cfa28
commit
c163e6b250
@ -2,12 +2,11 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
|
"github.com/rclone/rclone/lib/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error describes a B2 error response
|
// Error describes a B2 error response
|
||||||
@ -63,16 +62,17 @@ func (t *Timestamp) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const versionFormat = "-v2006-01-02-150405.000"
|
// HasVersion returns true if it looks like the passed filename has a timestamp on it.
|
||||||
|
//
|
||||||
|
// Note that the passed filename's timestamp may still be invalid even if this
|
||||||
|
// function returns true.
|
||||||
|
func HasVersion(remote string) bool {
|
||||||
|
return version.Match(remote)
|
||||||
|
}
|
||||||
|
|
||||||
// AddVersion adds the timestamp as a version string into the filename passed in.
|
// AddVersion adds the timestamp as a version string into the filename passed in.
|
||||||
func (t Timestamp) AddVersion(remote string) string {
|
func (t Timestamp) AddVersion(remote string) string {
|
||||||
ext := path.Ext(remote)
|
return version.Add(remote, time.Time(t))
|
||||||
base := remote[:len(remote)-len(ext)]
|
|
||||||
s := time.Time(t).Format(versionFormat)
|
|
||||||
// Replace the '.' with a '-'
|
|
||||||
s = strings.Replace(s, ".", "-", -1)
|
|
||||||
return base + s + ext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveVersion removes the timestamp from a filename as a version string.
|
// RemoveVersion removes the timestamp from a filename as a version string.
|
||||||
@ -80,24 +80,9 @@ func (t Timestamp) AddVersion(remote string) string {
|
|||||||
// It returns the new file name and a timestamp, or the old filename
|
// It returns the new file name and a timestamp, or the old filename
|
||||||
// and a zero timestamp.
|
// and a zero timestamp.
|
||||||
func RemoveVersion(remote string) (t Timestamp, newRemote string) {
|
func RemoveVersion(remote string) (t Timestamp, newRemote string) {
|
||||||
newRemote = remote
|
time, newRemote := version.Remove(remote)
|
||||||
ext := path.Ext(remote)
|
t = Timestamp(time)
|
||||||
base := remote[:len(remote)-len(ext)]
|
return
|
||||||
if len(base) < len(versionFormat) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
versionStart := len(base) - len(versionFormat)
|
|
||||||
// Check it ends in -xxx
|
|
||||||
if base[len(base)-4] != '-' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Replace with .xxx for parsing
|
|
||||||
base = base[:len(base)-4] + "." + base[len(base)-3:]
|
|
||||||
newT, err := time.Parse(versionFormat, base[versionStart:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return Timestamp(newT), base[:versionStart] + ext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsZero returns true if the timestamp is uninitialized
|
// IsZero returns true if the timestamp is uninitialized
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
emptyT api.Timestamp
|
emptyT api.Timestamp
|
||||||
t0 = api.Timestamp(fstest.Time("1970-01-01T01:01:01.123456789Z"))
|
t0 = api.Timestamp(fstest.Time("1970-01-01T01:01:01.123456789Z"))
|
||||||
t0r = api.Timestamp(fstest.Time("1970-01-01T01:01:01.123000000Z"))
|
|
||||||
t1 = api.Timestamp(fstest.Time("2001-02-03T04:05:06.123000000Z"))
|
t1 = api.Timestamp(fstest.Time("2001-02-03T04:05:06.123000000Z"))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,40 +35,6 @@ func TestTimestampUnmarshalJSON(t *testing.T) {
|
|||||||
assert.Equal(t, (time.Time)(t1), (time.Time)(tActual))
|
assert.Equal(t, (time.Time)(t1), (time.Time)(tActual))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimestampAddVersion(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
t api.Timestamp
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{t0, "potato.txt", "potato-v1970-01-01-010101-123.txt"},
|
|
||||||
{t1, "potato", "potato-v2001-02-03-040506-123"},
|
|
||||||
{t1, "", "-v2001-02-03-040506-123"},
|
|
||||||
} {
|
|
||||||
actual := test.t.AddVersion(test.in)
|
|
||||||
assert.Equal(t, test.expected, actual, test.in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampRemoveVersion(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
in string
|
|
||||||
expectedT api.Timestamp
|
|
||||||
expectedRemote string
|
|
||||||
}{
|
|
||||||
{"potato.txt", emptyT, "potato.txt"},
|
|
||||||
{"potato-v1970-01-01-010101-123.txt", t0r, "potato.txt"},
|
|
||||||
{"potato-v2001-02-03-040506-123", t1, "potato"},
|
|
||||||
{"-v2001-02-03-040506-123", t1, ""},
|
|
||||||
{"potato-v2A01-02-03-040506-123", emptyT, "potato-v2A01-02-03-040506-123"},
|
|
||||||
{"potato-v2001-02-03-040506=123", emptyT, "potato-v2001-02-03-040506=123"},
|
|
||||||
} {
|
|
||||||
actualT, actualRemote := api.RemoveVersion(test.in)
|
|
||||||
assert.Equal(t, test.expectedT, actualT, test.in)
|
|
||||||
assert.Equal(t, test.expectedRemote, actualRemote, test.in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampIsZero(t *testing.T) {
|
func TestTimestampIsZero(t *testing.T) {
|
||||||
assert.True(t, emptyT.IsZero())
|
assert.True(t, emptyT.IsZero())
|
||||||
assert.False(t, t0.IsZero())
|
assert.False(t, t0.IsZero())
|
||||||
|
52
lib/version/version.go
Normal file
52
lib/version/version.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Package version provides machinery for versioning file names
|
||||||
|
// with a timestamp-based version string
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const versionFormat = "-v2006-01-02-150405.000"
|
||||||
|
|
||||||
|
var versionRegexp = regexp.MustCompile("-v\\d{4}-\\d{2}-\\d{2}-\\d{6}-\\d{3}")
|
||||||
|
|
||||||
|
// Add returns fileName modified to include t as the version
|
||||||
|
func Add(fileName string, t time.Time) string {
|
||||||
|
ext := path.Ext(fileName)
|
||||||
|
base := fileName[:len(fileName)-len(ext)]
|
||||||
|
s := t.Format(versionFormat)
|
||||||
|
// Replace the '.' with a '-'
|
||||||
|
s = strings.Replace(s, ".", "-", -1)
|
||||||
|
return base + s + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove returns a modified fileName without the version string and the time it represented
|
||||||
|
// If the fileName did not have a version then time.Time{} is returned along with an unmodified fileName
|
||||||
|
func Remove(fileName string) (t time.Time, fileNameWithoutVersion string) {
|
||||||
|
fileNameWithoutVersion = fileName
|
||||||
|
ext := path.Ext(fileName)
|
||||||
|
base := fileName[:len(fileName)-len(ext)]
|
||||||
|
if len(base) < len(versionFormat) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
versionStart := len(base) - len(versionFormat)
|
||||||
|
// Check it ends in -xxx
|
||||||
|
if base[len(base)-4] != '-' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Replace with .xxx for parsing
|
||||||
|
base = base[:len(base)-4] + "." + base[len(base)-3:]
|
||||||
|
newT, err := time.Parse(versionFormat, base[versionStart:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return newT, base[:versionStart] + ext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the fileName has a version string
|
||||||
|
func Match(fileName string) bool {
|
||||||
|
return versionRegexp.MatchString(fileName)
|
||||||
|
}
|
73
lib/version/version_test.go
Normal file
73
lib/version/version_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package version_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rclone/rclone/fstest"
|
||||||
|
"github.com/rclone/rclone/lib/version"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyT time.Time
|
||||||
|
t0 = fstest.Time("1970-01-01T01:01:01.123456789Z")
|
||||||
|
t0r = fstest.Time("1970-01-01T01:01:01.123000000Z")
|
||||||
|
t1 = fstest.Time("2001-02-03T04:05:06.123000000Z")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionAdd(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
t time.Time
|
||||||
|
in string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{t0, "potato.txt", "potato-v1970-01-01-010101-123.txt"},
|
||||||
|
{t0, "potato-v2001-02-03-040506-123.txt", "potato-v2001-02-03-040506-123-v1970-01-01-010101-123.txt"},
|
||||||
|
{t0, "123.!!lipps", "123-v1970-01-01-010101-123.!!lipps"},
|
||||||
|
{t1, "potato", "potato-v2001-02-03-040506-123"},
|
||||||
|
{t1, "", "-v2001-02-03-040506-123"},
|
||||||
|
} {
|
||||||
|
actual := version.Add(test.in, test.t)
|
||||||
|
assert.Equal(t, test.expected, actual, test.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionRemove(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
in string
|
||||||
|
expectedT time.Time
|
||||||
|
expectedRemote string
|
||||||
|
}{
|
||||||
|
{"potato.txt", emptyT, "potato.txt"},
|
||||||
|
{"potato-v1970-01-01-010101-123.txt", t0r, "potato.txt"},
|
||||||
|
{"potato-v2001-02-03-040506-123-v1970-01-01-010101-123.txt", t0r, "potato-v2001-02-03-040506-123.txt"},
|
||||||
|
{"potato-v2001-02-03-040506-123", t1, "potato"},
|
||||||
|
{"-v2001-02-03-040506-123", t1, ""},
|
||||||
|
{"potato-v2A01-02-03-040506-123", emptyT, "potato-v2A01-02-03-040506-123"},
|
||||||
|
{"potato-v2001-02-03-040506=123", emptyT, "potato-v2001-02-03-040506=123"},
|
||||||
|
} {
|
||||||
|
actualT, actualRemote := version.Remove(test.in)
|
||||||
|
assert.Equal(t, test.expectedT, actualT, test.in)
|
||||||
|
assert.Equal(t, test.expectedRemote, actualRemote, test.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionMatch(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
in string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"potato.txt", false},
|
||||||
|
{"potato", false},
|
||||||
|
{"", false},
|
||||||
|
{"potato-v1970-01-01-010101-123.txt", true},
|
||||||
|
{"potato-v2001-02-03-040506-123-v1970-01-01-010101-123.txt", true},
|
||||||
|
{"potato-v2001-02-03-040506-123", true},
|
||||||
|
{"-v2001-02-03-040506-123", true},
|
||||||
|
{"-v9999-99-99-999999-999", true},
|
||||||
|
} {
|
||||||
|
actual := version.Match(test.in)
|
||||||
|
assert.Equal(t, test.expected, actual, test.in)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user