Home

nixfig @d3931c4481751ece09bb0f7ca5fbab300a885740 - refs - log -
-
https://git.jolheiser.com/nixfig.git
Nix as a Config
nixfig / nixfig.go
- raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
package nixfig

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

var (
	Nix            string   // Nix is the command to call for nix
	Fmt            []string // Fmt is the command (and args) to call to format the nix output
	ErrNixNotFound = errors.New("nix was not found or set. You can set it either with `nixfig.Nix` or the `NIXFIG_NIX` environment variable")
	ErrFmtNotFound = errors.New("nix formatter was not found or set. You can set it either with `nixfig.Fmt` or the `NIXFIG_FMT` environment variable")
)

func init() {
	nixPath, _ := exec.LookPath("nix")
	Nix = nixPath
	if envPath, ok := os.LookupEnv("NIXFIG_NIX"); ok {
		Nix = envPath
	}

	for _, formatter := range []string{"alejandra", "nixfmt-rfc-style", "nixfmt"} {
		fmtPath, _ := exec.LookPath(formatter)
		if fmtPath != "" {
			Fmt = []string{fmtPath, "--quiet"}
			break
		}
	}
	if envPath, ok := os.LookupEnv("NIXFIG_FMT"); ok {
		Fmt = strings.Split(envPath, " ")
	}
}

// Unmarshal unmarshals a nix expression as JSON into a struct
func Unmarshal(data []byte, v any) error {
	if Nix == "" {
		return ErrNixNotFound
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command(Nix, "eval", "--json", "--expr", string(data))
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("could not run %q: %w", Nix, err)
	}
	if stderr.Len() > 0 {
		return errors.New(stderr.String())
	}

	if err := json.Unmarshal(stdout.Bytes(), v); err != nil {
		return fmt.Errorf("could not unmarshal nix config as JSON: %w", err)
	}
	return nil
}

// Marshal marshals a struct into a nix expression
func Marshal(v any) ([]byte, error) {
	if Nix == "" {
		return nil, ErrNixNotFound
	}

	data, err := json.Marshal(v)
	if err != nil {
		return nil, fmt.Errorf("could not marshal JSON: %w", err)
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command(Nix, "eval", "--expr", fmt.Sprintf(`(builtins.fromJSON %q)`, string(data)))
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("could not run %q: %w", Nix, err)
	}
	if stderr.Len() > 0 {
		return nil, errors.New(stderr.String())
	}

	return stdout.Bytes(), nil
}

// MarshalFormat marshals a struct into a nix expression and formats it with Fmt
func MarshalFormat(v any) ([]byte, error) {
	if Fmt == nil {
		return nil, ErrFmtNotFound
	}

	data, err := Marshal(v)
	if err != nil {
		return nil, err
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command(Fmt[0], Fmt[1:]...)
	cmd.Stdin = bytes.NewBuffer(data)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("could not run %v: %w", Fmt, err)
	}
	if stderr.Len() > 0 {
		return nil, errors.New(stderr.String())
	}

	return stdout.Bytes(), nil
}