Skip to content

Commit

Permalink
dagger: function completion
Browse files Browse the repository at this point in the history
  • Loading branch information
rsteube committed Feb 29, 2024
1 parent 6556e80 commit 6a65f69
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 13 deletions.
1 change: 1 addition & 0 deletions completers/dagger_completer/cmd/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var callCmd = &cobra.Command{

func init() {
carapace.Gen(callCmd).Standalone()
callCmd.Flags().SetInterspersed(false)

callCmd.PersistentFlags().Bool("focus", false, "Only show output for focused commands")
callCmd.PersistentFlags().Bool("json", false, "Present result as JSON")
Expand Down
1 change: 1 addition & 0 deletions completers/dagger_completer/cmd/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func init() {
carapace.Gen(functionsCmd).Standalone()

functionsCmd.PersistentFlags().Bool("focus", false, "Only show output for focused commands")
functionsCmd.PersistentFlags().Bool("json", false, "Output as json")
functionsCmd.PersistentFlags().StringP("mod", "m", "", "Path to dagger.json config file for the module or a directory containing that file. Either local path (e.g. \"/path/to/some/dir\") or a github repo (e.g. \"github.com/dagger/dagger/path/to/some/subdir\")")
rootCmd.AddCommand(functionsCmd)

Expand Down
6 changes: 6 additions & 0 deletions completers/dagger_completer/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"github.com/rsteube/carapace"
"github.com/rsteube/carapace-bridge/pkg/actions/bridge"
"github.com/spf13/cobra"
)

Expand All @@ -15,8 +16,13 @@ var runCmd = &cobra.Command{

func init() {
carapace.Gen(runCmd).Standalone()
runCmd.Flags().SetInterspersed(false)

runCmd.Flags().String("cleanup-timeout", "", "max duration to wait between SIGTERM and SIGKILL on interrupt")
runCmd.Flags().Bool("focus", false, "Only show output for focused commands.")
rootCmd.AddCommand(runCmd)

carapace.Gen(runCmd).PositionalAnyCompletion(
bridge.ActionCarapaceBin(),
)
}
131 changes: 118 additions & 13 deletions pkg/actions/tools/dagger/function.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,132 @@
package dagger

import (
"regexp"
"strings"
"encoding/json"
"os"
"time"
"unicode"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace/pkg/cache"
"github.com/rsteube/carapace/pkg/cache/key"
"github.com/spf13/cobra"
)

// ActionFunctions completes functions
type function struct {
Name string `json:"Name"`
Description string `json:"Description"`
Args []struct {
Name string `json:"Name"`
Description string `json:"Description"`
TypeDef struct {
Kind string `json:"Kind"`
Optional bool `json:"Optional"`
AsObject *struct {
Name string `json:"Name"`
Functions any `json:"Functions"`
Fields any `json:"Fields"`
Constructor any `json:"Constructor"`
SourceModuleName string `json:"SourceModuleName"`
} `json:"AsObject"`
AsInterface any `json:"AsInterface"`
AsInput any `json:"AsInput"`
AsList any `json:"AsList"`
} `json:"TypeDef"`
DefaultValue string `json:"DefaultValue"`
} `json:"Args"`
}

type daggerFile struct {
Name string
Sdk string
Source string
}

// ActionFunctions completes functions and their arguments
//
// container-echo (dagger call container-echo --string-arg yo stdout)
// grep-dir (dagger call grep-dir --directory-arg . --pattern GrepDir)
// --directory-arg
func ActionFunctions() carapace.Action {
return carapace.ActionExecCommand("dagger", "functions", "--silent")(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")
r := regexp.MustCompile(`^(?P<name>[^ ]+) +example usage: "(?P<example>.*)"$`)

vals := make([]string, 0)
for _, line := range lines[1:] {
if matches := r.FindStringSubmatch(line); matches != nil {
vals = append(vals, matches[1], matches[2])
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
content, err := os.ReadFile("dagger.json")
if err != nil {
return carapace.ActionMessage(err.Error())
}

var d daggerFile
if err := json.Unmarshal(content, &d); err != nil {
return carapace.ActionMessage(err.Error())
}

output, err := cache.Cache(24*time.Hour, key.FolderStats(d.Source))(func() ([]byte, error) {
return c.Command("dagger", "functions", "--json").Output()
})
if err != nil {
return carapace.ActionMessage(err.Error())
}

var functions []function
if err := json.Unmarshal(output, &functions); err != nil {
return carapace.ActionMessage(err.Error())
}

cmd := &cobra.Command{}
carapace.Gen(cmd).Standalone()
for _, f := range functions {
f.Name = toKebab(f.Name)
subCmd := &cobra.Command{
Use: f.Name,
Short: f.Description,
Run: func(cmd *cobra.Command, args []string) {},
}
carapace.Gen(subCmd).Standalone()

for _, arg := range f.Args {
arg.Name = toKebab(arg.Name)

// TODO transform camelcase to kebab
switch arg.TypeDef.Kind { // TODO more types
case "STRING_KIND", "OBJECT_KIND":
subCmd.Flags().String(arg.Name, arg.DefaultValue, arg.Description)
default:
return carapace.ActionMessage("unknown kind %s", arg.TypeDef.Kind)
}

if arg.TypeDef.Optional {
subCmd.Flag(arg.Name).NoOptDefVal = " "
}

localArg := arg
carapace.Gen(subCmd).FlagCompletion(carapace.ActionMap{
arg.Name: carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if localArg.TypeDef.AsObject == nil {
return carapace.ActionValues()
}

switch localArg.TypeDef.AsObject.Name { // TODO more
case "Directory":
return carapace.ActionDirectories()
default:
return carapace.ActionValues()
}
}),
})
}

cmd.AddCommand(subCmd)
}
return carapace.ActionValuesDescribed(vals...)

return carapace.ActionExecute(cmd)
})
}

func toKebab(s string) string {
runes := make([]rune, 0)
for _, r := range []rune(s) {
if unicode.IsUpper(r) {
runes = append(runes, '-')
}
runes = append(runes, unicode.ToLower(r))
}
return string(runes)
}

0 comments on commit 6a65f69

Please sign in to comment.