1package expression
2
3import (
4	"strings"
5)
6
7// ProjectionBuilder represents Projection Expressions in DynamoDB.
8// ProjectionBuilders are the building blocks of Builders.
9// More Information at: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html
10type ProjectionBuilder struct {
11	names []NameBuilder
12}
13
14// NamesList returns a ProjectionBuilder representing the list of item
15// attribute names specified by the argument NameBuilders. The resulting
16// ProjectionBuilder can be used as a part of other ProjectionBuilders or as an
17// argument to the WithProjection() method for the Builder struct.
18//
19// Example:
20//
21//     // projection represents the list of names {"foo", "bar"}
22//     projection := expression.NamesList(expression.Name("foo"), expression.Name("bar"))
23//
24//     // Used in another Projection Expression
25//     anotherProjection := expression.AddNames(projection, expression.Name("baz"))
26//     // Used to make an Builder
27//     builder := expression.NewBuilder().WithProjection(newProjection)
28//
29// Expression Equivalent:
30//
31//     expression.NamesList(expression.Name("foo"), expression.Name("bar"))
32//     "foo, bar"
33func NamesList(nameBuilder NameBuilder, namesList ...NameBuilder) ProjectionBuilder {
34	namesList = append([]NameBuilder{nameBuilder}, namesList...)
35	return ProjectionBuilder{
36		names: namesList,
37	}
38}
39
40// NamesList returns a ProjectionBuilder representing the list of item
41// attribute names specified by the argument NameBuilders. The resulting
42// ProjectionBuilder can be used as a part of other ProjectionBuilders or as an
43// argument to the WithProjection() method for the Builder struct.
44//
45// Example:
46//
47//     // projection represents the list of names {"foo", "bar"}
48//     projection := expression.Name("foo").NamesList(expression.Name("bar"))
49//
50//     // Used in another Projection Expression
51//     anotherProjection := expression.AddNames(projection, expression.Name("baz"))
52//     // Used to make an Builder
53//     builder := expression.NewBuilder().WithProjection(newProjection)
54//
55// Expression Equivalent:
56//
57//     expression.Name("foo").NamesList(expression.Name("bar"))
58//     "foo, bar"
59func (nb NameBuilder) NamesList(namesList ...NameBuilder) ProjectionBuilder {
60	return NamesList(nb, namesList...)
61}
62
63// AddNames returns a ProjectionBuilder representing the list of item
64// attribute names equivalent to appending all of the argument item attribute
65// names to the argument ProjectionBuilder. The resulting ProjectionBuilder can
66// be used as a part of other ProjectionBuilders or as an argument to the
67// WithProjection() method for the Builder struct.
68//
69// Example:
70//
71//     // projection represents the list of names {"foo", "bar", "baz", "qux"}
72//     oldProj := expression.NamesList(expression.Name("foo"), expression.Name("bar"))
73//     projection := expression.AddNames(oldProj, expression.Name("baz"), expression.Name("qux"))
74//
75//     // Used in another Projection Expression
76//     anotherProjection := expression.AddNames(projection, expression.Name("quux"))
77//     // Used to make an Builder
78//     builder := expression.NewBuilder().WithProjection(newProjection)
79//
80// Expression Equivalent:
81//
82//     expression.AddNames(expression.NamesList(expression.Name("foo"), expression.Name("bar")), expression.Name("baz"), expression.Name("qux"))
83//     "foo, bar, baz, qux"
84func AddNames(projectionBuilder ProjectionBuilder, namesList ...NameBuilder) ProjectionBuilder {
85	projectionBuilder.names = append(projectionBuilder.names, namesList...)
86	return projectionBuilder
87}
88
89// AddNames returns a ProjectionBuilder representing the list of item
90// attribute names equivalent to appending all of the argument item attribute
91// names to the argument ProjectionBuilder. The resulting ProjectionBuilder can
92// be used as a part of other ProjectionBuilders or as an argument to the
93// WithProjection() method for the Builder struct.
94//
95// Example:
96//
97//     // projection represents the list of names {"foo", "bar", "baz", "qux"}
98//     oldProj := expression.NamesList(expression.Name("foo"), expression.Name("bar"))
99//     projection := oldProj.AddNames(expression.Name("baz"), expression.Name("qux"))
100//
101//     // Used in another Projection Expression
102//     anotherProjection := expression.AddNames(projection, expression.Name("quux"))
103//     // Used to make an Builder
104//     builder := expression.NewBuilder().WithProjection(newProjection)
105//
106// Expression Equivalent:
107//
108//     expression.NamesList(expression.Name("foo"), expression.Name("bar")).AddNames(expression.Name("baz"), expression.Name("qux"))
109//     "foo, bar, baz, qux"
110func (pb ProjectionBuilder) AddNames(namesList ...NameBuilder) ProjectionBuilder {
111	return AddNames(pb, namesList...)
112}
113
114// buildTree builds a tree structure of exprNodes based on the tree
115// structure of the input ProjectionBuilder's child NameBuilders. buildTree()
116// satisfies the treeBuilder interface so ProjectionBuilder can be a part of
117// Builder and Expression struct.
118func (pb ProjectionBuilder) buildTree() (exprNode, error) {
119	if len(pb.names) == 0 {
120		return exprNode{}, newUnsetParameterError("buildTree", "ProjectionBuilder")
121	}
122
123	childNodes, err := pb.buildChildNodes()
124	if err != nil {
125		return exprNode{}, err
126	}
127	ret := exprNode{
128		children: childNodes,
129	}
130
131	ret.fmtExpr = "$c" + strings.Repeat(", $c", len(pb.names)-1)
132
133	return ret, nil
134}
135
136// buildChildNodes creates the list of the child exprNodes.
137func (pb ProjectionBuilder) buildChildNodes() ([]exprNode, error) {
138	childNodes := make([]exprNode, 0, len(pb.names))
139	for _, name := range pb.names {
140		operand, err := name.BuildOperand()
141		if err != nil {
142			return []exprNode{}, err
143		}
144		childNodes = append(childNodes, operand.exprNode)
145	}
146
147	return childNodes, nil
148}
149