package nixfig import ( "bytes" "encoding/json" "errors" "fmt" "os" "os/exec" ) var ( // Nix is the command to call for nix Nix string ErrNixNotFound = errors.New("nix was not found or set. You can set it either with `nixfig.Nix` or the `NIXFIG_NIX` environment variable") ) func init() { nixPath, _ := exec.LookPath("nix") Nix = nixPath if envPath, ok := os.LookupEnv("NIXFIG_NIX"); ok { Nix = 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 }