lib/transform: add transform library and --name-transform flag

lib/transform adds the transform library, supporting advanced path name
transformations for converting and renaming files and directories by applying
prefixes, suffixes, and other alterations.

It also adds the --name-transform flag for use with sync, copy, and move.

Multiple transformations can be used in sequence, applied in the order they are
specified on the command line.

By default --name-transform will only apply to file names. The means only the leaf
file name will be transformed. However some of the transforms would be better
applied to the whole path or just directories. To choose which which part of the
file path is affected some tags can be added to the --name-transform:

file	Only transform the leaf name of files (DEFAULT)
dir	Only transform name of directories - these may appear anywhere in the path
all	Transform the entire path for files and directories

Example syntax:
--name-transform file,prefix=ABC
--name-transform dir,prefix=DEF
This commit is contained in:
nielash
2025-05-04 03:10:48 -04:00
committed by Nick Craig-Wood
parent 41a407dcc9
commit 013c563293
18 changed files with 1529 additions and 224 deletions

View File

@ -151,8 +151,8 @@ func init() {
alias("Dot", EncodeDot)
}
// validStrings returns all the valid MultiEncoder strings
func validStrings() string {
// ValidStrings returns all the valid MultiEncoder strings
func ValidStrings() string {
var out []string
for k := range nameToEncoding {
out = append(out, k)
@ -192,7 +192,7 @@ func (mask *MultiEncoder) Set(in string) error {
} else {
i, err := strconv.ParseUint(part, 0, 0)
if err != nil {
return fmt.Errorf("bad encoding %q: possible values are: %s", part, validStrings())
return fmt.Errorf("bad encoding %q: possible values are: %s", part, ValidStrings())
}
out |= MultiEncoder(i)
}
@ -313,8 +313,7 @@ func (mask MultiEncoder) Encode(in string) string {
}
if mask.Has(EncodeAsterisk) { // *
switch r {
case '*',
'':
case '*', '':
return true
}
}
@ -346,64 +345,55 @@ func (mask MultiEncoder) Encode(in string) string {
}
if mask.Has(EncodeQuestion) { // ?
switch r {
case '?',
'':
case '?', '':
return true
}
}
if mask.Has(EncodeColon) { // :
switch r {
case ':',
'':
case ':', '':
return true
}
}
if mask.Has(EncodePipe) { // |
switch r {
case '|',
'':
case '|', '':
return true
}
}
if mask.Has(EncodeDoubleQuote) { // "
switch r {
case '"',
'':
case '"', '':
return true
}
}
if mask.Has(EncodeSingleQuote) { // '
switch r {
case '\'',
'':
case '\'', '':
return true
}
}
if mask.Has(EncodeBackQuote) { // `
switch r {
case '`',
'':
case '`', '':
return true
}
}
if mask.Has(EncodeDollar) { // $
switch r {
case '$',
'':
case '$', '':
return true
}
}
if mask.Has(EncodeSlash) { // /
switch r {
case '/',
'':
case '/', '':
return true
}
}
if mask.Has(EncodeBackSlash) { // \
switch r {
case '\\',
'':
case '\\', '':
return true
}
}
@ -416,15 +406,13 @@ func (mask MultiEncoder) Encode(in string) string {
}
if mask.Has(EncodeHash) { // #
switch r {
case '#',
'':
case '#', '':
return true
}
}
if mask.Has(EncodePercent) { // %
switch r {
case '%',
'':
case '%', '':
return true
}
}
@ -1182,6 +1170,7 @@ func appendQuotedBytes(w io.Writer, s string) {
_, _ = fmt.Fprintf(w, string(QuoteRune)+"%02X", b)
}
}
func appendUnquotedByte(w io.Writer, s string) bool {
if len(s) < 2 {
return false
@ -1202,12 +1191,15 @@ func (identity) Decode(in string) string { return in }
func (i identity) FromStandardPath(s string) string {
return FromStandardPath(i, s)
}
func (i identity) FromStandardName(s string) string {
return FromStandardName(i, s)
}
func (i identity) ToStandardPath(s string) string {
return ToStandardPath(i, s)
}
func (i identity) ToStandardName(s string) string {
return ToStandardName(i, s)
}