Skip to content

Commit

Permalink
Merge pull request #46 from rsteube/private-macro
Browse files Browse the repository at this point in the history
macro: private members
  • Loading branch information
rsteube authored May 9, 2022
2 parents 5cefad8 + a5d8260 commit 8428009
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 128 deletions.
148 changes: 148 additions & 0 deletions action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package spec

import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace/pkg/style"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v3"
)

func ActionSpec(path string) {
carapace.ActionCallback(func(c carapace.Context) carapace.Action {
abs, err := filepath.Abs(path)
if err != nil {
return carapace.ActionMessage(err.Error())
}

content, err := os.ReadFile(abs)
if err != nil {
return carapace.ActionMessage(err.Error())
}

var cmd Command
if err := yaml.Unmarshal(content, &cmd); err != nil {
return carapace.ActionMessage(err.Error())
}
return carapace.ActionExecute(cmd.ToCobra())
})
}

func parseAction(cmd *cobra.Command, arr []string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
// TODO yuck - where to set thes best?
for index, arg := range c.Args {
c.Setenv(fmt.Sprintf("C_ARG%v", index), arg)
}
c.Setenv("C_CALLBACK", c.CallbackValue)

cmd.Flags().Visit(func(f *pflag.Flag) {
c.Setenv(fmt.Sprintf("C_FLAG_%v", strings.ToUpper(f.Name)), f.Value.String())
})

listDelimiter := ""
nospace := false
chdir := ""
multiparts := ""

// TODO don't alter the map each time, solve this differently
addCoreMacro("list", MacroI(func(s string) carapace.Action {
listDelimiter = s
return carapace.ActionValues()
}))
addCoreMacro("nospace", MacroI(func(s string) carapace.Action {
nospace = true
return carapace.ActionValues()
}))
addCoreMacro("chdir", MacroI(func(s string) carapace.Action {
chdir = s
return carapace.ActionValues()
}))
addCoreMacro("multiparts", MacroI(func(s string) carapace.Action {
multiparts = s
return carapace.ActionValues()
}))

addCoreMacro("files", MacroV(carapace.ActionFiles))
addCoreMacro("directories", MacroN(carapace.ActionDirectories))
addCoreMacro("message", MacroI(carapace.ActionMessage))
addCoreMacro("", MacroI(func(s string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
return carapace.ActionExecCommand("sh", "-c", s)(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")
vals := make([]string, 0)
for _, line := range lines {
if line != "" {
vals = append(vals, parseValue(line)...)
}
}
return carapace.ActionStyledValuesDescribed(vals...)
}).Invoke(c).ToA()
})
}))

batch := carapace.Batch()
vals := make([]string, 0)
for _, elem := range arr {
if elemSubst, err := c.Envsubst(elem); err != nil {
batch = append(batch, carapace.ActionMessage(fmt.Sprintf("%v: %v", err.Error(), elem)))
} else if strings.HasPrefix(elemSubst, "$") { // macro
batch = append(batch, ActionMacro(elemSubst))
} else {
vals = append(vals, parseValue(elemSubst)...)
}
}
batch = append(batch, carapace.ActionStyledValuesDescribed(vals...))

action := batch.ToA()
if chdir != "" {
action = action.Chdir(chdir)
}
if multiparts != "" {
actionCopy := action
action = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
return actionCopy.Invoke(c).ToMultiPartsA(multiparts)
})
}

if listDelimiter != "" {
return carapace.ActionMultiParts(listDelimiter, func(c carapace.Context) carapace.Action {
for index, arg := range c.Parts {
c.Setenv(fmt.Sprintf("C_PART%v", index), arg)
}
c.Setenv("C_CALLBACK", c.CallbackValue)

return action.Invoke(c).Filter(c.Parts).ToA()
})
} else if nospace {
return action.NoSpace()
}
return action.Invoke(c).ToA()
})
}

func parseValue(s string) []string {
if splitted := strings.SplitN(s, "\t", 3); len(splitted) > 2 {
return splitted
} else if len(splitted) > 1 {
return []string{splitted[0], splitted[1], style.Default}
} else {
return []string{splitted[0], "", style.Default}
}
}

func findNamedMatches(regex *regexp.Regexp, str string) map[string]string {
match := regex.FindStringSubmatch(str)

results := map[string]string{}
for i, name := range match {
results[regex.SubexpNames()[i]] = name
}
return results
}
20 changes: 11 additions & 9 deletions macro.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
)

type Macro struct {
Func func(string) carapace.Action
Signature func() string
f func(string) carapace.Action
s func() string
}

func (m Macro) Signature() string { return m.s() }

var macros = make(map[string]Macro)

