Implement yaml format
This commit is contained in:
140
instruction.go
140
instruction.go
@@ -5,17 +5,21 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type LinkInstruction struct {
|
||||
Source string
|
||||
Target string
|
||||
Force bool
|
||||
Hard bool
|
||||
Delete bool
|
||||
Source string `yaml:"source"`
|
||||
Target string `yaml:"target"`
|
||||
Force bool `yaml:"force,omitempty"`
|
||||
Hard bool `yaml:"hard,omitempty"`
|
||||
Delete bool `yaml:"delete,omitempty"`
|
||||
}
|
||||
|
||||
type YAMLConfig struct {
|
||||
Links []LinkInstruction `yaml:"links"`
|
||||
}
|
||||
|
||||
func (instruction *LinkInstruction) Tidy() {
|
||||
@@ -29,7 +33,26 @@ func (instruction *LinkInstruction) Tidy() {
|
||||
}
|
||||
|
||||
func (instruction *LinkInstruction) String() string {
|
||||
return fmt.Sprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s", SourceColor, instruction.Source, DefaultColor, deliminer, TargetColor, instruction.Target, DefaultColor, deliminer, strconv.FormatBool(instruction.Force), deliminer, strconv.FormatBool(instruction.Hard), deliminer, strconv.FormatBool(instruction.Delete))
|
||||
var flags []string
|
||||
if instruction.Force {
|
||||
flags = append(flags, "force=true")
|
||||
}
|
||||
if instruction.Hard {
|
||||
flags = append(flags, "hard=true")
|
||||
}
|
||||
if instruction.Delete {
|
||||
flags = append(flags, "delete=true")
|
||||
}
|
||||
|
||||
flagsStr := ""
|
||||
if len(flags) > 0 {
|
||||
flagsStr = " [" + strings.Join(flags, ", ") + "]"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s%s → %s%s%s%s",
|
||||
SourceColor, instruction.Source, DefaultColor,
|
||||
TargetColor, instruction.Target, DefaultColor,
|
||||
flagsStr)
|
||||
}
|
||||
|
||||
func ParseInstruction(line, workdir string) (LinkInstruction, error) {
|
||||
@@ -37,6 +60,7 @@ func ParseInstruction(line, workdir string) (LinkInstruction, error) {
|
||||
if strings.HasPrefix(line, "#") {
|
||||
return LinkInstruction{}, fmt.Errorf("comment line")
|
||||
}
|
||||
|
||||
parts := strings.Split(line, deliminer)
|
||||
instruction := LinkInstruction{}
|
||||
|
||||
@@ -46,19 +70,48 @@ func ParseInstruction(line, workdir string) (LinkInstruction, error) {
|
||||
|
||||
instruction.Source = strings.TrimSpace(parts[0])
|
||||
instruction.Target = strings.TrimSpace(parts[1])
|
||||
instruction.Force = false
|
||||
if len(parts) > 2 {
|
||||
res, _ := regexp.MatchString(`^(?i)\s*T|TRUE\s*$`, parts[2])
|
||||
instruction.Force = res
|
||||
}
|
||||
if len(parts) > 3 {
|
||||
res, _ := regexp.MatchString(`^(?i)\s*T|TRUE\s*$`, parts[3])
|
||||
instruction.Hard = res
|
||||
}
|
||||
if len(parts) > 4 {
|
||||
res, _ := regexp.MatchString(`^(?i)\s*T|TRUE\s*$`, parts[4])
|
||||
instruction.Delete = res
|
||||
instruction.Force = res
|
||||
|
||||
for i := 2; i < len(parts); i++ {
|
||||
flagPart := strings.TrimSpace(parts[i])
|
||||
|
||||
// Support for legacy format (backward compatibility)
|
||||
if !strings.Contains(flagPart, "=") {
|
||||
// Legacy format: positional boolean flags
|
||||
switch i {
|
||||
case 2: // Force flag (3rd position)
|
||||
instruction.Force = isTrue(flagPart)
|
||||
case 3: // Hard flag (4th position)
|
||||
instruction.Hard = isTrue(flagPart)
|
||||
case 4: // Delete flag (5th position)
|
||||
instruction.Delete = isTrue(flagPart)
|
||||
if instruction.Delete {
|
||||
instruction.Force = true // Delete implies Force
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// New format: named flags (name=value)
|
||||
nameValue := strings.SplitN(flagPart, "=", 2)
|
||||
if len(nameValue) != 2 {
|
||||
// Skip malformed flags
|
||||
continue
|
||||
}
|
||||
|
||||
flagName := strings.ToLower(strings.TrimSpace(nameValue[0]))
|
||||
flagValue := strings.TrimSpace(nameValue[1])
|
||||
|
||||
switch flagName {
|
||||
case "force", "f":
|
||||
instruction.Force = isTrue(flagValue)
|
||||
case "hard", "h":
|
||||
instruction.Hard = isTrue(flagValue)
|
||||
case "delete", "d":
|
||||
instruction.Delete = isTrue(flagValue)
|
||||
if instruction.Delete {
|
||||
instruction.Force = true // Delete implies Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instruction.Tidy()
|
||||
@@ -71,6 +124,11 @@ func ParseInstruction(line, workdir string) (LinkInstruction, error) {
|
||||
return instruction, nil
|
||||
}
|
||||
|
||||
func isTrue(value string) bool {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
return value == "true" || value == "t" || value == "yes" || value == "y" || value == "1"
|
||||
}
|
||||
|
||||
func (instruction *LinkInstruction) RunAsync(status chan (error)) {
|
||||
defer close(status)
|
||||
if !FileExists(instruction.Source) {
|
||||
@@ -155,3 +213,43 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) {
|
||||
|
||||
status <- nil
|
||||
}
|
||||
|
||||
func ParseYAMLFile(filename, workdir string) ([]LinkInstruction, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading YAML file: %w", err)
|
||||
}
|
||||
|
||||
// First try to parse as a list of link instructions
|
||||
var config YAMLConfig
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
if err != nil || len(config.Links) == 0 {
|
||||
// If that fails, try parsing as a direct list of instructions
|
||||
var instructions []LinkInstruction
|
||||
err = yaml.Unmarshal(data, &instructions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing YAML: %w", err)
|
||||
}
|
||||
config.Links = instructions
|
||||
}
|
||||
|
||||
for i := range config.Links {
|
||||
config.Links[i].Tidy()
|
||||
config.Links[i].Source, _ = ConvertHome(config.Links[i].Source)
|
||||
config.Links[i].Target, _ = ConvertHome(config.Links[i].Target)
|
||||
config.Links[i].Source = NormalizePath(config.Links[i].Source, workdir)
|
||||
config.Links[i].Target = NormalizePath(config.Links[i].Target, workdir)
|
||||
|
||||
// If Delete is true, Force must also be true
|
||||
if config.Links[i].Delete {
|
||||
config.Links[i].Force = true
|
||||
}
|
||||
}
|
||||
|
||||
return config.Links, nil
|
||||
}
|
||||
|
||||
func IsYAMLFile(filename string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
return ext == ".yaml" || ext == ".yml"
|
||||
}
|
||||
|
Reference in New Issue
Block a user