001/*
002 * Copyright (c) 2023 Chris K Wensel <[email protected]>. All Rights Reserved.
003 *
004 * This Source Code Form is subject to the terms of the Mozilla Public
005 * License, v. 2.0. If a copy of the MPL was not distributed with this
006 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
007 */
008
009package clusterless.commons.naming;
010
011import java.util.Objects;
012
013/**
014 * Creates an identifier to use as an export/import key for provider output/export values.
015 * <p/>
016 * <pre>
017 * aws:qualifier:[stage:]scopeName:scopeVersion:resourceNS:resourceType:resourceName
018 * </pre>
019 * <p/>
020 * Where the qualifier represents the type of value being exported/imported:
021 * - id
022 * - name
023 * - arn
024 * <p/>
025 * Where stage is the stage of the deployment, such as dev, test, prod, etc. It is optional.
026 * <p/>
027 * scopeName and scopeVersion are analogous to project names and versions.
028 * <p/>
029 * Resources are identified by a namespace, type, and name.
030 * <p/>
031 *
032 * <pre>
033 *   ref:aws:id:project-a:20230101:core:compute:spot
034 *   ref:aws:id:dev:project-a:20230101:core:compute:spot
035 * </pre>
036 */
037public final class Ref {
038    public static Ref ref() {
039        return new Ref();
040    }
041
042    public static Ref idRef() {
043        return new Ref().withQualifier(Qualifier.Id);
044    }
045
046    public static Ref arnRef() {
047        return new Ref().withQualifier(Qualifier.Arn);
048    }
049
050    public static Ref nameRef() {
051        return new Ref().withQualifier(Qualifier.Name);
052    }
053
054    public static boolean isRef(String value) {
055        return value != null && value.startsWith("ref:");
056    }
057
058    public static String provider(String value) {
059        return value != null ? value.split(":")[1] : null;
060    }
061
062    final Fixed provider;
063    final Qualifier qualifier;
064    final Stage stage;
065    final Fixed scope;
066    final Version scopeVersion;
067    final Fixed resourceNs;
068    final Fixed resourceType;
069    final Fixed resourceName;
070
071    public Ref() {
072        provider = Fixed.fixedNull();
073        qualifier = null;
074        stage = Stage.nullStage();
075        scope = Fixed.fixedNull();
076        scopeVersion = Version.versionNull();
077        resourceNs = Fixed.fixedNull();
078        resourceType = Fixed.fixedNull();
079        resourceName = Fixed.fixedNull();
080    }
081
082    private Ref(Fixed provider, Qualifier qualifier, Stage stage, Fixed scope, Version scopeVersion, Fixed resourceNs, Fixed resourceType, Fixed resourceName) {
083        this.provider = provider;
084        this.qualifier = qualifier;
085        this.stage = stage == null ? Stage.nullStage() : stage.asLower();
086        this.scope = scope;
087        this.scopeVersion = scopeVersion;
088        this.resourceNs = resourceNs;
089        this.resourceType = resourceType;
090        this.resourceName = resourceName;
091    }
092
093    public Ref withProvider(String provider) {
094        Objects.requireNonNull(provider);
095        return withProvider(Label.of(provider));
096    }
097
098    public Ref withProvider(Label provider) {
099        Objects.requireNonNull(provider);
100        return withProvider(Fixed.of(provider.lowerHyphen()));
101    }
102
103    public Ref withProvider(Fixed provider) {
104        Label.requireNonEmpty(provider);
105        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
106    }
107
108    public Ref withStage(Stage stage) {
109        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
110    }
111
112    public Ref withScope(String scope) {
113        return withScope(Label.of(scope));
114    }
115
116    public Ref withScope(Label scope) {
117        return withScope(Fixed.of(scope.lowerHyphen()));
118    }
119
120    public Ref withScope(Fixed scope) {
121        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
122    }
123
124    public Ref withScopeVersion(String scopeVersion) {
125        return withScopeVersion(Version.of(scopeVersion));
126    }
127
128    public Ref withScopeVersion(Fixed scopeVersion) {
129        return withScopeVersion(Version.of(scopeVersion.lowerHyphen()));
130    }
131
132    public Ref withScopeVersion(Version scopeVersion) {
133        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
134    }
135
136    public Ref withResourceNs(String resourceNs) {
137        return withResourceNs(Label.of(resourceNs));
138    }
139
140    public Ref withResourceNs(Label resourceNs) {
141        return withResourceNs(Fixed.of(resourceNs.lowerHyphen()));
142    }
143
144    public Ref withResourceNs(Fixed resourceNs) {
145        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
146    }
147
148    public Ref withResourceType(String resourceType) {
149        return withResourceType(Label.of(resourceType));
150    }
151
152    public Ref withResourceType(Label resourceType) {
153        return withResourceType(Fixed.of(resourceType.lowerHyphen()));
154    }
155
156    public Ref withResourceType(Fixed resourceType) {
157        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
158    }
159
160    public Ref withResourceName(String resourceName) {
161        return withResourceName(Label.of(resourceName));
162    }
163
164    public Ref withResourceName(Label resourceName) {
165        return withResourceName(Fixed.of(resourceName.lowerHyphen()));
166    }
167
168    public Ref withResourceName(Fixed resourceName) {
169        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
170    }
171
172    public Ref withQualifier(Qualifier qualifier) {
173        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
174    }
175
176    public Label provider() {
177        return provider;
178    }
179
180    public Stage stage() {
181        return stage;
182    }
183
184    public Fixed scope() {
185        return scope;
186    }
187
188    public Fixed scopeVersion() {
189        return scopeVersion;
190    }
191
192    public Fixed resourceNs() {
193        return resourceNs;
194    }
195
196    public Fixed resourceType() {
197        return resourceType;
198    }
199
200    public Fixed resourceName() {
201        return resourceName;
202    }
203
204    public Qualifier qualifier() {
205        return qualifier;
206    }
207
208    public Label resourceLabel() {
209        requireNonNull(resourceNs, "resourceNs required");
210        requireNonNull(resourceType, "resourceType required");
211        requireNonNull(resourceName, "resourceName required");
212
213        return Label.NULL
214                .with(resourceNs)
215                .with(Label.of(resourceType.value()))
216                .with(resourceName);
217    }
218
219    public Label label() {
220        requireNonNull(provider, "provider required");
221        requireNonNull(qualifier, "qualifier required");
222        requireNonNull(scope, "scope required");
223        requireNonNull(scopeVersion, "scopeVersion required");
224        requireNonNull(resourceNs, "resourceNs required");
225        requireNonNull(resourceType, "resourceType required");
226        requireNonNull(resourceName, "resourceName required");
227
228        return Label.of("ref")
229                .with(provider)
230                .with(qualifier)
231                .with(stage)
232                .with(scope)
233                .with(scopeVersion)
234                .with(resourceNs)
235                .with(resourceType)
236                .with(resourceName);
237    }
238
239    private static void requireNonNull(Label label, String message) {
240        Objects.requireNonNull(label, message);
241
242        if (label.isNull()) {
243            throw new NullPointerException(message);
244        }
245    }
246
247
248    public String exportName() {
249        return label().lowerColonPath();
250    }
251
252    @Override
253    public String toString() {
254        return Label.of("ref")
255                .with(provider)
256                .with(qualifier)
257                .with(stage)
258                .with(scope)
259                .with(scopeVersion)
260                .with(resourceNs)
261                .with(resourceType)
262                .with(resourceName).lowerColonPath();
263    }
264
265    @Override
266    public boolean equals(Object o) {
267        if (this == o) return true;
268        if (o == null || getClass() != o.getClass()) return false;
269        Ref ref = (Ref) o;
270        return Objects.equals(provider, ref.provider) && Objects.equals(stage, ref.stage) && Objects.equals(scope, ref.scope) && Objects.equals(scopeVersion, ref.scopeVersion) && Objects.equals(resourceNs, ref.resourceNs) && Objects.equals(resourceType, ref.resourceType) && Objects.equals(resourceName, ref.resourceName) && qualifier == ref.qualifier;
271    }
272
273    @Override
274    public int hashCode() {
275        return Objects.hash(provider, stage, scope, scopeVersion, resourceNs, resourceType, resourceName, qualifier);
276    }
277
278    public enum Qualifier implements Label.EnumLabel {
279        Name,
280        Id,
281        Arn;
282    }
283}