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 }