Skip to content

Commit

Permalink
add support for variables and pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
reugn committed Jun 13, 2020
1 parent dea79b9 commit bb2946a
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 19 deletions.
19 changes: 11 additions & 8 deletions rules/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ type Action struct {
Payload string `yaml:"payload"`
}

func (action *Action) echoAction(filePath string) {
log.Printf("[%s] %s\n", filePath, action.Payload)
func (action *Action) echoAction(filePath string, vars *Vars) {
msg := vars.Process(action.Payload, filePath)
log.Printf("[%s] %s\n", filePath, msg)
}

func (action *Action) touchAction(filePath string) {
Expand All @@ -25,17 +26,19 @@ func (action *Action) touchAction(filePath string) {
}
}

func (action *Action) moveAction(filePath string) {
err := os.Rename(filePath, action.Payload)
func (action *Action) moveAction(filePath string, vars *Vars) {
newPath := vars.Process(action.Payload, filePath)
err := os.Rename(filePath, newPath)
if err != nil {
log.Fatalf("Failed to move file: %s to: %s", filePath, action.Payload)
log.Fatalf("Failed to move file: %s to: %s", filePath, newPath)
}
}

func (action *Action) renameAction(filePath string) {
err := os.Rename(filePath, validatePath(path.Dir(filePath))+action.Payload)
func (action *Action) renameAction(filePath string, vars *Vars) {
newFileName := vars.Process(action.Payload, filePath)
err := os.Rename(filePath, validatePath(path.Dir(filePath))+newFileName)
if err != nil {
log.Fatalf("Failed to rename file: %s to: %s", filePath, action.Payload)
log.Fatalf("Failed to rename file: %s to: %s", filePath, newFileName)
}
}

Expand Down
27 changes: 16 additions & 11 deletions rules/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,30 @@ import (
"gopkg.in/yaml.v2"
)

var (
const (
operatorAnd = "AND"
operatorOr = "OR"
)

var (
const (
filterName = "name"
filterExtension = "ext"
filterSize = "size"
filterLastEdited = "lastEdited"
filterContains = "contains"

// Filters is the supported filters list
Filters = [...]string{filterName, filterExtension, filterSize, filterLastEdited, filterContains}
)

var (
const (
actionEcho = "echo"
actionTouch = "touch"
actionMove = "move"
actionRename = "rename"
actionDelete = "delete"
)

var (
// Filters is the supported filters list
Filters = [...]string{filterName, filterExtension, filterSize, filterLastEdited, filterContains}

// Actions is the supported actions list
Actions = [...]string{actionEcho, actionTouch, actionMove, actionRename, actionDelete}
Expand Down Expand Up @@ -99,17 +101,17 @@ func (r *Rule) filtersResult(res []bool) bool {
return true
}

func (r *Rule) runActions(filePath string) {
func (r *Rule) runActions(filePath string, vars *Vars) {
for _, action := range r.Actions {
switch action.Action {
case actionEcho:
action.echoAction(filePath)
action.echoAction(filePath, vars)
case actionTouch:
action.touchAction(filePath)
case actionMove:
action.moveAction(filePath)
action.moveAction(filePath, vars)
case actionRename:
action.renameAction(filePath)
action.renameAction(filePath, vars)
case actionDelete:
action.deleteAction(filePath)
default:
Expand All @@ -120,6 +122,7 @@ func (r *Rule) runActions(filePath string) {

// Config is a multiple rules container
type Config struct {
Vars Vars `yaml:"vars"`
Rules []Rule `yaml:"rules"`
wg sync.WaitGroup
}
Expand Down Expand Up @@ -148,6 +151,7 @@ func ReadConfigFromFile(file string) *Config {
// m, _ := yaml.Marshal(c)
// log.Printf("Parsed configuration file\n%s\n", string(m))

c.Vars.init()
return c
}

Expand All @@ -161,6 +165,7 @@ func ReadConfigFromByteArray(configYaml []byte) *Config {
return nil
}

c.Vars.init()
return c
}

Expand Down Expand Up @@ -196,7 +201,7 @@ func (c *Config) iterate(rule Rule) {
defer c.wg.Done()

if rule.checkFilters(path) {
rule.runActions(path)
rule.runActions(path, &c.Vars)
}
}(filePath)
}
Expand Down
47 changes: 47 additions & 0 deletions rules/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package rules

import (
"fmt"
"log"
"strconv"
"strings"
)

const (
trimFunc = "trim"
upperFunc = "upper"
lowerFunc = "lower"
quoteFunc = "quote"
headFunc = "head"
lenFunc = "len"
)

// Pipeline chains together a series of template commands to compactly express a series of transformations
func Pipeline(in string, chain []string) string {
out := in

for _, f := range chain {
switch strings.ToLower(f) {
case trimFunc:
out = strings.TrimSpace(out)
case upperFunc:
out = strings.ToUpper(out)
case lowerFunc:
out = strings.ToLower(out)
case quoteFunc:
out = fmt.Sprintf("\"%s\"", out)
case lenFunc:
out = strconv.Itoa(len(out))
}

if strings.HasPrefix(f, headFunc) {
tkn := strings.Split(f, " ")
if len(tkn) != 2 {
log.Fatalf("Invalid len function configuration: %s", f)
}
out = out[:len(tkn[1])]
}
}

return out
}
145 changes: 145 additions & 0 deletions rules/vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package rules

import (
"log"
"os"
"regexp"
"strconv"
"strings"
"time"

"github.com/reugn/fsweeper/ospkg"
"gopkg.in/yaml.v2"
)

const (
defaultTimeFormat = "15-04-05"
defaultDateFormat = "2006-01-02"
defaultDateTimeFormat = "2006-01-02[15-04-05]"
)

const (
fileSizeVar = ".FileSize"
fileNameVar = ".FileName"
filePathVar = ".FilePath"
fileExtVar = ".FileExt"
timeVar = ".Time"
dateVar = ".Date"
dateTimeVar = ".DateTime"
tsVar = ".Ts"
)

var re *regexp.Regexp = regexp.MustCompile(`\{\{(.+?)\}\}`)

// Vars are the configuration variables to substitute
type Vars struct {
TimeFormat string `yaml:"timeFormat"`
DateFormat string `yaml:"dateFormat"`
DateTimeFormat string `yaml:"dateTimeFormat"`
now time.Time
}

// String is a Stringer interface implementation
func (v *Vars) String() string {
arr, _ := yaml.Marshal(*v)
return string(arr)
}

// Process payload string
func (v *Vars) Process(str string, ctx string) string {
compile := func(s string) string {
return v.processVariableBlock(s, ctx)
}
return re.ReplaceAllStringFunc(str, compile)
}

// compile single variable block
func (v *Vars) processVariableBlock(variable string, ctx string) string {
variable = strings.Trim(variable, "{{")
variable = strings.Trim(variable, "}}")
tokens := strings.Split(variable, "|")

// trim spaces
for i := range tokens {
tokens[i] = strings.TrimSpace(tokens[i])
}

variable, chain := tokens[0], tokens[1:]

var value string
switch variable {
case fileSizeVar:
value = getFileSize(ctx)
case fileNameVar:
value = getFileName(ctx)
case filePathVar:
value = ctx
case fileExtVar:
value = getFileExtension(ctx)
case timeVar:
value = v.getTime()
case dateVar:
value = v.getDate()
case dateTimeVar:
value = v.getDateTime()
case tsVar:
value = strconv.FormatInt(v.now.Unix(), 10)
default:
log.Fatalf("Unknown variable configuration: %s", variable)
}

return Pipeline(value, chain)
}

// init variables, set default value if not set
func (v *Vars) init() {
if v.TimeFormat == "" {
v.TimeFormat = defaultTimeFormat
}

if v.DateFormat == "" {
v.DateFormat = defaultDateFormat
}

if v.DateTimeFormat == "" {
v.DateTimeFormat = defaultDateTimeFormat
}

v.now = time.Now()
}

// returns file size
func getFileSize(filePath string) string {
fi, err := os.Stat(filePath)
if err != nil {
log.Fatalf("Failed to get %s Stat()", filePath)
}
return strconv.FormatInt(fi.Size(), 10)
}

// returns file name
func getFileName(filePath string) string {
p := strings.Split(filePath, ospkg.PathSeparator)
return p[len(p)-1]
}

// returns file extension
func getFileExtension(filePath string) string {
p := strings.Split(filePath, ".")
return p[len(p)-1]
}

// returns formatted time
func (v *Vars) getTime() string {
return v.now.Format(v.TimeFormat)
}

// returns formatted date
func (v *Vars) getDate() string {
return v.now.Format(v.DateFormat)
}

// returns formatted datetime
func (v *Vars) getDateTime() string {
return v.now.Format(v.DateTimeFormat)
}

0 comments on commit bb2946a

Please sign in to comment.