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}