1 // Copyright 2021 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cue 16 17 import ( 18 "fmt" 19 20 "cuelang.org/go/cue/ast" 21 "cuelang.org/go/internal" 22 "cuelang.org/go/internal/core/export" 23 ) 24 25 // Attribute returns the attribute data for the given key. 26 // The returned attribute will return an error for any of its methods if there 27 // is no attribute for the requested key. 28 func (v Value) Attribute(key string) Attribute { 29 // look up the attributes 30 if v.v == nil { 31 return nonExistAttr(key) 32 } 33 // look up the attributes 34 for _, a := range export.ExtractFieldAttrs(v.v) { 35 k, _ := a.Split() 36 if key != k { 37 continue 38 } 39 return newAttr(internal.FieldAttr, a) 40 } 41 42 return nonExistAttr(key) 43 } 44 45 func newAttr(k internal.AttrKind, a *ast.Attribute) Attribute { 46 key, body := a.Split() 47 // Note: the body is always positioned just after 48 // the opening ( after the key. 49 x := internal.ParseAttrBody(a.Pos().Add(len(key)+1), body) 50 x.Name = key 51 x.Kind = k 52 return Attribute{x} 53 } 54 55 func nonExistAttr(key string) Attribute { 56 a := internal.NewNonExisting(key) 57 a.Name = key 58 a.Kind = internal.FieldAttr 59 return Attribute{a} 60 } 61 62 // Attributes reports all field attributes for the Value. 63 // 64 // To retrieve attributes of multiple kinds, you can bitwise-or kinds together. 65 // Use ValueKind to query attributes associated with a value. 66 func (v Value) Attributes(mask AttrKind) []Attribute { 67 if v.v == nil { 68 return nil 69 } 70 71 attrs := []Attribute{} 72 73 if mask&FieldAttr != 0 { 74 for _, a := range export.ExtractFieldAttrs(v.v) { 75 attrs = append(attrs, newAttr(internal.FieldAttr, a)) 76 } 77 } 78 79 if mask&DeclAttr != 0 { 80 for _, a := range export.ExtractDeclAttrs(v.v) { 81 attrs = append(attrs, newAttr(internal.DeclAttr, a)) 82 } 83 } 84 85 return attrs 86 } 87 88 // AttrKind indicates the location of an attribute within CUE source. 89 type AttrKind int 90 91 const ( 92 // FieldAttr indicates a field attribute. 93 // foo: bar @attr() 94 FieldAttr AttrKind = AttrKind(internal.FieldAttr) 95 96 // DeclAttr indicates a declaration attribute. 97 // foo: { 98 // @attr() 99 // } 100 DeclAttr AttrKind = AttrKind(internal.DeclAttr) 101 102 // A ValueAttr is a bit mask to request any attribute that is locally 103 // associated with a field, instead of, for instance, an entire file. 104 ValueAttr AttrKind = FieldAttr | DeclAttr 105 106 // TODO: Possible future attr kinds 107 // ElemAttr (is a ValueAttr) 108 // FileAttr (not a ValueAttr) 109 110 // TODO: Merge: merge namesake attributes. 111 ) 112 113 // An Attribute contains metadata about a field. 114 // 115 // By convention, an attribute is split into positional arguments 116 // according to the rules below. However, these are not mandatory. 117 // To access the raw contents of an attribute, use [Attribute.Contents]. 118 // 119 // Arguments are of the form key[=value] where key and value each 120 // consist of an arbitrary number of CUE tokens with balanced brackets 121 // ((), [], and {}). These are the arguments retrieved by the 122 // [Attribute] methods. 123 // 124 // Leading and trailing white space will be stripped from both key and 125 // value. If there is no value and the key consists of exactly one 126 // quoted string, it will be unquoted. 127 type Attribute struct { 128 attr internal.Attr 129 } 130 131 // Format implements fmt.Formatter. 132 func (a Attribute) Format(w fmt.State, verb rune) { 133 fmt.Fprintf(w, "@%s(%s)", a.attr.Name, a.attr.Body) 134 } 135 136 var _ fmt.Formatter = &Attribute{} 137 138 // Name returns the name of the attribute, for instance, "json" for @json(...). 139 func (a *Attribute) Name() string { 140 return a.attr.Name 141 } 142 143 // Contents reports the full contents of an attribute within parentheses, so 144 // contents in @attr(contents). 145 func (a *Attribute) Contents() string { 146 return a.attr.Body 147 } 148 149 // NumArgs reports the number of arguments parsed for this attribute. 150 func (a *Attribute) NumArgs() int { 151 return len(a.attr.Fields) 152 } 153 154 // Arg reports the contents of the ith comma-separated argument of a. 155 // 156 // If the argument contains an unescaped equals sign, it returns a key-value 157 // pair. Otherwise it returns the contents in key. 158 func (a *Attribute) Arg(i int) (key, value string) { 159 // TODO: Returning the contents in key for a non-key-value argument 160 // is counter to the original documentation for this method and 161 // counter-intuitive too, but it remains that way to avoid breaking 162 // backward compatibility. In the future it would be nice to 163 // change it to return ("", value) in this case. 164 f := a.attr.Fields[i] 165 if f.Key() == "" { 166 return f.Value(), "" 167 } 168 return f.Key(), f.Value() 169 } 170 171 // RawArg reports the raw contents of the ith comma-separated argument of a, 172 // including surrounding spaces. 173 func (a *Attribute) RawArg(i int) string { 174 return a.attr.Fields[i].Text() 175 } 176 177 // Kind reports the type of location within CUE source where the attribute 178 // was specified. 179 func (a *Attribute) Kind() AttrKind { 180 return AttrKind(a.attr.Kind) 181 } 182 183 // Err returns the error associated with this Attribute or nil if this 184 // attribute is valid. 185 func (a *Attribute) Err() error { 186 return a.attr.Err 187 } 188 189 // String reports the possibly empty string value at the given position or 190 // an error the attribute is invalid or if the position does not exist. 191 func (a *Attribute) String(pos int) (string, error) { 192 return a.attr.String(pos) 193 } 194 195 // Int reports the integer at the given position or an error if the attribute is 196 // invalid, the position does not exist, or the value at the given position is 197 // not an integer. 198 func (a *Attribute) Int(pos int) (int64, error) { 199 return a.attr.Int(pos) 200 } 201 202 // Flag reports whether an entry with the given name exists at position pos or 203 // onwards or an error if the attribute is invalid or if the first pos-1 entries 204 // are not defined. 205 func (a *Attribute) Flag(pos int, key string) (bool, error) { 206 return a.attr.Flag(pos, key) 207 } 208 209 // Lookup searches for an entry of the form key=value from position pos onwards 210 // and reports the value if found. It reports an error if the attribute is 211 // invalid or if the first pos-1 entries are not defined. 212 func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) { 213 val, found, err = a.attr.Lookup(pos, key) 214 215 // TODO: remove at some point. This is an ugly hack to simulate the old 216 // behavior of protobufs. 217 if !found && a.attr.Name == "protobuf" && key == "type" { 218 val, err = a.String(1) 219 found = err == nil 220 } 221 return val, found, err 222 } 223