commit eb753d2a89ba8b323904500b25c0c6a332772dcb
parent 899195ad02cd51dcf2cb190849947cb206d8b0e6
Author: Sean Enck <sean@ttypty.com>
Date: Mon, 7 Oct 2024 18:51:42 -0400
cleaning up shell conditional handling
Diffstat:
6 files changed, 68 insertions(+), 96 deletions(-)
diff --git a/internal/app/completions.go b/internal/app/completions.go
@@ -6,11 +6,16 @@ import (
"embed"
"fmt"
"slices"
+ "sort"
"text/template"
"github.com/seanenck/lockbox/internal/config"
)
+const (
+ shellIsNotText = `[ "%s" != "%s" ]`
+)
+
type (
// Completions handles the inputs to completions for templating
Completions struct {
@@ -32,10 +37,12 @@ type (
TOTPSubCommands []CompletionOption
Conditionals struct {
Not struct {
- ReadOnly string
- NoClip string
- NoTOTP string
- AskMode string
+ ReadOnly string
+ NoClip string
+ NoTOTP string
+ AskMode string
+ Ever string
+ NoPasswordGen string
}
}
}
@@ -44,31 +51,28 @@ type (
Conditional string
Key string
}
- shellPreparer interface {
- ShellIsNotConditional(string) string
- }
- emptyShellPreparer struct{}
)
//go:embed shell/*
var shell embed.FS
-func (e emptyShellPreparer) ShellIsNotConditional(s string) string {
- return config.ShellIsNotConditional("1", s)
+func newShellIsNotEqualConditional(keyed interface{ Key() string }, right string) string {
+ return fmt.Sprintf(shellIsNotText, fmt.Sprintf("$%s", keyed.Key()), right)
}
-func newGenOptions(defaults []string, kv map[string]shellPreparer) []CompletionOption {
- genOption := func(to []CompletionOption, command string, prep shellPreparer, compareTo string) []CompletionOption {
- val := prep.ShellIsNotConditional(compareTo)
- return append(to, CompletionOption{val, command})
- }
+func (c Completions) newGenOptions(defaults []string, kv map[string]string) []CompletionOption {
opt := []CompletionOption{}
- emptyPrepare := emptyShellPreparer{}
for _, a := range defaults {
- opt = genOption(opt, a, emptyPrepare, "0")
+ opt = append(opt, CompletionOption{c.Conditionals.Not.Ever, a})
+ }
+ var keys []string
+ for k := range kv {
+ keys = append(keys, k)
}
- for key, env := range kv {
- opt = genOption(opt, key, env, config.YesValue)
+ sort.Strings(keys)
+ for _, key := range keys {
+ check := kv[key]
+ opt = append(opt, CompletionOption{check, key})
}
return opt
}
@@ -94,25 +98,27 @@ func GenerateCompletions(completionType, exe string) ([]string, error) {
DoList: fmt.Sprintf("%s %s", exe, ListCommand),
DoTOTPList: fmt.Sprintf("%s %s %s", exe, TOTPCommand, TOTPListCommand),
}
- c.Conditionals.Not.ReadOnly = config.EnvReadOnly.ShellIsNotConditional(config.YesValue)
- c.Conditionals.Not.NoClip = config.EnvNoClip.ShellIsNotConditional(config.YesValue)
- c.Conditionals.Not.NoTOTP = config.EnvNoTOTP.ShellIsNotConditional(config.YesValue)
- c.Conditionals.Not.AskMode = config.KeyModeIsNotAskConditional()
+ c.Conditionals.Not.ReadOnly = newShellIsNotEqualConditional(config.EnvReadOnly, config.YesValue)
+ c.Conditionals.Not.NoClip = newShellIsNotEqualConditional(config.EnvNoClip, config.YesValue)
+ c.Conditionals.Not.NoTOTP = newShellIsNotEqualConditional(config.EnvNoTOTP, config.YesValue)
+ c.Conditionals.Not.AskMode = newShellIsNotEqualConditional(config.EnvKeyMode, string(config.AskKeyMode))
+ c.Conditionals.Not.NoPasswordGen = newShellIsNotEqualConditional(config.EnvNoPasswordGen, config.YesValue)
+ c.Conditionals.Not.Ever = fmt.Sprintf(shellIsNotText, "1", "0")
- c.Options = newGenOptions([]string{EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
- map[string]shellPreparer{
- ClipCommand: config.EnvNoClip,
- TOTPCommand: config.EnvNoTOTP,
- MoveCommand: config.EnvReadOnly,
- RemoveCommand: config.EnvReadOnly,
- InsertCommand: config.EnvReadOnly,
- MultiLineCommand: config.EnvReadOnly,
- PasswordGenerateCommand: config.EnvNoPasswordGen,
+ c.Options = c.newGenOptions([]string{EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
+ map[string]string{
+ ClipCommand: c.Conditionals.Not.NoClip,
+ TOTPCommand: c.Conditionals.Not.NoTOTP,
+ MoveCommand: c.Conditionals.Not.ReadOnly,
+ RemoveCommand: c.Conditionals.Not.ReadOnly,
+ InsertCommand: c.Conditionals.Not.ReadOnly,
+ MultiLineCommand: c.Conditionals.Not.ReadOnly,
+ PasswordGenerateCommand: c.Conditionals.Not.NoPasswordGen,
})
- c.TOTPSubCommands = newGenOptions([]string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
- map[string]shellPreparer{
- TOTPClipCommand: config.EnvNoClip,
- TOTPInsertCommand: config.EnvReadOnly,
+ c.TOTPSubCommands = c.newGenOptions([]string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
+ map[string]string{
+ TOTPClipCommand: c.Conditionals.Not.NoClip,
+ TOTPInsertCommand: c.Conditionals.Not.ReadOnly,
})
using, err := readShell(completionType)
if err != nil {
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -129,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
@@ -145,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 {
@@ -179,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
}
@@ -195,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) {
@@ -346,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)
@@ -371,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 {
@@ -507,13 +507,3 @@ func newDefaultedEnvironment[T any](val T, base environmentBase) environmentDefa
obj.defaultValue = val
return obj
}
-
-// ShellIsNotConditional will produce a shell-ready conditional statement for an environment variable
-func (e environmentBase) ShellIsNotConditional(compareTo string) string {
- return ShellIsNotConditional(fmt.Sprintf("$%s", e.key()), compareTo)
-}
-
-// ShellIsNotConditional will produce a shell-ready conditional statement
-func ShellIsNotConditional(key, compareTo string) string {
- return fmt.Sprintf("[ \"%s\" != \"%s\" ]", key, compareTo)
-}
diff --git a/internal/config/core_test.go b/internal/config/core_test.go
@@ -52,20 +52,6 @@ func TestKeyValue(t *testing.T) {
}
}
-func TestShellIsNotConditionalEnv(t *testing.T) {
- val := config.EnvStore.ShellIsNotConditional("x")
- if val != `[ "$LOCKBOX_STORE" != "x" ]` {
- t.Errorf("invalid conditiona: %s", val)
- }
-}
-
-func TestShellIsNotConditional(t *testing.T) {
- val := config.ShellIsNotConditional("y", "x")
- if val != `[ "y" != "x" ]` {
- t.Errorf("invalid conditiona: %s", val)
- }
-}
-
func TestNewPlatform(t *testing.T) {
for _, item := range config.Platforms.List() {
os.Setenv("LOCKBOX_PLATFORM", item)
diff --git a/internal/config/key.go b/internal/config/key.go
@@ -23,8 +23,9 @@ type (
const (
plainKeyMode KeyModeType = "plaintext"
- askKeyMode KeyModeType = "ask"
- noKeyMode KeyModeType = "none"
+ // AskKeyMode is the mode in which the user is prompted for key input (each time)
+ AskKeyMode KeyModeType = "ask"
+ noKeyMode KeyModeType = "none"
// IgnoreKeyMode will ignore the value set in the key (acts like no key)
IgnoreKeyMode KeyModeType = "ignore"
commandKeyMode KeyModeType = "command"
@@ -35,7 +36,7 @@ const (
// NewKey will create a new key
func NewKey(defaultKeyModeType KeyModeType) (Key, error) {
useKey := envKey.Get()
- keyMode := envKeyMode.Get()
+ keyMode := EnvKeyMode.Get()
if keyMode == "" {
keyMode = string(defaultKeyModeType)
}
@@ -46,7 +47,7 @@ func NewKey(defaultKeyModeType KeyModeType) (Key, error) {
case string(noKeyMode):
requireEmptyKey = true
case string(commandKeyMode), string(plainKeyMode):
- case string(askKeyMode):
+ case string(AskKeyMode):
isInteractive, err := EnvInteractive.Get()
if err != nil {
return Key{}, err
@@ -77,7 +78,7 @@ func (k Key) empty() bool {
// Ask will indicate if prompting is required to get the key
func (k Key) Ask() bool {
- return k.valid && k.mode == askKeyMode
+ return k.valid && k.mode == AskKeyMode
}
// Read will read the key as configured by the mode
@@ -93,7 +94,7 @@ func (k Key) Read(ask AskPassword) (string, error) {
}
useKey := k.inputKey
switch k.mode {
- case askKeyMode:
+ case AskKeyMode:
read, err := ask()
if err != nil {
return "", err
@@ -117,8 +118,3 @@ func (k Key) Read(ask AskPassword) (string, error) {
}
return key, nil
}
-
-// KeyModeIsNotAskConditional will get the key mode for 'ask' as a shell conditional
-func KeyModeIsNotAskConditional() string {
- return envKeyMode.ShellIsNotConditional(string(askKeyMode))
-}
diff --git a/internal/config/key_test.go b/internal/config/key_test.go
@@ -202,10 +202,3 @@ func TestCommandKey(t *testing.T) {
t.Errorf("invalid error: %v", err)
}
}
-
-func TestKeyModeAskConditional(t *testing.T) {
- val := config.KeyModeIsNotAskConditional()
- if val != `[ "$LOCKBOX_KEYMODE" != "ask" ]` {
- t.Errorf("invalid value: %s", val)
- }
-}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -120,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{
@@ -288,7 +288,8 @@ Note that this setting is not output as part of the environment.`, noEnvironment
canDefault: true,
allowed: []string{detectEnvironment, fileExample, noEnvironment},
})
- envKeyMode = environmentRegister(
+ // EnvKeyMode is the variable for indicating the keymode used to get the key
+ EnvKeyMode = environmentRegister(
EnvironmentString{
environmentDefault: newDefaultedEnvironment(string(DefaultKeyMode),
environmentBase{
@@ -297,7 +298,7 @@ Note that this setting is not output as part of the environment.`, noEnvironment
desc: fmt.Sprintf(`How to retrieve the database store password. Set to '%s' when only using a key file.
Set to '%s' to ignore the set key value`, noKeyMode, IgnoreKeyMode),
}),
- allowed: []string{string(askKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)},
+ allowed: []string{string(AskKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)},
canDefault: true,
})
envKey = environmentRegister(
@@ -430,7 +431,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)