package scheduler import ( "fmt" "io/ioutil" "path/filepath" "regexp" "strings" "time" ) // ProcessOutputPattern processes an output pattern with variables and returns the result // This function is useful for testing pattern processing in isolation func ProcessOutputPattern(pattern string, originalFilename string) string { // Process date variables dateRegex := regexp.MustCompile(`\${date:([^}]+)}`) processedPattern := dateRegex.ReplaceAllStringFunc(pattern, func(match string) string { format := dateRegex.FindStringSubmatch(match)[1] return time.Now().Format(format) }) // Split the filename and extension ext := filepath.Ext(originalFilename) filename := strings.TrimSuffix(originalFilename, ext) // Replace filename and extension variables processedPattern = strings.ReplaceAll(processedPattern, "${filename}", filename) // Remove leading dot from ext before replacing processedPattern = strings.ReplaceAll(processedPattern, "${ext}", strings.TrimPrefix(ext, ".")) return processedPattern } // createRcloneFilterFile creates a temporary filter file for rclone with rename rules func createRcloneFilterFile(pattern string) (string, error) { // Create a temporary file tmpFile, err := ioutil.TempFile("", "rclone-filter-*.txt") if err != nil { return "", fmt.Errorf("failed to create temporary filter file: %v", err) } defer tmpFile.Close() // Process the pattern to create a rclone filter rule // First, replace date variables with current date in the specified format dateRegex := regexp.MustCompile(`\${date:([^}]+)}`) processedPattern := dateRegex.ReplaceAllStringFunc(pattern, func(match string) string { format := dateRegex.FindStringSubmatch(match)[1] return time.Now().Format(format) }) // Replace filename and extension variables with rclone's capture group references // For rclone rename filters, we need to use {1} for the first capture group, not $1 // See: https://rclone.org/filtering/#rename // Extract filename without extension processedPattern = strings.ReplaceAll(processedPattern, "${filename}", "{1}") // Extract extension (with the dot) processedPattern = strings.ReplaceAll(processedPattern, "${ext}", "{2}") // Create a rename rule for rclone using the correct syntax: // - The format for rename filters is: "-- SourceRegexp ReplacementPattern" // - For files with extension: capture the name and extension separately rule := fmt.Sprintf("-- (.*)(\\..+)$ %s\n", processedPattern) // Correct escaping for dot // Add a fallback rule for files without extension // Keep [^.] as it correctly excludes literal dot in character class fallbackRule := fmt.Sprintf("-- ([^.]+)$ %s\n", strings.ReplaceAll(processedPattern, "{2}", "")) // Removed duplicate declaration below // Write the rules to the file if _, err := tmpFile.WriteString(rule + fallbackRule); err != nil { return "", fmt.Errorf("failed to write to filter file: %v", err) } return tmpFile.Name(), nil }