Skip to content

Commit

Permalink
Merge branch 'main' of github.com:grafana/cuetsy into generate-imports
Browse files Browse the repository at this point in the history
  • Loading branch information
spinillos committed Apr 20, 2023
2 parents f144456 + 85523e0 commit 0a02617
Show file tree
Hide file tree
Showing 23 changed files with 873 additions and 350 deletions.
88 changes: 46 additions & 42 deletions analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/format"
)
Expand Down Expand Up @@ -62,7 +63,11 @@ func getKindFor(v cue.Value) (TSType, error) {
attr = a
}
}

if !found {
if t, ok := getKindForField(v); ok {
return t, nil
}
return "", valError(v, "value has no \"@%s\" attribute", attrname)
}

Expand All @@ -77,29 +82,40 @@ func getKindFor(v cue.Value) (TSType, error) {
return TSType(tt), nil
}

func getForceText(v cue.Value) string {
var found bool
var attr cue.Attribute
for _, a := range v.Attributes(cue.ValueAttr) {
if a.Name() == attrname {
found = true
attr = a
func getKindForField(v cue.Value) (TSType, bool) {
if field, ok := v.Source().(*ast.Field); ok {
if s, ok := field.Value.(*ast.StructLit); ok {
return iterateField(s)
}
}
if !found {
return ""
}
return "", false
}

ft, found, err := attr.Lookup(0, attrForceText)
if err != nil || !found {
return ""
func iterateField(s *ast.StructLit) (TSType, bool) {
for _, el := range s.Elts {
if field, ok := el.(*ast.Field); ok {
if len(field.Attrs) > 0 {
return parseStringAttribute(field.Attrs[0].Text), true
}
if s, ok := field.Value.(*ast.StructLit); ok {
return iterateField(s)
}
}
}

return ft
return "", false
}

func targetsAnyKind(v cue.Value) bool {
return targetsKind(v)
func parseStringAttribute(attr string) TSType {
switch attr {
case "@cuetsy(kind=\"interface\")":
return TypeInterface
case "@cuetsy(kind=\"enum\")":
return TypeEnum
case "@cuetsy(kind=\"type\")":
return TypeAlias
default:
return ""
}
}

func targetsKind(v cue.Value, kinds ...TSType) bool {
Expand All @@ -119,23 +135,6 @@ func targetsKind(v cue.Value, kinds ...TSType) bool {
return false
}

// containsReference recursively flattens expressions within a Value to find all
// its constituent Values, and checks if any of those Values are references.
//
// It does NOT walk struct fields - only expression structures, as returned from Expr().
// Remember that Expr() _always_ drops values in default branches.
func containsReference(v cue.Value) bool {
if isReference(v) {
return true
}
for _, dv := range flatten(v) {
if isReference(dv) {
return true
}
}
return false
}

// containsCuetsyReference does the same as containsReference, but returns true
// iff at least one referenced node passes the targetsKind predicate check
func containsCuetsyReference(v cue.Value, kinds ...TSType) bool {
Expand All @@ -160,8 +159,7 @@ func hasOverrideValues(v cue.Value, kinds ...TSType) bool {
return false
}

defaultOp, _ := values[1].Expr()
if defaultOp == cue.AndOp {
if values[1].Kind() != cue.BottomKind || values[1].IncompleteKind() == cue.TopKind {
return false
}

Expand Down Expand Up @@ -281,6 +279,12 @@ outer:
a = appendSplit(a, splitBy, v)
}
}

if defaultValue, is := v.Default(); is {
if o, _ := v.Expr(); o == cue.NoOp {
a = append(a, defaultValue)
}
}
return a
}

Expand Down Expand Up @@ -324,19 +328,19 @@ type listField struct {
props listProps
}

func (li *listField) eq(oli *listField) bool {
if li.props.isOpen == oli.props.isOpen && li.props.divergentTypes == oli.props.divergentTypes && li.lenElems == oli.lenElems {
if !li.props.isOpen {
if li.lenElems == 0 {
func (l *listField) eq(oli *listField) bool {
if l.props.isOpen == oli.props.isOpen && l.props.divergentTypes == oli.props.divergentTypes && l.lenElems == oli.lenElems {
if !l.props.isOpen {
if l.lenElems == 0 {
return true
}
p := cue.MakePath(cue.Index(0))
// Sloppy, but enough to cover all but really complicated cases that
// are likely unsupportable anyway
return li.v.LookupPath(p).Equals(oli.v.LookupPath(p))
return l.v.LookupPath(p).Equals(oli.v.LookupPath(p))
}

return li.anyType.Subsume(oli.anyType, cue.Raw(), cue.Schema()) == nil && oli.anyType.Subsume(li.anyType, cue.Raw(), cue.Schema()) == nil
return l.anyType.Subsume(oli.anyType, cue.Raw(), cue.Schema()) == nil && oli.anyType.Subsume(l.anyType, cue.Raw(), cue.Schema()) == nil
}

return false
Expand Down
11 changes: 2 additions & 9 deletions cmd/cuetsy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ type Options struct {
}

func convert(options Options) error {

wd, err := os.Getwd()
if err != nil {
os.Exit(1)
Expand All @@ -40,7 +39,6 @@ func convert(options Options) error {
for _, inst := range instances {
b, err := cuetsy.Generate(inst.Value(), cuetsy.Config{Export: options.Export})
if err != nil {

errors.Print(os.Stderr, err, &errors.Config{
Cwd: wd,
})
Expand All @@ -50,9 +48,9 @@ func convert(options Options) error {
if options.DestinationPath == "-" {
fmt.Println(string(b))
} else {

fd, err := os.Create(options.DestinationPath)
if err != nil {
fmt.Println(err.Error())
return err
}

Expand All @@ -65,9 +63,7 @@ func convert(options Options) error {
}

func main() {

options := Options{}

app := &cli.App{
Name: "cuetsy",
Usage: "Converting CUE objects to their TypeScript equivalent.",
Expand All @@ -92,7 +88,6 @@ func main() {
},
},
Action: func(cCtx *cli.Context) error {

if len(os.Args) == 1 {
fmt.Fprint(os.Stderr,
`
Expand All @@ -107,20 +102,18 @@ Try 'cuetsy --help' for more information.
options.CuePath = os.Args[1]
}

if options.DestinationPath == "" {
if options.DestinationPath == "" && len(os.Args) > 2 {
options.DestinationPath = options.CuePath[:len(options.CuePath)-3] + "ts"
}

convert(options)

return nil
},
}

if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}

os.Exit(0)

}
5 changes: 1 addition & 4 deletions cmd/cuetsy/test.cue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package cuetsy

E1: "e1str1" | "e1str2" | "e1str3" @cuetsy(kind="enum")
E2: "e2str1" | "e2str2" | "e2str3" | "e2str4" @cuetsy(kind="enum")
E3: {
Walla: "laadeedaa"
run: "OMG"
} @cuetsy(kind="enum")
E3: "laadeedaa" | "run" @cuetsy(kind="enum", memberNames="Walla|OMG")

I1: {
I1_OptionalDisjunctionLiteral?: "other" | "values" | 2
Expand Down
6 changes: 3 additions & 3 deletions dobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ func doit(conf NewConfig, inst *cue.Instance) (result []tsast.Decl, err error) {
panic("TODO")
}

func (c *buildContext) build(name string, v cue.Value) *tsoutput {
func (b *buildContext) build(name string, v cue.Value) *tsoutput {
// TODO should we let errors escape here? Maybe only unsupported-type ones?
return newTypeBuilder(c).enterGen(nil, name, v)
return newTypeBuilder(b).enterGen(nil, name, v)
}

func (c *buildContext) makeRef(inst *cue.Instance, ref []string) string {
func (b *buildContext) makeRef(inst *cue.Instance, ref []string) string {
ref = append([]string{}, ref...)

// NOTE this is where oapi does things with its NameFunc
Expand Down
6 changes: 1 addition & 5 deletions generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ func TestGenerateWithImports(t *testing.T) {
Name: "gen",
Update: *updateGolden,
ToDo: map[string]string{
"imports/oneref_verbose": "Figure out how to disambiguate struct literals from the struct-with-braces-and-one-element case",
"imports/struct_shorthand": "Shorthand struct notation is currently unsupported, needs fixing",
"imports/single_embed": "Single-item struct embeds should be treated as just another interface to compose, but get confused with references - #60",
"imports/inline_comments": "Inline comments do not appear be retrievable from cue.Value.Doc()",
"imports/nulltype": "null types are not handled correctly",
"imports/oneref_verbose": "Figure out how to disambiguate struct literals from the struct-with-braces-and-one-element case",
},
}

Expand Down
Loading

0 comments on commit 0a02617

Please sign in to comment.