1// Copyright 2024 Google Inc. All rights reserved.
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
15package release_config_lib
16
17import (
18	"cmp"
19	"fmt"
20	"slices"
21
22	rc_proto "android/soong/cmd/release_config/release_config_proto"
23
24	"google.golang.org/protobuf/proto"
25)
26
27// A flag artifact, with its final value and declaration/override history.
28type FlagArtifact struct {
29	// The flag_declaration message.
30	FlagDeclaration *rc_proto.FlagDeclaration
31
32	// The index of the config directory where this flag was declared.
33	// Flag values cannot be set in a location with a lower index.
34	DeclarationIndex int
35
36	// A history of value assignments and overrides.
37	Traces []*rc_proto.Tracepoint
38
39	// The value of the flag.
40	Value *rc_proto.Value
41
42	// This flag is redacted.  Set by UpdateValue when the FlagValue proto
43	// says to redact it.
44	Redacted bool
45}
46
47// Key is flag name.
48type FlagArtifacts map[string]*FlagArtifact
49
50func FlagArtifactFactory(declPath string) *FlagArtifact {
51	fd := &rc_proto.FlagDeclaration{}
52	fa := &FlagArtifact{
53		FlagDeclaration:  fd,
54		DeclarationIndex: -1,
55		Traces:           []*rc_proto.Tracepoint{},
56	}
57	if declPath != "" {
58		LoadMessage(declPath, fd)
59		fa.Value = fd.GetValue()
60		fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(declPath), Value: fa.Value})
61	}
62	return fa
63}
64
65func FlagArtifactsFactory(artifactsPath string) *FlagArtifacts {
66	ret := make(FlagArtifacts)
67	if artifactsPath != "" {
68		fas := &rc_proto.FlagArtifacts{}
69		LoadMessage(artifactsPath, fas)
70		for _, fa_pb := range fas.FlagArtifacts {
71			fa := &FlagArtifact{}
72			fa.FlagDeclaration = fa_pb.GetFlagDeclaration()
73			if val := fa_pb.GetValue(); val != nil {
74				fa.Value = val
75			}
76			if traces := fa_pb.GetTraces(); traces != nil {
77				fa.Traces = traces
78			}
79			ret[*fa.FlagDeclaration.Name] = fa
80		}
81	}
82	return &ret
83}
84
85func (fas *FlagArtifacts) SortedFlagNames() []string {
86	var names []string
87	for k, _ := range *fas {
88		names = append(names, k)
89	}
90	slices.Sort(names)
91	return names
92}
93
94func (fa *FlagArtifact) GenerateFlagDeclarationArtifact() *rc_proto.FlagDeclarationArtifact {
95	ret := &rc_proto.FlagDeclarationArtifact{
96		Name:            fa.FlagDeclaration.Name,
97		DeclarationPath: fa.Traces[0].Source,
98	}
99	if namespace := fa.FlagDeclaration.GetNamespace(); namespace != "" {
100		ret.Namespace = proto.String(namespace)
101	}
102	if description := fa.FlagDeclaration.GetDescription(); description != "" {
103		ret.Description = proto.String(description)
104	}
105	if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_Workflow_Unspecified {
106		ret.Workflow = &workflow
107	}
108	if containers := fa.FlagDeclaration.GetContainers(); containers != nil {
109		ret.Containers = containers
110	}
111	return ret
112}
113
114func FlagDeclarationArtifactsFactory(path string) *rc_proto.FlagDeclarationArtifacts {
115	ret := &rc_proto.FlagDeclarationArtifacts{}
116	if path != "" {
117		LoadMessage(path, ret)
118	} else {
119		ret.FlagDeclarationArtifacts = []*rc_proto.FlagDeclarationArtifact{}
120	}
121	return ret
122}
123
124func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts {
125	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifacts: []*rc_proto.FlagDeclarationArtifact{}}
126	for _, fa := range *fas {
127		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fa.GenerateFlagDeclarationArtifact())
128	}
129	for _, fda := range intermediates {
130		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fda.FlagDeclarationArtifacts...)
131	}
132	slices.SortFunc(ret.FlagDeclarationArtifacts, func(a, b *rc_proto.FlagDeclarationArtifact) int {
133		return cmp.Compare(*a.Name, *b.Name)
134	})
135	return ret
136}
137
138// Create a clone of the flag artifact.
139//
140// Returns:
141//
142//	*FlagArtifact: the copy of the artifact.
143func (src *FlagArtifact) Clone() *FlagArtifact {
144	value := &rc_proto.Value{}
145	proto.Merge(value, src.Value)
146	return &FlagArtifact{
147		FlagDeclaration:  src.FlagDeclaration,
148		Traces:           src.Traces,
149		Value:            value,
150		DeclarationIndex: src.DeclarationIndex,
151		Redacted:         src.Redacted,
152	}
153}
154
155// Clone FlagArtifacts.
156//
157// Returns:
158//
159//	FlagArtifacts: a copy of the source FlagArtifacts.
160func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
161	if dst == nil {
162		dst = make(FlagArtifacts)
163	}
164	for k, v := range src {
165		dst[k] = v.Clone()
166	}
167	return
168}
169
170// Update the value of a flag.
171//
172// This appends to flagArtifact.Traces, and updates flagArtifact.Value.
173//
174// Args:
175//
176//	flagValue FlagValue: the value to assign
177//
178// Returns:
179//
180//	error: any error encountered
181func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
182	name := *flagValue.proto.Name
183	fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
184	if flagValue.proto.GetRedacted() {
185		fa.Redacted = true
186		fmt.Printf("Redacting flag %s in %s\n", name, flagValue.path)
187		return nil
188	}
189	if fa.Value.GetObsolete() {
190		return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
191	}
192	var newValue *rc_proto.Value
193	switch val := flagValue.proto.Value.Val.(type) {
194	case *rc_proto.Value_StringValue:
195		newValue = &rc_proto.Value{Val: &rc_proto.Value_StringValue{val.StringValue}}
196	case *rc_proto.Value_BoolValue:
197		newValue = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{val.BoolValue}}
198	case *rc_proto.Value_Obsolete:
199		if !val.Obsolete {
200			return fmt.Errorf("%s: Cannot set obsolete=false.  Trace=%v", name, fa.Traces)
201		}
202		newValue = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}}
203	default:
204		return fmt.Errorf("Invalid type for flag_value: %T.  Trace=%v", val, fa.Traces)
205	}
206	if proto.Equal(newValue, fa.Value) {
207		warnf("%s: redundant override (set in %s)\n", flagValue.path, *fa.Traces[len(fa.Traces)-2].Source)
208	}
209	fa.Value = newValue
210	return nil
211}
212
213// Marshal the FlagArtifact into a flag_artifact message.
214func (fa *FlagArtifact) Marshal() (*rc_proto.FlagArtifact, error) {
215	if fa.Redacted {
216		return nil, nil
217	}
218	return &rc_proto.FlagArtifact{
219		FlagDeclaration: fa.FlagDeclaration,
220		Value:           fa.Value,
221		Traces:          fa.Traces,
222	}, nil
223}
224
225// Marshal the FlagArtifact without Traces.
226func (fa *FlagArtifact) MarshalWithoutTraces() (*rc_proto.FlagArtifact, error) {
227	if fa.Redacted {
228		return nil, nil
229	}
230	return &rc_proto.FlagArtifact{
231		FlagDeclaration: fa.FlagDeclaration,
232		Value:           fa.Value,
233	}, nil
234}
235