cuelang.org/go@v0.10.1/internal/buildattr/buildattr.go (about) 1 // Package buildattr implements support for interpreting the @if 2 // build attributes in CUE files. 3 package buildattr 4 5 import ( 6 "cuelang.org/go/cue/ast" 7 "cuelang.org/go/cue/errors" 8 "cuelang.org/go/cue/parser" 9 "cuelang.org/go/cue/token" 10 ) 11 12 // ShouldIgnoreFile reports whether a File contains an @ignore() file-level 13 // attribute and hence should be ignored. 14 func ShouldIgnoreFile(f *ast.File) bool { 15 ignore, _, _ := getBuildAttr(f) 16 return ignore 17 } 18 19 // ShouldBuildFile reports whether a File should be included based on its 20 // attributes. It uses tagIsSet to determine whether a given attribute 21 // key should be treated as set. 22 // 23 // It also returns the build attribute if one was found. 24 func ShouldBuildFile(f *ast.File, tagIsSet func(key string) bool) (bool, *ast.Attribute, errors.Error) { 25 ignore, a, err := getBuildAttr(f) 26 if ignore || err != nil { 27 return false, a, err 28 } 29 if a == nil { 30 return true, nil, nil 31 } 32 33 _, body := a.Split() 34 35 expr, parseErr := parser.ParseExpr("", body) 36 if parseErr != nil { 37 return false, a, errors.Promote(parseErr, "") 38 } 39 40 include, err := shouldInclude(expr, tagIsSet) 41 if err != nil { 42 return false, a, err 43 } 44 return include, a, nil 45 } 46 47 func getBuildAttr(f *ast.File) (ignore bool, a *ast.Attribute, err errors.Error) { 48 for _, d := range f.Decls { 49 switch x := d.(type) { 50 case *ast.Attribute: 51 switch key, _ := x.Split(); key { 52 case "ignore": 53 return true, x, nil 54 case "if": 55 if a != nil { 56 err := errors.Newf(d.Pos(), "multiple @if attributes") 57 err = errors.Append(err, 58 errors.Newf(a.Pos(), "previous declaration here")) 59 return false, a, err 60 } 61 a = x 62 } 63 case *ast.Package: 64 return false, a, nil 65 case *ast.CommentGroup: 66 default: 67 // If it's anything else, then we know we won't see a package 68 // clause so avoid scanning more than we need to (this 69 // could be a large file with no package clause) 70 return false, a, nil 71 } 72 } 73 return false, a, nil 74 } 75 76 func shouldInclude(expr ast.Expr, tagIsSet func(key string) bool) (bool, errors.Error) { 77 switch x := expr.(type) { 78 case *ast.Ident: 79 return tagIsSet(x.Name), nil 80 81 case *ast.ParenExpr: 82 return shouldInclude(x.X, tagIsSet) 83 84 case *ast.BinaryExpr: 85 switch x.Op { 86 case token.LAND, token.LOR: 87 a, err := shouldInclude(x.X, tagIsSet) 88 if err != nil { 89 return false, err 90 } 91 b, err := shouldInclude(x.Y, tagIsSet) 92 if err != nil { 93 return false, err 94 } 95 if x.Op == token.LAND { 96 return a && b, nil 97 } 98 return a || b, nil 99 100 default: 101 return false, errors.Newf(token.NoPos, "invalid operator %v in build attribute", x.Op) 102 } 103 104 case *ast.UnaryExpr: 105 if x.Op != token.NOT { 106 return false, errors.Newf(token.NoPos, "invalid operator %v in build attribute", x.Op) 107 } 108 v, err := shouldInclude(x.X, tagIsSet) 109 if err != nil { 110 return false, err 111 } 112 return !v, nil 113 114 default: 115 return false, errors.Newf(token.NoPos, "invalid type %T in build attribute", expr) 116 } 117 }