func addCoreMacro(s string, m Macro) {
Expand All @@ -36,22 +38,22 @@ func ActionMacro(s string) carapace.Action {
if m, ok := macros[matches["macro"]]; !ok {
return carapace.ActionMessage(fmt.Sprintf("unknown macro: '%v'", s))
} else {
return m.Func(matches["arg"])
return m.f(matches["arg"])
}
}

func MacroN(f func() carapace.Action) Macro {
return Macro{
Func: func(s string) carapace.Action {
f: func(s string) carapace.Action {
return f()
},
Signature: func() string { return "" },
s: func() string { return "" },
}
}

func MacroI[T any](f func(t T) carapace.Action) Macro {
return Macro{
Func: func(s string) carapace.Action {
f: func(s string) carapace.Action {
var t T
if reflect.TypeOf(t).Kind() == reflect.String {
reflect.ValueOf(&t).Elem().SetString(s)
Expand All @@ -62,13 +64,13 @@ func MacroI[T any](f func(t T) carapace.Action) Macro {
}
return f(t)
},
Signature: func() string { return signature(new(T)) },
s: func() string { return signature(new(T)) },
}
}

func MacroV[T any](f func(s ...T) carapace.Action) Macro {
return Macro{
Func: func(s string) carapace.Action {
f: func(s string) carapace.Action {
if s == "" {
return f()
}
Expand All @@ -79,7 +81,7 @@ func MacroV[T any](f func(s ...T) carapace.Action) Macro {
}
return f(t...)
},
Signature: func() string { return fmt.Sprintf("[%v]", signature(new(T))) },
s: func() string { return fmt.Sprintf("[%v]", signature(new(T))) },
}
}

Expand Down
119 changes: 0 additions & 119 deletions spec.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package spec

import (
"fmt"
"regexp"
"strings"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace/pkg/style"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

type Command struct {
Expand Down Expand Up @@ -70,116 +64,3 @@ func (c *Command) ToCobra() *cobra.Command {

return cmd
}

func parseAction(cmd *cobra.Command, arr []string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
// TODO yuck - where to set thes best?
for index, arg := range c.Args {
c.Setenv(fmt.Sprintf("C_ARG%v", index), arg)
}
c.Setenv("C_CALLBACK", c.CallbackValue)

cmd.Flags().Visit(func(f *pflag.Flag) {
c.Setenv(fmt.Sprintf("C_FLAG_%v", strings.ToUpper(f.Name)), f.Value.String())
})

listDelimiter := ""
nospace := false
chdir := ""
multiparts := ""

// TODO don't alter the map each time, solve this differently
addCoreMacro("list", MacroI(func(s string) carapace.Action {
listDelimiter = s
return carapace.ActionValues()
}))
addCoreMacro("nospace", MacroI(func(s string) carapace.Action {
nospace = true
return carapace.ActionValues()
}))
addCoreMacro("chdir", MacroI(func(s string) carapace.Action {
chdir = s
return carapace.ActionValues()
}))
addCoreMacro("multiparts", MacroI(func(s string) carapace.Action {
multiparts = s
return carapace.ActionValues()
}))

addCoreMacro("files", MacroV(carapace.ActionFiles))
addCoreMacro("directories", MacroN(carapace.ActionDirectories))
addCoreMacro("message", MacroI(carapace.ActionMessage))
addCoreMacro("", MacroI(func(s string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
return carapace.ActionExecCommand("sh", "-c", s)(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")
vals := make([]string, 0)
for _, line := range lines {
if line != "" {
vals = append(vals, parseValue(line)...)
}
}
return carapace.ActionStyledValuesDescribed(vals...)
}).Invoke(c).ToA()
})
}))

batch := carapace.Batch()
vals := make([]string, 0)
for _, elem := range arr {
if elemSubst, err := c.Envsubst(elem); err != nil {
batch = append(batch, carapace.ActionMessage(fmt.Sprintf("%v: %v", err.Error(), elem)))
} else if strings.HasPrefix(elemSubst, "$") { // macro
batch = append(batch, ActionMacro(elemSubst))
} else {
vals = append(vals, parseValue(elemSubst)...)
}
}
batch = append(batch, carapace.ActionStyledValuesDescribed(vals...))

action := batch.ToA()
if chdir != "" {
action = action.Chdir(chdir)
}
if multiparts != "" {
actionCopy := action
action = carapace.ActionCallback(func(c carapace.Context) carapace.Action {
return actionCopy.Invoke(c).ToMultiPartsA(multiparts)
})
}

if listDelimiter != "" {
return carapace.ActionMultiParts(listDelimiter, func(c carapace.Context) carapace.Action {
for index, arg := range c.Parts {
c.Setenv(fmt.Sprintf("C_PART%v", index), arg)
}
c.Setenv("C_CALLBACK", c.CallbackValue)

return action.Invoke(c).Filter(c.Parts).ToA()
})
} else if nospace {
return action.NoSpace()
}
return action.Invoke(c).ToA()
})
}

func parseValue(s string) []string {
if splitted := strings.SplitN(s, "\t", 3); len(splitted) > 2 {
return splitted
} else if len(splitted) > 1 {
return []string{splitted[0], splitted[1], style.Default}
} else {
return []string{splitted[0], "", style.Default}
}
}

func findNamedMatches(regex *regexp.Regexp, str string) map[string]string {
match := regex.FindStringSubmatch(str)

results := map[string]string{}
for i, name := range match {
results[regex.SubexpNames()[i]] = name
}
return results
}

0 comments on commit 8428009

Please sign in to comment.