resolve.go

 1package config
 2
 3import (
 4	"context"
 5	"fmt"
 6	"strings"
 7	"time"
 8
 9	"github.com/charmbracelet/crush/internal/shell"
10	"github.com/charmbracelet/crush/pkg/env"
11)
12
13type VariableResolver interface {
14	ResolveValue(value string) (string, error)
15}
16
17type Shell interface {
18	Exec(ctx context.Context, command string) (stdout, stderr string, err error)
19}
20
21type shellVariableResolver struct {
22	shell Shell
23	env   env.Env
24}
25
26func NewShellVariableResolver(env env.Env) VariableResolver {
27	return &shellVariableResolver{
28		env: env,
29		shell: shell.NewShell(
30			&shell.Options{
31				Env: env.Env(),
32			},
33		),
34	}
35}
36
37// ResolveValue is a method for resolving values, such as environment variables.
38// it will expect strings that start with `$` to be resolved as environment variables or shell commands.
39// if the string does not start with `$`, it will return the string as is.
40func (r *shellVariableResolver) ResolveValue(value string) (string, error) {
41	if !strings.HasPrefix(value, "$") {
42		return value, nil
43	}
44
45	if strings.HasPrefix(value, "$(") && strings.HasSuffix(value, ")") {
46		command := strings.TrimSuffix(strings.TrimPrefix(value, "$("), ")")
47		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
48		defer cancel()
49
50		stdout, _, err := r.shell.Exec(ctx, command)
51		if err != nil {
52			return "", fmt.Errorf("command execution failed: %w", err)
53		}
54		return strings.TrimSpace(stdout), nil
55	}
56
57	if strings.HasPrefix(value, "$") {
58		varName := strings.TrimPrefix(value, "$")
59		value = r.env.Get(varName)
60		if value == "" {
61			return "", fmt.Errorf("environment variable %q not set", varName)
62		}
63		return value, nil
64	}
65	return "", fmt.Errorf("invalid value format: %s", value)
66}
67
68type environmentVariableResolver struct {
69	env env.Env
70}
71
72func NewEnvironmentVariableResolver(env env.Env) VariableResolver {
73	return &environmentVariableResolver{
74		env: env,
75	}
76}
77
78// ResolveValue resolves environment variables from the provided env.Env.
79func (r *environmentVariableResolver) ResolveValue(value string) (string, error) {
80	if !strings.HasPrefix(value, "$") {
81		return value, nil
82	}
83
84	varName := strings.TrimPrefix(value, "$")
85	resolvedValue := r.env.Get(varName)
86	if resolvedValue == "" {
87		return "", fmt.Errorf("environment variable %q not set", varName)
88	}
89	return resolvedValue, nil
90}