package instruction import ( "fmt" "github.com/xdrm-brackets/nix-amer/internal/cnf" "os" "path/filepath" "strings" ) // ErrCannotSet is raised when there is an error trying to update // a field var ErrCannotSet = fmt.Errorf("cannot set the field") type set struct { raw string // File is the path to the file File string // Path is the configuration field path Path string // Value if the value to add or update Value string // Format is the configuration format in use Format *cnf.ConfigurationFormat } func (d *set) Raw() string { return strings.Join([]string{"set", d.raw}, " ") } func (d *set) Build(_args string) error { // 1. extract action (sub command) split := strings.Fields(_args) // 2. check path if len(split) < 2 { return fmt.Errorf("missing configuration path") } path := split[0] value := strings.Join(split[1:], "") // 3. check path separator splitPath := strings.Split(path, "@") if len(splitPath) > 2 { return fmt.Errorf("invalid path (additional '@'?)") } d.File = splitPath[0] if len(splitPath) > 1 { // add field path only if set d.Path = splitPath[1] } // add value d.Value = value d.raw = _args return nil } func (d set) Exec(ctx ExecutionContext) ([]byte, error) { // 1. get file / alias path := d.File if !strings.Contains(path, "/") { if p, exists := ctx.Alias[path]; exists { path = p } } // 2. fail if file not found if _, err := os.Stat(path); os.IsNotExist(err) { return nil, fmt.Errorf("cannot find file '%s'", path) } // 3. try to load format format, err := cnf.Load(path) if err != nil { return nil, err } // 4. try to update value if !format.Set(d.Path, d.Value) { return nil, ErrCannotSet } // 5. Update file file, err := os.OpenFile(path, os.O_WRONLY, 0) if err != nil { return nil, err } defer file.Close() if _, err = format.WriteTo(file); err != nil { return nil, err } return nil, nil } func (d set) DryRun(ctx ExecutionContext) ([]byte, error) { // 1. get file / alias path := d.File if !strings.Contains(path, "/") { if p, exists := ctx.Alias[path]; exists { path = p } } // 2. fail if file not found if _, err := os.Stat(path); os.IsNotExist(err) { return nil, fmt.Errorf("cannot find file '%s'", path) } // 3. try to load format format, err := cnf.Load(path) if err != nil { return nil, err } // 4. try to update value if !format.Set(d.Path, d.Value) { return nil, ErrCannotSet } // 5. fail on missing write permission file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, os.FileMode(0775)) if err != nil { return nil, fmt.Errorf("cannot update '%s' | %s", path, err) } file.Close() // 6. create non-destructive dry-run folder dryRunFolder := "/tmp/dry-run" if err := os.MkdirAll(dryRunFolder, os.FileMode(0777)); err != nil { return nil, fmt.Errorf("cannot create dry-run folder | %s", err) } // 7. create updated file inside .dry-run tmpout := filepath.Join(dryRunFolder, strings.Replace(path, "/", "-", -1)) file, err = os.Create(tmpout) if err != nil { return nil, fmt.Errorf("cannot create dry-run file '%s' | %s", tmpout, err) } defer file.Close() if _, err = format.WriteTo(file); err != nil { return nil, fmt.Errorf("cannot write dry-run file '%s' | %s", tmpout, err) } return nil, nil }