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