ReplaceMagicVar only had filemanager callers and took 9 positional params (three same-typed strings, two adjacent bools). It now lives in pkg/filemanager/fs taking a named MagicVarProps. BuildRegexp, BuildConcat and the asymmetric EncodeTimeFlowString pair had no production callers — deleted along with their tests. Authored By: TDvorak <info@tdvorak.dev> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>pull/3582/head
parent
26fcb1b2eb
commit
053274afef
@ -0,0 +1,101 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
// MagicVarProps carries the context available to magic-variable expansion in
|
||||
// path templates.
|
||||
type MagicVarProps struct {
|
||||
FsSeparator string
|
||||
PathAvailable bool
|
||||
BlobAvailable bool
|
||||
Time time.Time
|
||||
UserID int
|
||||
OriginName string
|
||||
OriginPath string
|
||||
CompleteBlobPath string
|
||||
}
|
||||
|
||||
var magicVarRe = regexp.MustCompile(`\{[^{}]+\}`)
|
||||
|
||||
// ReplaceMagicVar expands {var} placeholders in rawString using p.
|
||||
func ReplaceMagicVar(rawString string, p MagicVarProps) string {
|
||||
return magicVarRe.ReplaceAllStringFunc(rawString, func(match string) string {
|
||||
switch match {
|
||||
case "{randomkey16}":
|
||||
return util.RandStringRunes(16)
|
||||
case "{randomkey8}":
|
||||
return util.RandStringRunes(8)
|
||||
case "{timestamp}":
|
||||
return strconv.FormatInt(p.Time.Unix(), 10)
|
||||
case "{timestamp_nano}":
|
||||
return strconv.FormatInt(p.Time.UnixNano(), 10)
|
||||
case "{randomnum2}":
|
||||
return strconv.Itoa(rand.Intn(2))
|
||||
case "{randomnum3}":
|
||||
return strconv.Itoa(rand.Intn(3))
|
||||
case "{randomnum4}":
|
||||
return strconv.Itoa(rand.Intn(4))
|
||||
case "{randomnum8}":
|
||||
return strconv.Itoa(rand.Intn(8))
|
||||
case "{uid}":
|
||||
return strconv.Itoa(p.UserID)
|
||||
case "{datetime}":
|
||||
return p.Time.Format("20060102150405")
|
||||
case "{date}":
|
||||
return p.Time.Format("20060102")
|
||||
case "{year}":
|
||||
return p.Time.Format("2006")
|
||||
case "{month}":
|
||||
return p.Time.Format("01")
|
||||
case "{day}":
|
||||
return p.Time.Format("02")
|
||||
case "{hour}":
|
||||
return p.Time.Format("15")
|
||||
case "{minute}":
|
||||
return p.Time.Format("04")
|
||||
case "{second}":
|
||||
return p.Time.Format("05")
|
||||
case "{uuid}":
|
||||
return uuid.Must(uuid.NewV4()).String()
|
||||
case "{ext}":
|
||||
return filepath.Ext(p.OriginName)
|
||||
case "{originname}":
|
||||
return p.OriginName
|
||||
case "{originname_without_ext}":
|
||||
return strings.TrimSuffix(p.OriginName, filepath.Ext(p.OriginName))
|
||||
case "{path}":
|
||||
if p.PathAvailable {
|
||||
return p.OriginPath + p.FsSeparator
|
||||
}
|
||||
return match
|
||||
case "{blob_name}":
|
||||
if p.BlobAvailable {
|
||||
return filepath.Base(p.CompleteBlobPath)
|
||||
}
|
||||
return match
|
||||
case "{blob_name_without_ext}":
|
||||
if p.BlobAvailable {
|
||||
return strings.TrimSuffix(filepath.Base(p.CompleteBlobPath), filepath.Ext(p.CompleteBlobPath))
|
||||
}
|
||||
return match
|
||||
case "{blob_path}":
|
||||
if p.BlobAvailable {
|
||||
return path.Dir(p.CompleteBlobPath) + p.FsSeparator
|
||||
}
|
||||
return match
|
||||
default:
|
||||
return match
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReplaceMagicVar(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
now := time.Date(2024, 3, 5, 6, 7, 8, 0, time.UTC)
|
||||
props := MagicVarProps{
|
||||
FsSeparator: "/",
|
||||
PathAvailable: true,
|
||||
BlobAvailable: true,
|
||||
Time: now,
|
||||
UserID: 42,
|
||||
OriginName: "photo.jpg",
|
||||
OriginPath: "album/2024",
|
||||
CompleteBlobPath: "blobs/abc123.bin",
|
||||
}
|
||||
|
||||
a.Equal("u42", ReplaceMagicVar("u{uid}", props))
|
||||
a.Equal("20240305", ReplaceMagicVar("{date}", props))
|
||||
a.Equal("20240305060708", ReplaceMagicVar("{datetime}", props))
|
||||
a.Equal("2024/03/05", ReplaceMagicVar("{year}/{month}/{day}", props))
|
||||
a.Equal("photo.jpg", ReplaceMagicVar("{originname}", props))
|
||||
a.Equal("photo", ReplaceMagicVar("{originname_without_ext}", props))
|
||||
a.Equal(".jpg", ReplaceMagicVar("{ext}", props))
|
||||
a.Equal("album/2024/", ReplaceMagicVar("{path}", props))
|
||||
a.Equal("abc123.bin", ReplaceMagicVar("{blob_name}", props))
|
||||
a.Equal("abc123", ReplaceMagicVar("{blob_name_without_ext}", props))
|
||||
a.Equal("blobs/", ReplaceMagicVar("{blob_path}", props))
|
||||
a.Equal("{unknown}", ReplaceMagicVar("{unknown}", props))
|
||||
|
||||
// Unavailable context leaves the placeholder untouched.
|
||||
noPath := props
|
||||
noPath.PathAvailable = false
|
||||
a.Equal("{path}", ReplaceMagicVar("{path}", noPath))
|
||||
noBlob := props
|
||||
noBlob.BlobAvailable = false
|
||||
a.Equal("{blob_name}", ReplaceMagicVar("{blob_name}", noBlob))
|
||||
|
||||
// Random vars produce non-empty output.
|
||||
a.Len(ReplaceMagicVar("{randomkey8}", props), 8)
|
||||
a.Len(ReplaceMagicVar("{uuid}", props), 36)
|
||||
}
|
||||
Loading…
Reference in new issue