mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-09 14:28:01 -05:00
CHANGES - Add --debug flag controlling runtime logging verbosity levels - Introduce internal/log package with Off, Basic, Detailed, Trace - Replace ad-hoc Debugf and globals with centralized debug logger - Wire debug level during early CLI argument parsing - Add bash, zsh, fish completions for --debug levels - Document debug levels in README with usage examples - Add comprehensive STT guide covering models, flags, workflows - Simplify splitAudioFile signature and log ffmpeg chunking operations - Remove FABRIC_STT_DEBUG environment variable and related code - Clean minor code paths in vendors and template modules
245 lines
6.9 KiB
Go
245 lines
6.9 KiB
Go
package template
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
debuglog "github.com/danielmiessler/fabric/internal/log"
|
|
)
|
|
|
|
var (
|
|
textPlugin = &TextPlugin{}
|
|
datetimePlugin = &DateTimePlugin{}
|
|
filePlugin = &FilePlugin{}
|
|
fetchPlugin = &FetchPlugin{}
|
|
sysPlugin = &SysPlugin{}
|
|
)
|
|
|
|
var extensionManager *ExtensionManager
|
|
|
|
func init() {
|
|
homedir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
debugf("Warning: could not initialize extension manager: %v\n", err)
|
|
}
|
|
configDir := filepath.Join(homedir, ".config/fabric")
|
|
extensionManager = NewExtensionManager(configDir)
|
|
// Extensions will work if registry exists, otherwise they'll just fail gracefully
|
|
}
|
|
|
|
var pluginPattern = regexp.MustCompile(`\{\{plugin:([^:]+):([^:]+)(?::([^}]+))?\}\}`)
|
|
var extensionPattern = regexp.MustCompile(`\{\{ext:([^:]+):([^:]+)(?::([^}]+))?\}\}`)
|
|
|
|
func debugf(format string, a ...interface{}) {
|
|
debuglog.Debug(debuglog.Trace, format, a...)
|
|
}
|
|
|
|
func ApplyTemplate(content string, variables map[string]string, input string) (string, error) {
|
|
|
|
var missingVars []string
|
|
r := regexp.MustCompile(`\{\{([^{}]+)\}\}`)
|
|
|
|
debugf("Starting template processing\n")
|
|
for strings.Contains(content, "{{") {
|
|
matches := r.FindAllStringSubmatch(content, -1)
|
|
if len(matches) == 0 {
|
|
break
|
|
}
|
|
|
|
replaced := false
|
|
for _, match := range matches {
|
|
fullMatch := match[0]
|
|
varName := match[1]
|
|
|
|
// Check if this is a plugin call
|
|
if strings.HasPrefix(varName, "plugin:") {
|
|
pluginMatches := pluginPattern.FindStringSubmatch(fullMatch)
|
|
if len(pluginMatches) >= 3 {
|
|
namespace := pluginMatches[1]
|
|
operation := pluginMatches[2]
|
|
value := ""
|
|
if len(pluginMatches) == 4 {
|
|
value = pluginMatches[3]
|
|
}
|
|
|
|
debugf("\nPlugin call:\n")
|
|
debugf(" Namespace: %s\n", namespace)
|
|
debugf(" Operation: %s\n", operation)
|
|
debugf(" Value: %s\n", value)
|
|
|
|
var result string
|
|
var err error
|
|
|
|
switch namespace {
|
|
case "text":
|
|
debugf("Executing text plugin\n")
|
|
result, err = textPlugin.Apply(operation, value)
|
|
case "datetime":
|
|
debugf("Executing datetime plugin\n")
|
|
result, err = datetimePlugin.Apply(operation, value)
|
|
case "file":
|
|
debugf("Executing file plugin\n")
|
|
result, err = filePlugin.Apply(operation, value)
|
|
debugf("File plugin result: %#v\n", result)
|
|
case "fetch":
|
|
debugf("Executing fetch plugin\n")
|
|
result, err = fetchPlugin.Apply(operation, value)
|
|
case "sys":
|
|
debugf("Executing sys plugin\n")
|
|
result, err = sysPlugin.Apply(operation, value)
|
|
default:
|
|
return "", fmt.Errorf("unknown plugin namespace: %s", namespace)
|
|
}
|
|
|
|
if err != nil {
|
|
debugf("Plugin error: %v\n", err)
|
|
return "", fmt.Errorf("plugin %s error: %v", namespace, err)
|
|
}
|
|
|
|
debugf("Plugin result: %s\n", result)
|
|
content = strings.ReplaceAll(content, fullMatch, result)
|
|
debugf("Content after replacement: %s\n", content)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if pluginMatches := extensionPattern.FindStringSubmatch(fullMatch); len(pluginMatches) >= 3 {
|
|
name := pluginMatches[1]
|
|
operation := pluginMatches[2]
|
|
value := ""
|
|
if len(pluginMatches) == 4 {
|
|
value = pluginMatches[3]
|
|
}
|
|
|
|
debugf("\nExtension call:\n")
|
|
debugf(" Name: %s\n", name)
|
|
debugf(" Operation: %s\n", operation)
|
|
debugf(" Value: %s\n", value)
|
|
|
|
result, err := extensionManager.ProcessExtension(name, operation, value)
|
|
if err != nil {
|
|
return "", fmt.Errorf("extension %s error: %v", name, err)
|
|
}
|
|
|
|
content = strings.ReplaceAll(content, fullMatch, result)
|
|
replaced = true
|
|
continue
|
|
}
|
|
|
|
// Handle regular variables and input
|
|
debugf("Processing variable: %s\n", varName)
|
|
if varName == "input" {
|
|
debugf("Replacing {{input}}\n")
|
|
replaced = true
|
|
content = strings.ReplaceAll(content, fullMatch, input)
|
|
} else {
|
|
if val, ok := variables[varName]; !ok {
|
|
debugf("Missing variable: %s\n", varName)
|
|
missingVars = append(missingVars, varName)
|
|
return "", fmt.Errorf("missing required variable: %s", varName)
|
|
} else {
|
|
debugf("Replacing variable %s with value: %s\n", varName, val)
|
|
content = strings.ReplaceAll(content, fullMatch, val)
|
|
replaced = true
|
|
}
|
|
}
|
|
if !replaced {
|
|
return "", fmt.Errorf("template processing stuck - potential infinite loop")
|
|
}
|
|
}
|
|
}
|
|
|
|
debugf("Starting template processing\n")
|
|
for strings.Contains(content, "{{") {
|
|
matches := r.FindAllStringSubmatch(content, -1)
|
|
if len(matches) == 0 {
|
|
break
|
|
}
|
|
|
|
replaced := false
|
|
for _, match := range matches {
|
|
fullMatch := match[0]
|
|
varName := match[1]
|
|
|
|
// Check if this is a plugin call
|
|
if strings.HasPrefix(varName, "plugin:") {
|
|
pluginMatches := pluginPattern.FindStringSubmatch(fullMatch)
|
|
if len(pluginMatches) >= 3 {
|
|
namespace := pluginMatches[1]
|
|
operation := pluginMatches[2]
|
|
value := ""
|
|
if len(pluginMatches) == 4 {
|
|
value = pluginMatches[3]
|
|
}
|
|
|
|
debugf("\nPlugin call:\n")
|
|
debugf(" Namespace: %s\n", namespace)
|
|
debugf(" Operation: %s\n", operation)
|
|
debugf(" Value: %s\n", value)
|
|
|
|
var result string
|
|
var err error
|
|
|
|
switch namespace {
|
|
case "text":
|
|
debugf("Executing text plugin\n")
|
|
result, err = textPlugin.Apply(operation, value)
|
|
case "datetime":
|
|
debugf("Executing datetime plugin\n")
|
|
result, err = datetimePlugin.Apply(operation, value)
|
|
case "file":
|
|
debugf("Executing file plugin\n")
|
|
result, err = filePlugin.Apply(operation, value)
|
|
debugf("File plugin result: %#v\n", result)
|
|
case "fetch":
|
|
debugf("Executing fetch plugin\n")
|
|
result, err = fetchPlugin.Apply(operation, value)
|
|
case "sys":
|
|
debugf("Executing sys plugin\n")
|
|
result, err = sysPlugin.Apply(operation, value)
|
|
default:
|
|
return "", fmt.Errorf("unknown plugin namespace: %s", namespace)
|
|
}
|
|
|
|
if err != nil {
|
|
debugf("Plugin error: %v\n", err)
|
|
return "", fmt.Errorf("plugin %s error: %v", namespace, err)
|
|
}
|
|
|
|
debugf("Plugin result: %s\n", result)
|
|
content = strings.ReplaceAll(content, fullMatch, result)
|
|
debugf("Content after replacement: %s\n", content)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Handle regular variables and input
|
|
debugf("Processing variable: %s\n", varName)
|
|
if varName == "input" {
|
|
debugf("Replacing {{input}}\n")
|
|
replaced = true
|
|
content = strings.ReplaceAll(content, fullMatch, input)
|
|
} else {
|
|
if val, ok := variables[varName]; !ok {
|
|
debugf("Missing variable: %s\n", varName)
|
|
missingVars = append(missingVars, varName)
|
|
return "", fmt.Errorf("missing required variable: %s", varName)
|
|
} else {
|
|
debugf("Replacing variable %s with value: %s\n", varName, val)
|
|
content = strings.ReplaceAll(content, fullMatch, val)
|
|
replaced = true
|
|
}
|
|
}
|
|
if !replaced {
|
|
return "", fmt.Errorf("template processing stuck - potential infinite loop")
|
|
}
|
|
}
|
|
}
|
|
|
|
debugf("Template processing complete\n")
|
|
return content, nil
|
|
}
|