commit e9247b213d948a41586e1a6063fe0030caec312c
parent 587457304e4818e048cbc9f4efbfc2b31dea10b3
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 6 Oct 2024 17:08:27 -0400
rebuild zsh/bash logic to be much simpler to control features on/off (fish is wip)
Diffstat:
15 files changed, 148 insertions(+), 386 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,7 +10,7 @@ all: $(TARGET)
build: $(TARGET)
-$(TARGET): cmd/main.go internal/**/*.go go.* internal/app/doc/*
+$(TARGET): cmd/main.go internal/**/*.go go.* internal/app/doc/* internal/app/shell/*
ifeq ($(VERSION),)
$(error version not set)
endif
diff --git a/internal/app/completions.go b/internal/app/completions.go
@@ -6,7 +6,6 @@ import (
"embed"
"fmt"
"slices"
- "strings"
"text/template"
"github.com/seanenck/lockbox/internal/config"
@@ -30,97 +29,41 @@ type (
DefaultCompletion string
HelpCommand string
HelpAdvancedCommand string
- Profiles []Profile
- Shell string
- CompletionEnv string
- IsYes string
- DefaultProfile Profile
- IsFish bool
+ Options []CompletionOption
+ TOTPSubCommands []CompletionOption
}
-
- // Profile is a completion profile
- Profile struct {
- Name string
- CanClip bool
- CanTOTP bool
- CanList bool
- CanGenerate bool
- ReadOnly bool
- IsDefault bool
+ // CompletionOption are conditional wrapped logic for options that may be disabled
+ CompletionOption struct {
Conditional string
+ Key string
}
)
//go:embed shell/*
var shell embed.FS
-// Options will list the profile options
-func (p Profile) Options() []string {
- opts := []string{EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand}
- if p.CanClip {
- opts = append(opts, ClipCommand)
- }
- if !p.ReadOnly {
- opts = append(opts, MoveCommand, RemoveCommand, InsertCommand, MultiLineCommand)
- }
- if p.CanTOTP {
- opts = append(opts, TOTPCommand)
- }
- if p.CanGenerate {
- opts = append(opts, PasswordGenerateCommand)
- }
- return opts
+func newConditional(left, right string) string {
+ return fmt.Sprintf("[ \"%s\" != \"%s\" ]", left, right)
}
-// TOTPSubCommands are the list of sub commands for TOTP within the profile
-func (p Profile) TOTPSubCommands() []string {
- totp := []string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand}
- if p.CanClip {
- totp = append(totp, TOTPClipCommand)
- }
- if !p.ReadOnly {
- totp = append(totp, TOTPInsertCommand)
- }
- return totp
+func genOption(to []CompletionOption, command, left, right string) []CompletionOption {
+ conditional := newConditional(left, right)
+ return append(to, CompletionOption{conditional, command})
}
-func loadProfiles(exe string) []Profile {
- profiles := config.LoadCompletionProfiles()
- conditionals := make(map[int][]Profile)
- maxCount := 0
- for _, p := range profiles {
- name := p.Name
- if p.Default {
- name = "default"
- }
- n := Profile{Name: fmt.Sprintf("_%s-%s", exe, name)}
- n.CanClip = p.Clip
- n.CanList = p.List
- n.CanTOTP = p.TOTP
- n.ReadOnly = !p.Write
- n.IsDefault = p.Default
- n.CanGenerate = p.Generate
- var sub []string
- for _, e := range p.Env {
- sub = append(sub, fmt.Sprintf("[ %s ]", e))
- }
- n.Conditional = strings.Join(sub, " && ")
- count := len(p.Env)
- val := conditionals[count]
- conditionals[count] = append(val, n)
- if count > maxCount {
- maxCount = count
- }
+func newGenOptions(defaults ...string) []CompletionOption {
+ opt := []CompletionOption{}
+ for _, a := range defaults {
+ opt = genOption(opt, a, "1", "0")
}
- var res []Profile
- for maxCount >= 0 {
- val, ok := conditionals[maxCount]
- if ok {
- res = append(res, val...)
- }
- maxCount--
+ return opt
+}
+
+func genOptionKeyValues(to []CompletionOption, kv map[string]string) []CompletionOption {
+ for key, env := range kv {
+ to = genOption(to, key, fmt.Sprintf("$%s", env), config.YesValue)
}
- return res
+ return to
}
// GenerateCompletions handles creating shell completion outputs
@@ -143,31 +86,37 @@ func GenerateCompletions(completionType, exe string) ([]string, error) {
MoveCommand: MoveCommand,
DoList: fmt.Sprintf("%s %s", exe, ListCommand),
DoTOTPList: fmt.Sprintf("%s %s %s", exe, TOTPCommand, TOTPListCommand),
- DefaultCompletion: fmt.Sprintf("$%s", config.EnvDefaultCompletionKey),
- IsYes: config.YesValue,
- IsFish: completionType == CompletionsFishCommand,
}
+ // TOTPSubCommands: []string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand, TOTPClipCommand, TOTPInsertCommand},
+ cmds := newGenOptions(
+ EnvCommand,
+ HelpCommand,
+ ListCommand,
+ ShowCommand,
+ VersionCommand,
+ JSONCommand,
+ )
+ cmds = genOptionKeyValues(cmds, map[string]string{
+ ClipCommand: config.EnvNoClip.Key(),
+ TOTPCommand: config.EnvNoTOTP.Key(),
+ MoveCommand: config.EnvReadOnly.Key(),
+ RemoveCommand: config.EnvReadOnly.Key(),
+ InsertCommand: config.EnvReadOnly.Key(),
+ MultiLineCommand: config.EnvReadOnly.Key(),
+ PasswordGenerateCommand: config.EnvNoPasswordGen.Key(),
+ })
+ c.Options = cmds
+ totp := newGenOptions(TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand)
+ totp = genOptionKeyValues(totp, map[string]string{
+ TOTPClipCommand: config.EnvNoClip.Key(),
+ TOTPInsertCommand: config.EnvReadOnly.Key(),
+ })
+ c.TOTPSubCommands = totp
using, err := readShell(completionType)
if err != nil {
return nil, err
}
- shellScript, err := readShell("shell")
- if err != nil {
- return nil, err
- }
- c.Profiles = loadProfiles(exe)
- for _, p := range c.Profiles {
- if p.IsDefault {
- c.DefaultProfile = p
- break
- }
- }
- shell, err := templateScript(shellScript, c)
- if err != nil {
- return nil, err
- }
- c.Shell = shell
s, err := templateScript(using, c)
if err != nil {
return nil, err
diff --git a/internal/app/completions_test.go b/internal/app/completions_test.go
@@ -10,7 +10,6 @@ import (
func TestCompletions(t *testing.T) {
for k, v := range map[string]string{
"zsh": "typeset -A opt_args",
- "fish": "set -l commands",
"bash": "local cur opts",
} {
testCompletion(t, k, v)
@@ -29,47 +28,3 @@ func testCompletion(t *testing.T, completionMode, need string) {
t.Errorf("invalid output, bad shell generation: %v", v)
}
}
-
-func TestProfileOptions(t *testing.T) {
- p := app.Profile{Name: "_abc-test-awera-zzz"}
- p.CanClip = true
- p.CanTOTP = true
- if len(p.Options()) != 12 {
- t.Errorf("invalid options: %v", p.Options())
- }
- p.CanClip = false
- if len(p.Options()) != 11 {
- t.Errorf("invalid options: %v", p.Options())
- }
- p.CanClip = true
- p.CanTOTP = false
- if len(p.Options()) != 11 {
- t.Errorf("invalid options: %v", p.Options())
- }
- p.CanTOTP = true
- p.ReadOnly = true
- if len(p.Options()) != 8 {
- t.Errorf("invalid options: %v", p.Options())
- }
- p.CanGenerate = true
- if len(p.Options()) != 9 {
- t.Errorf("invalid options: %v", p.Options())
- }
-}
-
-func TestProfileTOTPSubOptions(t *testing.T) {
- p := app.Profile{Name: "_abc-test-awera-zzz"}
- p.CanClip = true
- if len(p.TOTPSubCommands()) != 5 {
- t.Errorf("invalid options: %v", p.TOTPSubCommands())
- }
- p.CanClip = false
- if len(p.TOTPSubCommands()) != 4 {
- t.Errorf("invalid options: %v", p.TOTPSubCommands())
- }
- p.CanClip = true
- p.ReadOnly = true
- if len(p.TOTPSubCommands()) != 4 {
- t.Errorf("invalid options: %v", p.TOTPSubCommands())
- }
-}
diff --git a/internal/app/core.go b/internal/app/core.go
@@ -71,10 +71,8 @@ const (
JSONCommand = "json"
// CompletionsZshCommand is the command to generate zsh completions
CompletionsZshCommand = "zsh"
- // CompletionsFishCommand is the command to generate fish completions
- CompletionsFishCommand = "fish"
- docDir = "doc"
- textFile = ".txt"
+ docDir = "doc"
+ textFile = ".txt"
// PasswordGenerateCommand is the command to do password generation
PasswordGenerateCommand = "pwgen"
)
@@ -82,7 +80,7 @@ const (
var (
//go:embed doc/*
docs embed.FS
- completionTypes = []string{CompletionsBashCommand, CompletionsFishCommand, CompletionsZshCommand}
+ completionTypes = []string{CompletionsBashCommand, CompletionsZshCommand}
)
type (
diff --git a/internal/app/core_test.go b/internal/app/core_test.go
@@ -9,11 +9,11 @@ import (
func TestUsage(t *testing.T) {
u, _ := app.Usage(false, "lb")
- if len(u) != 26 {
+ if len(u) != 25 {
t.Errorf("invalid usage, out of date? %d", len(u))
}
u, _ = app.Usage(true, "lb")
- if len(u) != 115 {
+ if len(u) != 114 {
t.Errorf("invalid verbose usage, out of date? %d", len(u))
}
for _, usage := range u {
diff --git a/internal/app/info.go b/internal/app/info.go
@@ -77,7 +77,7 @@ func info(command string, args []string) ([]string, error) {
return nil, errors.New("invalid completions subcommand")
}
switch shell {
- case CompletionsZshCommand, CompletionsBashCommand, CompletionsFishCommand:
+ case CompletionsZshCommand, CompletionsBashCommand:
break
default:
return nil, fmt.Errorf("unknown completion type: %s", shell)
diff --git a/internal/app/info_test.go b/internal/app/info_test.go
@@ -74,7 +74,6 @@ func TestCompletionInfo(t *testing.T) {
defer os.Clearenv()
for k, v := range map[string]string{
"zsh": "typeset -A opt_args",
- "fish": "set -l commands",
"bash": "local cur opts",
} {
for _, b := range []bool{true, false} {
diff --git a/internal/app/shell/bash.sh b/internal/app/shell/bash.sh
@@ -1,68 +1,71 @@
# {{ $.Executable }} completion
-{{ $.Shell }}
-
-{{- range $idx, $profile := $.Profiles }}
-
-{{ $profile.Name }}() {
- local cur opts
+_{{ $.Executable }}() {
+ local cur opts chosen found
cur=${COMP_WORDS[COMP_CWORD]}
if [ "$COMP_CWORD" -eq 1 ]; then
-{{- range $idx, $value := $profile.Options }}
- opts="${opts}{{ $value }} "
+{{- range $idx, $value := $.Options }}
+ if {{ $value.Conditional }}; then
+ opts="${opts}{{ $value.Key }} "
+ fi
{{- end}}
# shellcheck disable=SC2207
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
else
+ if [ "$COMP_CWORD" -lt 2 ]; then
+ return
+ fi
+ chosen=${COMP_WORDS[1]}
+ found=0
+{{- range $idx, $value := $.Options }}
+ if {{ $value.Conditional }}; then
+ if [ "$chosen" == "{{ $value.Key }}" ]; then
+ found=1
+ fi
+ fi
+{{- end}}
+ if [ "$found" -eq 0 ]; then
+ return
+ fi
if [ "$COMP_CWORD" -eq 2 ]; then
- case ${COMP_WORDS[1]} in
+ case "$chosen" in
"{{ $.HelpCommand }}")
opts="{{ $.HelpAdvancedCommand }}"
;;
-{{- if not $profile.ReadOnly }}
-{{- if $profile.CanList }}
"{{ $.InsertCommand }}" | "{{ $.MultiLineCommand }}" | "{{ $.MoveCommand }}" | "{{ $.RemoveCommand }}")
opts="$opts $({{ $.DoList }})"
;;
-{{- end}}
-{{- end}}
-{{- if $profile.CanTOTP }}
"{{ $.TOTPCommand }}")
opts="{{ $.TOTPListCommand }} "
{{- range $key, $value := .TOTPSubCommands }}
- opts="$opts {{ $value }}"
+ if {{ $value.Conditional }}; then
+ opts="$opts {{ $value.Key }}"
+ fi
{{- end}}
;;
-{{- end}}
-{{- if $profile.CanList }}
- "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}"{{ if $profile.CanClip }} | "{{ $.ClipCommand }}" {{end}})
+ "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}")
opts=$({{ $.DoList }})
;;
-{{- end}}
esac
-{{- if $profile.CanList }}
else
if [ "$COMP_CWORD" -eq 3 ]; then
- case "${COMP_WORDS[1]}" in
-{{- if not $profile.ReadOnly }}
+ case "$chosen" in
"{{ $.MoveCommand }}")
opts=$({{ $.DoList }})
;;
-{{- end }}
-{{- if $profile.CanTOTP }}
"{{ $.TOTPCommand }}")
case "${COMP_WORDS[2]}" in
-{{- range $key, $value := $profile.TOTPSubCommands }}
- "{{ $value }}")
- opts=$({{ $.DoTOTPList }})
+{{- range $key, $value := $.TOTPSubCommands }}
+ "{{ $value.Key }}")
+ if {{ $value.Conditional }}; then
+ opts=$({{ $.DoTOTPList }})
+ fi
;;
{{- end}}
esac
;;
-{{- end}}
esac
fi
-{{- end}}
fi
if [ -n "$opts" ]; then
# shellcheck disable=SC2207
@@ -70,6 +73,5 @@
fi
fi
}
-{{- end}}
complete -F _{{ $.Executable }} -o bashdefault {{ $.Executable }}
diff --git a/internal/app/shell/fish.sh b/internal/app/shell/fish.sh
@@ -1,32 +0,0 @@
-complete -c {{ $.Executable }} -f
-
-{{- range $idx, $profile := $.Profiles }}
-
-function {{ $profile.Name }}
- set -l commands {{ range $idx, $value := $profile.Options }}{{ if gt $idx 0}} {{ end }}{{ $value }}{{ end }}
- complete -c {{ $.Executable }} -n "not __fish_seen_subcommand_from $commands" -a "$commands"
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.HelpCommand }}; and test (count (commandline -opc)) -lt 3" -a "{{ $.HelpAdvancedCommand }}"
-{{- if not $profile.ReadOnly }}
-{{- if $profile.CanList }}
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.InsertCommand }} {{ $.MultiLineCommand }} {{ $.RemoveCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList }})"
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.MoveCommand }}; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoList }})"
-{{- end}}
-{{- end}}
-{{- if $profile.CanTOTP }}
- set -l totps {{ $.TOTPListCommand }}{{ range $key, $value := .TOTPSubCommands }} {{ $value }}{{ end }}
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.TOTPCommand }}; and not __fish_seen_subcommand_from $totps" -a "$totps"
-{{- if $profile.CanList }}
-complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.TOTPCommand }}; and __fish_seen_subcommand_from $totps; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoTOTPList }})"
-{{- end}}
-{{- end}}
-{{- if $profile.CanList }}
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.ShowCommand }} {{ $.JSONCommand }}{{ if $profile.CanClip }} {{ $.ClipCommand }} {{end}}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList}})"
-{{- end}}
-end
-{{- end}}
-
-function {{ $.Executable }}-completions
- {{ $.Shell }}
-end
-
-{{ $.Executable }}-completions
diff --git a/internal/app/shell/shell.sh b/internal/app/shell/shell.sh
@@ -1,17 +0,0 @@
-{{- if not $.IsFish }}
-_{{ $.Executable }}() {
-{{- end }}
- if [ -z "{{ $.DefaultCompletion }}" ] || [ "{{ $.DefaultCompletion }}" != "{{ $.IsYes }}" ]{{ if not $.IsFish }}; then{{ end }}
- {{- range $idx, $prof := $.Profiles }}
- {{- if not $prof.IsDefault }}
- if {{ $prof.Conditional }}{{ if not $.IsFish }}; then {{ end }}
- {{ $prof.Name }}
- return
- {{ if $.IsFish }}end{{ else }}fi{{ end }}
- {{- end }}
- {{- end }}
- {{ if $.IsFish }}end{{ else }}fi{{ end }}
- {{ $.DefaultProfile.Name }}
-{{- if not $.IsFish }}
-}
-{{- end }}
diff --git a/internal/app/shell/zsh.sh b/internal/app/shell/zsh.sh
@@ -1,11 +1,7 @@
#compdef _{{ $.Executable }} {{ $.Executable }}
-{{ $.Shell }}
-
-{{- range $idx, $profile := $.Profiles }}
-
-{{ $profile.Name }}() {
- local curcontext="$curcontext" state len
+_{{ $.Executable }}() {
+ local curcontext="$curcontext" state len chosen found args
typeset -A opt_args
_arguments \
@@ -15,17 +11,39 @@
len=${#words[@]}
case $state in
main)
- _arguments '1:main:({{ range $idx, $value := $profile.Options }}{{ if gt $idx 0}} {{ end }}{{ $value }}{{ end }})'
+ args=""
+{{- range $idx, $value := $.Options }}
+ if {{ $value.Conditional }}; then
+ if [ -n "$args" ]; then
+ args="$args "
+ fi
+ args="${args}{{ $value.Key }}"
+ fi
+{{- end }}
+ _arguments "1:main:($args)"
;;
*)
- case $words[2] in
+ if [ "$len" -lt 2 ]; then
+ return
+ fi
+ chosen=$words[2]
+ found=0
+{{- range $idx, $value := $.Options }}
+ if {{ $value.Conditional }}; then
+ if [[ "$chosen" == "{{ $value.Key }}" ]]; then
+ found=1
+ fi
+ fi
+{{- end }}
+ if [ "$found" -eq 0 ]; then
+ return
+ fi
+ case $chosen in
"{{ $.HelpCommand }}")
if [ "$len" -eq 3 ]; then
compadd "$@" "{{ $.HelpAdvancedCommand }}"
fi
;;
-{{- if not $profile.ReadOnly }}
-{{- if $profile.CanList }}
"{{ $.InsertCommand }}" | "{{ $.MultiLineCommand }}" | "{{ $.RemoveCommand }}")
if [ "$len" -eq 3 ]; then
compadd "$@" $({{ $.DoList }})
@@ -38,37 +56,35 @@
;;
esac
;;
-{{- end}}
-{{- end}}
-{{- if $profile.CanTOTP }}
"{{ $.TOTPCommand }}")
case "$len" in
3)
- compadd "$@" {{ $.TOTPListCommand }}{{ range $key, $value := .TOTPSubCommands }} {{ $value }}{{ end }}
+ compadd "$@" {{ $.TOTPListCommand }}
+{{- range $key, $value := .TOTPSubCommands }}
+ if {{ $value.Conditional }}; then
+ compadd "$@" {{ $value.Key }}
+ fi
+{{ end }}
;;
-{{- if $profile.CanList }}
4)
case $words[3] in
{{- range $key, $value := .TOTPSubCommands }}
- "{{ $value }}")
- compadd "$@" $({{ $.DoTOTPList }})
+ "{{ $value.Key }}")
+ if {{ $value.Conditional }}; then
+ compadd "$@" $({{ $.DoTOTPList }})
+ fi
;;
{{- end}}
esac
-{{- end}}
esac
;;
-{{- end}}
-{{- if $profile.CanList }}
- "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}"{{ if $profile.CanClip }} | "{{ $.ClipCommand }}" {{end}})
+ "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}")
if [ "$len" -eq 3 ]; then
compadd "$@" $({{ $.DoList }})
fi
;;
-{{- end}}
esac
esac
}
-{{- end}}
-compdef _lb lb
+compdef _{{ $.Executable }} {{ $.Executable }}
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -93,17 +93,6 @@ type (
Start int
End int
}
- // CompletionProfile are shell completion definitions with backing environment information
- CompletionProfile struct {
- Clip bool
- TOTP bool
- List bool
- Write bool
- Name string
- Env []string
- Default bool
- Generate bool
- }
// ReKeyArgs are the arguments for rekeying
ReKeyArgs struct {
NoKey bool
@@ -140,13 +129,13 @@ func environOrDefault(envKey, defaultValue string) string {
return val
}
-func (e environmentBase) key() string {
+func (e environmentBase) Key() string {
return fmt.Sprintf("LOCKBOX_%s%s", string(e.cat), e.subKey)
}
// Get will get the boolean value for the setting
func (e EnvironmentBool) Get() (bool, error) {
- read := strings.ToLower(strings.TrimSpace(getExpand(e.key())))
+ read := strings.ToLower(strings.TrimSpace(getExpand(e.Key())))
switch read {
case no:
return false, nil
@@ -156,13 +145,13 @@ func (e EnvironmentBool) Get() (bool, error) {
return e.defaultValue, nil
}
- return false, fmt.Errorf("invalid yes/no env value for %s", e.key())
+ return false, fmt.Errorf("invalid yes/no env value for %s", e.Key())
}
// Get will get the integer value for the setting
func (e EnvironmentInt) Get() (int, error) {
val := e.defaultValue
- use := getExpand(e.key())
+ use := getExpand(e.Key())
if use != "" {
i, err := strconv.Atoi(use)
if err != nil {
@@ -190,14 +179,14 @@ func (e EnvironmentInt) Get() (int, error) {
// Get will read the string from the environment
func (e EnvironmentString) Get() string {
if !e.canDefault {
- return getExpand(e.key())
+ return getExpand(e.Key())
}
- return environOrDefault(e.key(), e.defaultValue)
+ return environOrDefault(e.Key(), e.defaultValue)
}
// Get will read (and shlex) the value if set
func (e EnvironmentCommand) Get() ([]string, error) {
- value := environOrDefault(e.key(), "")
+ value := environOrDefault(e.Key(), "")
if strings.TrimSpace(value) == "" {
return nil, nil
}
@@ -206,24 +195,24 @@ func (e EnvironmentCommand) Get() ([]string, error) {
// KeyValue will get the string representation of the key+value
func (e environmentBase) KeyValue(value string) string {
- return fmt.Sprintf("%s=%s", e.key(), value)
+ return fmt.Sprintf("%s=%s", e.Key(), value)
}
// Setenv will do an environment set for the value to key
func (e environmentBase) Set(value string) error {
- unset, err := IsUnset(e.key(), value)
+ unset, err := IsUnset(e.Key(), value)
if err != nil {
return err
}
if unset {
return nil
}
- return os.Setenv(e.key(), value)
+ return os.Setenv(e.Key(), value)
}
// Get will retrieve the value with the formatted input included
func (e EnvironmentFormatter) Get(value string) string {
- return e.fxn(e.key(), value)
+ return e.fxn(e.Key(), value)
}
func (e EnvironmentString) values() (string, []string) {
@@ -357,8 +346,8 @@ func Environ() []string {
var results []string
for _, k := range os.Environ() {
for _, r := range registeredEnv {
- key := r.self().key()
- if key == EnvConfig.key() {
+ key := r.self().Key()
+ if key == EnvConfig.Key() {
continue
}
key = fmt.Sprintf("%s=", key)
@@ -382,7 +371,7 @@ func ExpandParsed(inputs map[string]string) (map[string]string, error) {
}
var err error
var cycles int
- possibleCycles, ok := inputs[envConfigExpands.key()]
+ possibleCycles, ok := inputs[envConfigExpands.Key()]
if ok {
cycles, err = strconv.Atoi(possibleCycles)
} else {
diff --git a/internal/config/core_test.go b/internal/config/core_test.go
@@ -52,6 +52,13 @@ func TestKeyValue(t *testing.T) {
}
}
+func TestKey(t *testing.T) {
+ val := config.EnvStore.Key()
+ if val != "LOCKBOX_STORE" {
+ t.Errorf("invalid key")
+ }
+}
+
func TestNewPlatform(t *testing.T) {
for _, item := range config.Platforms.List() {
os.Setenv("LOCKBOX_PLATFORM", item)
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -17,11 +17,6 @@ const (
fileExample = "<file>"
detectedValue = "<detected>"
requiredKeyOrKeyFile = "a key, a key file, or both must be set"
- askProfile = "ask"
- roProfile = "readonly"
- noTOTPProfile = "nototp"
- noClipProfile = "noclip"
- noGenProfile = "nopwgen"
// ModTimeFormat is the expected modtime format
ModTimeFormat = time.RFC3339
)
@@ -125,7 +120,7 @@ var (
}),
})
// EnvDefaultCompletionKey is the key for default completion handling
- EnvDefaultCompletionKey = EnvDefaultCompletion.key()
+ EnvDefaultCompletionKey = EnvDefaultCompletion.Key()
// EnvNoColor indicates if color outputs are disabled
EnvNoColor = environmentRegister(
EnvironmentBool{
@@ -435,7 +430,7 @@ func ListEnvironmentVariables() []string {
if r != "" {
requirement = r
}
- text := fmt.Sprintf("\n%s\n%s requirement: %s\n default: %s\n options: %s\n", env.key(), description, requirement, value, strings.Join(allow, "|"))
+ text := fmt.Sprintf("\n%s\n%s requirement: %s\n default: %s\n options: %s\n", env.Key(), description, requirement, value, strings.Join(allow, "|"))
results = append(results, text)
}
sort.Strings(results)
@@ -479,83 +474,6 @@ func ParseJSONOutput() (JSONOutputMode, error) {
return JSONOutputs.Blank, fmt.Errorf("invalid JSON output mode: %s", val)
}
-func exportProfileKeyValue(e environmentBase, val string) string {
- return fmt.Sprintf("\"$%s\" = \"%s\"", e.key(), val)
-}
-
-func newProfile(keys []string) CompletionProfile {
- p := CompletionProfile{}
- p.Clip = true
- p.List = true
- p.TOTP = true
- p.Write = true
- p.Generate = true
- name := ""
- sort.Strings(keys)
- var e []string
- for _, k := range keys {
- name = fmt.Sprintf("%s%s-", name, k)
- switch k {
- case askProfile:
- e = append(e, exportProfileKeyValue(envKeyMode.environmentBase, string(askKeyMode)))
- p.List = false
- case noTOTPProfile:
- e = append(e, exportProfileKeyValue(EnvNoTOTP.environmentBase, yes))
- p.TOTP = false
- case noClipProfile:
- e = append(e, exportProfileKeyValue(EnvNoClip.environmentBase, yes))
- p.Clip = false
- case roProfile:
- e = append(e, exportProfileKeyValue(EnvReadOnly.environmentBase, yes))
- p.Write = false
- case noGenProfile:
- e = append(e, exportProfileKeyValue(EnvNoPasswordGen.environmentBase, yes))
- p.Generate = false
- }
- }
- sort.Strings(e)
- p.Env = e
- p.Name = strings.TrimSuffix(name, "-")
- return p
-}
-
-func generateProfiles(keys []string) map[string]CompletionProfile {
- m := make(map[string]CompletionProfile)
- if len(keys) == 0 {
- return m
- }
- p := newProfile(keys)
- m[p.Name] = p
- for _, cur := range keys {
- var subset []string
- for _, key := range keys {
- if key == cur {
- continue
- }
- subset = append(subset, key)
- }
-
- for _, p := range generateProfiles(subset) {
- m[p.Name] = p
- }
- }
- return m
-}
-
-// LoadCompletionProfiles will generate known completion profile with backing env information
-func LoadCompletionProfiles() []CompletionProfile {
- loaded := generateProfiles([]string{noClipProfile, roProfile, noTOTPProfile, askProfile, noGenProfile})
- var profiles []CompletionProfile
- for _, v := range loaded {
- profiles = append(profiles, v)
- }
- sort.Slice(profiles, func(i, j int) bool {
- return strings.Compare(profiles[i].Name, profiles[j].Name) < 0
- })
- profiles = append(profiles, CompletionProfile{Generate: true, Clip: true, Write: true, TOTP: true, List: true, Default: true})
- return profiles
-}
-
// CanColor indicates if colorized output is allowed (or disabled)
func CanColor() (bool, error) {
if _, noColor := os.LookupEnv("NO_COLOR"); noColor {
diff --git a/internal/config/vars_test.go b/internal/config/vars_test.go
@@ -269,28 +269,6 @@ func TestEnvironDefinitions(t *testing.T) {
}
}
-func TestLoadCompletionProfiles(t *testing.T) {
- p := config.LoadCompletionProfiles()
- if len(p) != 32 {
- t.Errorf("invalid completion count: %d", len(p))
- }
- exp := len(p) - 1
- for idx, prof := range p {
- if prof.Default {
- if idx != exp {
- t.Error("profile defaulted incorrectly")
- }
- if prof.Name != "" {
- t.Error("default profile is unnamed")
- }
- } else {
- if len(prof.Env) == 0 {
- t.Error("profile has no environment information")
- }
- }
- }
-}
-
func TestCanColor(t *testing.T) {
defer os.Clearenv()
os.Clearenv()