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}