cuelang.org/go@v0.10.1/cue/attribute.go (about) 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 x.Pos = a.Pos() 53 return Attribute{x} 54 } 55 56 func nonExistAttr(key string) Attribute { 57 a := internal.NewNonExisting(key) 58 a.Name = key 59 a.Kind = internal.FieldAttr 60 return Attribute{a} 61 } 62 63 // Attributes reports all field attributes for the Value. 64 // 65 // To retrieve attributes of multiple kinds, you can bitwise-or kinds together. 66 // Use ValueKind to query attributes associated with a value. 67 func (v Value) Attributes(mask AttrKind) []Attribute { 68 if v.v == nil { 69 return nil 70 } 71 72 attrs := []Attribute{} 73 74 if mask&FieldAttr != 0 { 75 for _, a := range export.ExtractFieldAttrs(v.v) { 76 attrs = append(attrs, newAttr(internal.FieldAttr, a)) 77 } 78 } 79 80 if mask&DeclAttr != 0 { 81 for _, a := range export.ExtractDeclAttrs(v.v) { 82 attrs = append(attrs, newAttr(internal.DeclAttr, a)) 83 } 84 } 85 86 return attrs 87 } 88 89 // AttrKind indicates the location of an attribute within CUE source. 90 type AttrKind int 91 92 const ( 93 // FieldAttr indicates a field attribute. 94 // foo: bar @attr() 95 FieldAttr AttrKind = AttrKind(internal.FieldAttr) 96 97 // DeclAttr indicates a declaration attribute. 98 // foo: { 99 // @attr() 100 // } 101 DeclAttr AttrKind = AttrKind(internal.DeclAttr) 102 103 // A ValueAttr is a bit mask to request any attribute that is locally 104 // associated with a field, instead of, for instance, an entire file. 105 ValueAttr AttrKind = FieldAttr | DeclAttr 106 107 // TODO: Possible future attr kinds 108 // ElemAttr (is a ValueAttr) 109 // FileAttr (not a ValueAttr) 110 111 // TODO: Merge: merge namesake attributes. 112 ) 113 114 // An Attribute contains metadata about a field. 115 // 116 // By convention, an attribute is split into positional arguments 117 // according to the rules below. However, these are not mandatory. 118 // To access the raw contents of an attribute, use [Attribute.Contents]. 119 // 120 // Arguments are of the form key[=value] where key and value each 121 // consist of an arbitrary number of CUE tokens with balanced brackets 122 // ((), [], and {}). These are the arguments retrieved by the 123 // [Attribute] methods. 124 // 125 // Leading and trailing white space will be stripped from both key and 126 // value. If there is no value and the key consists of exactly one 127 // quoted string, it will be unquoted. 128 type Attribute struct { 129 attr internal.Attr 130 } 131 132 // Format implements fmt.Formatter. 133 func (a Attribute) Format(w fmt.State, verb rune) { 134 fmt.Fprintf(w, "@%s(%s)", a.attr.Name, a.attr.Body) 135 } 136 137 var _ fmt.Formatter = &Attribute{} 138 139 // Name returns the name of the attribute, for instance, "json" for @json(...). 140 func (a *Attribute) Name() string { 141 return a.attr.Name 142 } 143 144 // Contents reports the full contents of an attribute within parentheses, so 145 // contents in @attr(contents). 146 func (a *Attribute) Contents() string { 147 return a.attr.Body 148 } 149 150 // NumArgs reports the number of arguments parsed for this attribute. 151 func (a *Attribute) NumArgs() int { 152 return len(a.attr.Fields) 153 } 154 155 // Arg reports the contents of the ith comma-separated argument of a. 156 // 157 // If the argument contains an unescaped equals sign, it returns a key-value 158 // pair. Otherwise it returns the contents in key. 159 func (a *Attribute) Arg(i int) (key, value string) { 160 // TODO: Returning the contents in key for a non-key-value argument 161 // is counter to the original documentation for this method and 162 // counter-intuitive too, but it remains that way to avoid breaking 163 // backward compatibility. In the future it would be nice to 164 // change it to return ("", value) in this case. 165 f := a.attr.Fields[i] 166 if f.Key() == "" { 167 return f.Value(), "" 168 } 169 return f.Key(), f.Value() 170 } 171 172 // RawArg reports the raw contents of the ith comma-separated argument of a, 173 // including surrounding spaces. 174 func (a *Attribute) RawArg(i int) string { 175 return a.attr.Fields[i].Text() 176 } 177 178 // Kind reports the type of location within CUE source where the attribute 179 // was specified. 180 func (a *Attribute) Kind() AttrKind { 181 return AttrKind(a.attr.Kind) 182 } 183 184 // Err returns the error associated with this Attribute or nil if this 185 // attribute is valid. 186 func (a *Attribute) Err() error { 187 return a.attr.Err 188 } 189 190 // String reports the possibly empty string value at the given position or 191 // an error the attribute is invalid or if the position does not exist. 192 func (a *Attribute) String(pos int) (string, error) { 193 return a.attr.String(pos) 194 } 195 196 // Int reports the integer at the given position or an error if the attribute is 197 // invalid, the position does not exist, or the value at the given position is 198 // not an integer. 199 func (a *Attribute) Int(pos int) (int64, error) { 200 return a.attr.Int(pos) 201 } 202 203 // Flag reports whether an entry with the given name exists at position pos or 204 // onwards or an error if the attribute is invalid or if the first pos-1 entries 205 // are not defined. 206 func (a *Attribute) Flag(pos int, key string) (bool, error) { 207 return a.attr.Flag(pos, key) 208 } 209 210 // Lookup searches for an entry of the form key=value from position pos onwards 211 // and reports the value if found. It reports an error if the attribute is 212 // invalid or if the first pos-1 entries are not defined. 213 func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) { 214 val, found, err = a.attr.Lookup(pos, key) 215 216 // TODO: remove at some point. This is an ugly hack to simulate the old 217 // behavior of protobufs. 218 if !found && a.attr.Name == "protobuf" && key == "type" { 219 val, err = a.String(1) 220 found = err == nil 221 } 222 return val, found, err 223 }