confage/confage.go

111 lines
2.0 KiB
Go

package confage
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"strings"
"filippo.io/age"
)
// Type is a config type
type Type[T any] struct {
i age.Identity
r age.Recipient
Value T
}
// MustNew returns a new Type, panicking on error
func MustNew[T any](key string, val T) Type[T] {
t, err := New(key, val)
if err != nil {
panic(err)
}
return t
}
// New returns a new Type
func New[T any](key string, val T) (v Type[T], err error) {
v.Value = val
var i age.Identity
var r age.Recipient
switch {
case strings.HasPrefix(key, "AGE-SECRET-KEY-1"):
ii, err := age.ParseX25519Identity(key)
if err != nil {
return v, err
}
i, r = ii, ii.Recipient()
default:
i, err = age.NewScryptIdentity(key)
if err != nil {
return v, err
}
r, err = age.NewScryptRecipient(key)
if err != nil {
return v, err
}
}
v.i, v.r = i, r
return v, nil
}
// String implements fmt.Stringer
// It simply returns MarshalText
func (t Type[T]) String() string {
b, err := t.MarshalText()
if err != nil {
return fmt.Sprintf("string error: %v", err)
}
return string(b)
}
// MarshalText implements encoding.MarshalText
func (t Type[T]) MarshalText() ([]byte, error) {
var b bytes.Buffer
w, err := age.Encrypt(&b, t.r)
if err != nil {
return nil, err
}
if err := json.NewEncoder(w).Encode(t.Value); err != nil {
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
out := base64.StdEncoding.EncodeToString(b.Bytes())
return []byte(out), nil
}
// UnmarshalText implements encoding.UnmarshalText
func (t *Type[T]) UnmarshalText(text []byte) error {
b, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return err
}
r, err := age.Decrypt(bytes.NewBuffer(b), t.i)
if err != nil {
return err
}
var out bytes.Buffer
if _, err := io.Copy(&out, r); err != nil {
return err
}
var val T
if err := json.Unmarshal(out.Bytes(), &val); err != nil {
return err
}
(*t).Value = val
return nil
}