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		shell: shell.NewShell(
29			&shell.Options{
30				Env: env.Env(),
31			},
32		),
33	}
34}
35
36// ResolveValue is a method for resolving values, such as environment variables.
37// it will expect strings that start with `$` to be resolved as environment variables or shell commands.
38// if the string does not start with `$`, it will return the string as is.
39func (r *shellVariableResolver) ResolveValue(value string) (string, error) {
40	if !strings.HasPrefix(value, "$") {
41		return value, nil
42	}
43
44	if strings.HasPrefix(value, "$(") && strings.HasSuffix(value, ")") {
45		command := strings.TrimSuffix(strings.TrimPrefix(value, "$("), ")")
46		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
47		defer cancel()
48
49		stdout, _, err := r.shell.Exec(ctx, command)
50		if err != nil {
51			return "", fmt.Errorf("command execution failed: %w", err)
52		}
53		return strings.TrimSpace(stdout), nil
54	}
55
56	if strings.HasPrefix(value, "$") {
57		varName := strings.TrimPrefix(value, "$")
58		value = r.env.Get(varName)
59		if value == "" {
60			return "", fmt.Errorf("environment variable %q not set", varName)
61		}
62		return value, nil
63	}
64	return "", fmt.Errorf("invalid value format: %s", value)
65}
66
67type environmentVariableResolver struct {
68	env env.Env
69}
70
71func NewEnvironmentVariableResolver(env env.Env) VariableResolver {
72	return &environmentVariableResolver{
73		env: env,
74	}
75}
76
77// ResolveValue resolves environment variables from the provided env.Env.
78func (r *environmentVariableResolver) ResolveValue(value string) (string, error) {
79	if !strings.HasPrefix(value, "$") {
80		return value, nil
81	}
82
83	varName := strings.TrimPrefix(value, "$")
84	resolvedValue := r.env.Get(varName)
85	if resolvedValue == "" {
86		return "", fmt.Errorf("environment variable %q not set", varName)
87	}
88	return resolvedValue, nil
89}