nix-amer/internal/instruction/set.go

152 lines
3.2 KiB
Go

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
}