Skip to content

Instantly share code, notes, and snippets.

@sdboyer
Last active October 3, 2022 14:56
Show Gist options
  • Save sdboyer/b55ada925904c8e61a543c36907d3284 to your computer and use it in GitHub Desktop.
Save sdboyer/b55ada925904c8e61a543c36907d3284 to your computer and use it in GitHub Desktop.
rapidly prototyped demo of thema and codegen for loki config file
package lokicfg
import "github.com/grafana/thema"
// - MIGHT be helpful for them if there could be an init command that tries to start from Go structs
// - Probably need to generate YAML struct tags
thema.#Lineage
name: "lokicfg"
seqs: [
{
schemas: [
// v0.0
{
server: {
port: int32
}
},
// v0.1
{
server: {
port: int32
tlsVersion: int32 & (*1 | 2)
}
}
]
},
]
// WE'RE IMAGINING THIS FILE IS AUTOGENERATED
package lokicfg
import (
"embed"
"sync"
"cuelang.org/go/cue/cuecontext"
"github.com/grafana/thema"
"github.com/grafana/thema/load"
"github.com/grafana/thema/vmux"
)
type Lokicfg struct {
Server struct {
Port int32 `json:"port"`
TlsVersion int32 `json:"tlsVersion"`
} `json:"server"`
}
// vars representing the state of the lineage cue file
var (
//go:embed lokicfg.cue
cueFS embed.FS
currentVersion = thema.SV(0, 1)
)
// vars for handling caching of the lineage factory output
var (
lineageOnce sync.Once
clin thema.ConvergentLineage[*Lokicfg]
clinerr error
)
var allctx = cuecontext.New()
var allrt = thema.NewRuntime(allctx)
// type guard
var _ thema.ConvergentLineageFactory[*Lokicfg] = Lineage
func init() {
// It's probably useful to trigger this on init, and panic right away if there's
// a problem, as the only possible fix is updating lokicfg.cue.
_, err := Lineage(allrt)
if err != nil {
panic(err)
}
}
func Lineage(rt *thema.Runtime, opt ...thema.BindOption) (thema.ConvergentLineage[*Lokicfg], error) {
if rt == nil || rt == allrt {
lineageOnce.Do(func() {
// This freezes the thema.BindOption set to whatever is passed in on the first
// call. That could be problematic in the future, but for now the set is really
// small.
clin, clinerr = doLineage(allrt, opt...)
})
return clin, clinerr
}
return doLineage(rt, opt...)
}
func doLineage(rt *thema.Runtime, opt ...thema.BindOption) (thema.ConvergentLineage[*Lokicfg], error) {
linval, err := load.InstancesWithThema(cueFS, ".")
if err != nil {
return nil, err
}
// this returns a thema.Lineage
lin, err := thema.BindLineage(rt.Context().BuildInstance(linval), rt)
if err != nil {
// This would error out if you have a generally invalid lineage -
// e.g., schemas don't follow backwards compatibility rules,
// lenses are incomplete, etc.; all the general invariants Thema
// aims to provide over all lineages
return nil, err
}
sch, _ := lin.Schema(currentVersion)
tsch, err := thema.BindType[*Lokicfg](sch, &Lokicfg{})
if err != nil {
// This would error out if the Lokicfg isn't assignable to the 0.1 schema.
// This should be unreachable, barring a critical bug in Thema's Go generator.
return nil, err
}
return tsch.ConvergentLineage(), nil
}
func GimmeValueMux(rt *thema.Runtime) vmux.ValueMux[*Lokicfg] {
lin, err := Lineage(rt)
if err != nil {
panic(err)
}
return vmux.NewValueMux(lin.TypedSchema(), vmux.NewYAMLEndec("conf.yaml"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment