Last active
January 4, 2022 03:41
-
-
Save sdboyer/9184e1bce43911ca31a6c33358d2a6a1 to your computer and use it in GitHub Desktop.
Thema input kernel handwavy code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kernel | |
import ( | |
"cuelang.org/go/cue" | |
"cuelang.org/go/encoding/gocode/gocodec" | |
"github.com/grafana/thema" | |
) | |
// An InputKernel accepts all the valid inputs for a given lineage, converges | |
// them onto a single statically-chosen schema version, and emits the result in | |
// a native Go type. | |
// | |
// It's a one-stop shop for encapsulating version juggling of inputs at the | |
// boundary of your program. | |
type InputKernel struct { | |
tf TypeFactory | |
df Decoder | |
lin thema.Lineage | |
to thema.SyntacticVersion | |
ctx *cue.Context | |
// TODO add something for interrupting/mediating translation vis-a-vis accumulated lacunae | |
} | |
// NewInputKernel constructs an input kernel. | |
// | |
// InputKernels accepts as input data, in whatever format (e.g. JSON, YAML) | |
// supported by its Decoder, validates the data, translates it to a single target version, then | |
func NewInputKernel(lin thema.Lineage, tf TypeFactory, df Decoder, to thema.SyntacticVersion) (*InputKernel, error) { | |
// can't even | |
if lin == nil { | |
panic("must provide a non-nil lineage") | |
} | |
// The concurrency warnings in the docs on Codec are concerning - don't use | |
// the Runtime concurrently for any other operations, but concurrent use of | |
// only the codec is fine? ugh, that wouldn't be easy to coordinate under the | |
// best of circumstances. And there's really nothing we can do in a | |
// situation like this. So...guess we're YOLOing it for now? | |
codec := gocodec.New((*cue.Runtime)(lin.RawValue().Context()), nil) | |
sch, err := thema.Pick(lin, to) | |
if err != nil { | |
return nil, err | |
} | |
// Verify that the input Go type is valid with respect to the indicated | |
// schema. Effect is that the caller cannot get an InputKernel without a | |
// valid Go type to write to. | |
// | |
// TODO verify this is actually how we check this | |
if err = codec.Validate(sch.RawValue(), tf()); err != nil { | |
return nil, err | |
} | |
return &InputKernel{ | |
tf: tf, | |
df: df, | |
lin: lin, | |
to: to, | |
}, nil | |
} | |
// A TypeFactory must emit a pointer to the Go type that a kernel will | |
// ultimately produce as output. | |
// | |
// TODO the function accomplished by this should be trivial to achieve with generics...? | |
type TypeFactory func() interface{} | |
// A Decoder takes some input data and decodes it into a cue.Value. | |
type Decoder func(*cue.Context, interface{}) (cue.Value, error) | |
// Converge runs data through the kernel: validate, translate to a fixed | |
// version, return transformed instance along with any emitted lacunae. | |
// | |
// Valid types for the input are governed by the Decoder func with which the | |
// kernel was constructed. | |
// | |
// Type safety of the return value is guaranteed by checks performed in | |
// NewInputKernel() - if error is non-nil, the first return value is guaranteed | |
// to be an instance of the type returned from the TypeFactory with which the | |
// kernel was constructed. | |
func (k *InputKernel) Converge(data interface{}) (interface{}, thema.TranslationLacunae, error) { | |
ctx := k.lin.RawValue().Context() | |
// Decode the input data into a cue.Value | |
v, err := k.df(ctx, data) | |
if err != nil { | |
return nil, nil, err | |
} | |
// Validate that the data constitutes an instance of at least one of the schemas in the lineage | |
inst, err := thema.SearchAndValidate(k.lin, v) | |
if err != nil { | |
return nil, nil, err | |
} | |
transval, lac := thema.Translate(inst, k.to) | |
ret := k.tf() | |
transval.Decode(ret) | |
return ret, lac, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment