Skip to content

Commit 9d044f0

Browse files
committed
cue: hoist attribute-related functionality to separate file
Change-Id: I8d88a2ea34ce9e5a96124c5189987962929a1dfb Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9501 Reviewed-by: CUE cueckoo <[email protected]> Reviewed-by: Paul Jolly <[email protected]>
1 parent 5cd76bb commit 9d044f0

File tree

4 files changed

+551
-506
lines changed

4 files changed

+551
-506
lines changed

cue/attribute.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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/cue/token"
22+
"cuelang.org/go/internal"
23+
"cuelang.org/go/internal/core/export"
24+
)
25+
26+
// Attribute returns the attribute data for the given key.
27+
// The returned attribute will return an error for any of its methods if there
28+
// is no attribute for the requested key.
29+
func (v Value) Attribute(key string) Attribute {
30+
// look up the attributes
31+
if v.v == nil {
32+
return nonExistAttr(key)
33+
}
34+
// look up the attributes
35+
for _, a := range export.ExtractFieldAttrs(v.v) {
36+
k, _ := a.Split()
37+
if key != k {
38+
continue
39+
}
40+
return newAttr(internal.FieldAttr, a)
41+
}
42+
43+
return nonExistAttr(key)
44+
}
45+
46+
func newAttr(k internal.AttrKind, a *ast.Attribute) Attribute {
47+
key, body := a.Split()
48+
x := internal.ParseAttrBody(token.NoPos, body)
49+
x.Name = key
50+
x.Kind = k
51+
return Attribute{x}
52+
}
53+
54+
func nonExistAttr(key string) Attribute {
55+
a := internal.NewNonExisting(key)
56+
a.Name = key
57+
a.Kind = internal.FieldAttr
58+
return Attribute{a}
59+
}
60+
61+
// Attributes reports all field attributes for the Value.
62+
//
63+
// To retrieve attributes of multiple kinds, you can bitwise-or kinds together.
64+
// Use ValueKind to query attributes associated with a value.
65+
func (v Value) Attributes(mask AttrKind) []Attribute {
66+
if v.v == nil {
67+
return nil
68+
}
69+
70+
attrs := []Attribute{}
71+
72+
if mask&FieldAttr != 0 {
73+
for _, a := range export.ExtractFieldAttrs(v.v) {
74+
attrs = append(attrs, newAttr(internal.FieldAttr, a))
75+
}
76+
}
77+
78+
if mask&DeclAttr != 0 {
79+
for _, a := range export.ExtractDeclAttrs(v.v) {
80+
attrs = append(attrs, newAttr(internal.DeclAttr, a))
81+
}
82+
}
83+
84+
return attrs
85+
}
86+
87+
// AttrKind indicates the location of an attribute within CUE source.
88+
type AttrKind int
89+
90+
const (
91+
// FieldAttr indicates a field attribute.
92+
// foo: bar @attr()
93+
FieldAttr AttrKind = AttrKind(internal.FieldAttr)
94+
95+
// DeclAttr indicates a declaration attribute.
96+
// foo: {
97+
// @attr()
98+
// }
99+
DeclAttr AttrKind = AttrKind(internal.DeclAttr)
100+
101+
// A ValueAttr is a bit mask to request any attribute that is locally
102+
// associated with a field, instead of, for instance, an entire file.
103+
ValueAttr AttrKind = FieldAttr | DeclAttr
104+
105+
// TODO: Possible future attr kinds
106+
// ElemAttr (is a ValueAttr)
107+
// FileAttr (not a ValueAttr)
108+
109+
// TODO: Merge: merge namesake attributes.
110+
)
111+
112+
// An Attribute contains meta data about a field.
113+
type Attribute struct {
114+
attr internal.Attr
115+
}
116+
117+
// Format implements fmt.Formatter.
118+
func (a Attribute) Format(w fmt.State, verb rune) {
119+
fmt.Fprintf(w, "@%s(%s)", a.attr.Name, a.attr.Body)
120+
}
121+
122+
var _ fmt.Formatter = &Attribute{}
123+
124+
// Name returns the name of the attribute, for instance, "json" for @json(...).
125+
func (a *Attribute) Name() string {
126+
return a.attr.Name
127+
}
128+
129+
// Contents reports the full contents of an attribute within parentheses, so
130+
// contents in @attr(contents).
131+
func (a *Attribute) Contents() string {
132+
return a.attr.Body
133+
}
134+
135+
// NumArgs reports the number of arguments parsed for this attribute.
136+
func (a *Attribute) NumArgs() int {
137+
return len(a.attr.Fields)
138+
}
139+
140+
// Arg reports the contents of the ith comma-separated argument of a.
141+
//
142+
// If the argument contains an unescaped equals sign, it returns a key-value
143+
// pair. Otherwise it returns the contents in value.
144+
func (a *Attribute) Arg(i int) (key, value string) {
145+
f := a.attr.Fields[i]
146+
return f.Key(), f.Value()
147+
}
148+
149+
// RawArg reports the raw contents of the ith comma-separated argument of a,
150+
// including surrounding spaces.
151+
func (a *Attribute) RawArg(i int) string {
152+
return a.attr.Fields[i].Text()
153+
}
154+
155+
// Kind reports the type of location within CUE source where the attribute
156+
// was specified.
157+
func (a *Attribute) Kind() AttrKind {
158+
return AttrKind(a.attr.Kind)
159+
}
160+
161+
// Err returns the error associated with this Attribute or nil if this
162+
// attribute is valid.
163+
func (a *Attribute) Err() error {
164+
return a.attr.Err
165+
}
166+
167+
// String reports the possibly empty string value at the given position or
168+
// an error the attribute is invalid or if the position does not exist.
169+
func (a *Attribute) String(pos int) (string, error) {
170+
return a.attr.String(pos)
171+
}
172+
173+
// Int reports the integer at the given position or an error if the attribute is
174+
// invalid, the position does not exist, or the value at the given position is
175+
// not an integer.
176+
func (a *Attribute) Int(pos int) (int64, error) {
177+
return a.attr.Int(pos)
178+
}
179+
180+
// Flag reports whether an entry with the given name exists at position pos or
181+
// onwards or an error if the attribute is invalid or if the first pos-1 entries
182+
// are not defined.
183+
func (a *Attribute) Flag(pos int, key string) (bool, error) {
184+
return a.attr.Flag(pos, key)
185+
}
186+
187+
// Lookup searches for an entry of the form key=value from position pos onwards
188+
// and reports the value if found. It reports an error if the attribute is
189+
// invalid or if the first pos-1 entries are not defined.
190+
func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) {
191+
val, found, err = a.attr.Lookup(pos, key)
192+
193+
// TODO: remove at some point. This is an ugly hack to simulate the old
194+
// behavior of protobufs.
195+
if !found && a.attr.Name == "protobuf" && key == "type" {
196+
val, err = a.String(1)
197+
found = err == nil
198+
}
199+
return val, found, err
200+
}

0 commit comments

Comments
 (0)