Skip to content

Commit

Permalink
nix-build: add initial support
Browse files Browse the repository at this point in the history
  • Loading branch information
thatsmydoing committed Jan 15, 2023
1 parent 617b13c commit 49ac640
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
43 changes: 43 additions & 0 deletions completers/nix-build_completer/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cmd

import (
"github.com/rsteube/carapace"
"github.com/rsteube/carapace-bin/pkg/actions/tools/nix"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "nix-build",
Short: "build a Nix expression",
Long: "https://nixos.org/manual/nix/stable/command-ref/nix-build.html",
Run: func(cmd *cobra.Command, args []string) {},
}

func Execute() error {
return rootCmd.Execute()
}
func init() {
carapace.Gen(rootCmd).Standalone()

rootCmd.Flags().StringP("attr", "A", "", "Attribute to build")
rootCmd.Flags().Bool("dry-run", false, "Show what store paths would be built or downloaded")
rootCmd.Flags().StringP("include", "I", "", "Include paths")
rootCmd.Flags().Bool("no-out-link", false, "Do not create a symlink to the output path")
rootCmd.Flags().StringP("out-link", "o", "", "Change the name of the symlink to the output path")
// TODO support --arg and --argstr

carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{
"attr": carapace.ActionCallback(func(c carapace.Context) carapace.Action {
opts := nix.AttributeOpts{Source: "default.nix", Include: rootCmd.Flag("include").Value.String()}
if len(c.Args) > 0 {
opts.Source = c.Args[0]
}
return nix.ActionAttributes(opts)
}),
})

carapace.Gen(rootCmd).PositionalAnyCompletion(
nix.ActionPaths(),
)

}
7 changes: 7 additions & 0 deletions completers/nix-build_completer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "github.com/rsteube/carapace-bin/completers/nix-build_completer/cmd"

func main() {
cmd.Execute()
}
89 changes: 89 additions & 0 deletions pkg/actions/tools/nix/attribute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package nix

import (
_ "embed"
"encoding/json"
"fmt"
"path"
"strings"

"github.com/rsteube/carapace"
)

//go:embed attributeComplete.nix
var attributeCompleteScript string

type AttributeOpts struct {
Source string
Include string
}

// ActionAttributes completes attributes
//
// firefox
// git
func ActionAttributes(opts AttributeOpts) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if opts.Source == "" {
opts.Source = "default.nix"
}

attrPath := c.CallbackValue

if strings.HasPrefix(opts.Source, "<") {
// expression is a local channel, use as-is
} else if strings.HasPrefix(opts.Source, "http:") || strings.HasPrefix(opts.Source, "https:") {
// expression is a url, wrap in fetchTarball
opts.Source = fmt.Sprintf("(fetchTarball %s)", opts.Source)
} else if strings.HasPrefix(opts.Source, "channel:") {
// expression is a channel alias, convert to url
channelName := opts.Source[8:]
opts.Source = fmt.Sprintf("(fetchTarball https://nixos.org/channels/%s/nixexprs.tar.xz)", channelName)
} else {
// expression is a local file
filePath := path.Clean(opts.Source)
if path.Base(filePath) == filePath {
// paths must have at least one `/`
filePath = fmt.Sprintf("./%s", filePath)
}
opts.Source = filePath
}
expression := fmt.Sprintf("import %s", opts.Source)

// nix itself handles quotes weirdly so we just avoid them altogether
if strings.Contains(attrPath, "\"") {
return carapace.ActionMessage("attrPath may not contain double-quotes")
}

// strip right-most attr
lastDotIndex := strings.LastIndex(attrPath, ".")
if lastDotIndex >= 0 {
attrPath = attrPath[0:lastDotIndex]
} else {
attrPath = ""
}

args := []string{
"--eval", "--json",
"--expr", attributeCompleteScript,
"--arg", "__carapaceInput__", expression,
"--argstr", "__carapaceAttrPath__", attrPath,
}
if opts.Include != "" {
args = append(args, "-I", opts.Include)
}
// TODO handle passing through --arg and --argstr from original command

return carapace.ActionExecCommand("nix-instantiate", args...)(func(output []byte) carapace.Action {
var data []string
json.Unmarshal(output, &data)

prefix := ""
if attrPath != "" {
prefix = fmt.Sprintf("%s.", attrPath)
}

return carapace.ActionValues(data...).Prefix(prefix).NoSpace()
})
})
}
25 changes: 25 additions & 0 deletions pkg/actions/tools/nix/attributeComplete.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
all@{ __carapaceInput__, __carapaceAttrPath__, ... }:
let
inputArgs = builtins.removeAttrs all ["__carapaceInput__" "__carapaceAttrPath__"];

input =
if builtins.isFunction __carapaceInput__ then
__carapaceInput__ inputArgs
else
__carapaceInput__;

autocall = fnOrAttrset:
if builtins.isFunction fnOrAttrset then
fnOrAttrset {}
else
fnOrAttrset;

paths = builtins.filter (path: (builtins.isString path) && path != "") (builtins.split "\\." __carapaceAttrPath__);

reducer = attrset: name: autocall (builtins.getAttr name attrset);
result = builtins.foldl' reducer input paths;
in
if builtins.isAttrs result then
builtins.attrNames result
else
[]
31 changes: 31 additions & 0 deletions pkg/actions/tools/nix/path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nix

import (
"strings"

"github.com/rsteube/carapace"
)

// ActionPaths completes paths
//
// A path can be one of:
// - a local file path (default.nix)
// - an http/https URL (https://releases.nixos.org/../nixexprs.tar.xz
// - a channel: specifier (channel:nixos-22.05)
// - a local channel (<nixpkgs>)
func ActionPaths() carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
batch := carapace.Batch(
ActionLocalChannels().Prefix("<").Suffix(">"),
carapace.ActionFiles(".nix"),
)

if !strings.HasPrefix(c.CallbackValue, "channel:") {
batch = append(batch, carapace.ActionValues("channel:").NoSpace())
} else {
batch = append(batch, ActionRemoteChannels().Prefix("channel:"))
}

return batch.ToA()
})
}

0 comments on commit 49ac640

Please sign in to comment.