templates.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Package templates provides the built-in provider env-key skeletons shipped
  2. // inside the cc-switch binary. The data is embedded at compile time from
  3. // templates.yaml; no network or external file I/O is ever performed.
  4. //
  5. // Templates intentionally do NOT include secrets — only key names, hints, and
  6. // occasional defaults (e.g. a provider's canonical base URL).
  7. package templates
  8. import (
  9. _ "embed"
  10. "fmt"
  11. "sort"
  12. "gopkg.in/yaml.v3"
  13. )
  14. // Template describes one provider env skeleton.
  15. type Template struct {
  16. Name string `yaml:"name"`
  17. Description string `yaml:"description"`
  18. Env []EnvKey `yaml:"env"`
  19. }
  20. // EnvKey is one env entry inside a template.
  21. type EnvKey struct {
  22. Name string `yaml:"name"`
  23. Hint string `yaml:"hint,omitempty"`
  24. Default string `yaml:"default,omitempty"`
  25. }
  26. //go:embed templates.yaml
  27. var embeddedYAML []byte
  28. type fileShape struct {
  29. Templates []Template `yaml:"templates"`
  30. }
  31. // Parse parses the given YAML bytes into a slice of Templates. Exposed for
  32. // testing.
  33. func Parse(data []byte) ([]Template, error) {
  34. var f fileShape
  35. if err := yaml.Unmarshal(data, &f); err != nil {
  36. return nil, fmt.Errorf("parse templates: %w", err)
  37. }
  38. for i, t := range f.Templates {
  39. if t.Name == "" {
  40. return nil, fmt.Errorf("template[%d]: empty name", i)
  41. }
  42. if len(t.Env) == 0 {
  43. return nil, fmt.Errorf("template %q: env must have at least one key", t.Name)
  44. }
  45. for j, e := range t.Env {
  46. if e.Name == "" {
  47. return nil, fmt.Errorf("template %q env[%d]: empty key name", t.Name, j)
  48. }
  49. }
  50. }
  51. return f.Templates, nil
  52. }
  53. var cache []Template
  54. // All returns every embedded template. The result is cached; callers MUST NOT
  55. // mutate the returned slice (treat as read-only).
  56. func All() []Template {
  57. if cache != nil {
  58. return cache
  59. }
  60. ts, err := Parse(embeddedYAML)
  61. if err != nil {
  62. // Embedded data is authored in-repo; a parse failure here is a
  63. // programmer bug caught by tests, not a runtime condition.
  64. panic(fmt.Sprintf("embedded templates.yaml is invalid: %v", err))
  65. }
  66. cache = ts
  67. return cache
  68. }
  69. // Get returns the template with the given name.
  70. func Get(name string) (Template, bool) {
  71. for _, t := range All() {
  72. if t.Name == name {
  73. return t, true
  74. }
  75. }
  76. return Template{}, false
  77. }
  78. // Names returns a sorted list of available template names.
  79. func Names() []string {
  80. all := All()
  81. names := make([]string, len(all))
  82. for i, t := range all {
  83. names[i] = t.Name
  84. }
  85. sort.Strings(names)
  86. return names
  87. }