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 Label provider;
063    final Qualifier qualifier;
064    final Stage stage;
065    final Label scope;
066    final Fixed scopeVersion;
067    final Label resourceNs;
068    final Fixed resourceType;
069    final Label resourceName;
070
071    public Ref() {
072        provider = null;
073        stage = Stage.nullStage();
074        scope = null;
075        scopeVersion = null;
076        resourceNs = null;
077        resourceType = null;
078        resourceName = null;
079        qualifier = null;
080    }
081
082    private Ref(Label provider, Qualifier qualifier, Stage stage, Label scope, Fixed scopeVersion, Label resourceNs, Fixed resourceType, Label 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        Label.requireNonEmpty(provider);
100        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
101    }
102
103    public Ref withStage(Stage stage) {
104        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
105    }
106
107    public Ref withScope(String scope) {
108        return withScope(Label.of(scope));
109    }
110
111    public Ref withScope(Label scope) {
112        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
113    }
114
115    public Ref withScopeVersion(String scopeVersion) {
116        return withScopeVersion(Fixed.of(scopeVersion));
117    }
118
119    public Ref withScopeVersion(Fixed scopeVersion) {
120        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
121    }
122
123    public Ref withResourceNs(String resourceNs) {
124        return withResourceNs(Label.of(resourceNs));
125    }
126
127    public Ref withResourceNs(Label resourceNs) {
128        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
129    }
130
131    public Ref withResourceType(String resourceType) {
132        return withResourceType(Fixed.of(resourceType));
133    }
134
135    public Ref withResourceType(Fixed resourceType) {
136        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
137    }
138
139    public Ref withResourceName(String resourceName) {
140        return withResourceName(Label.of(resourceName));
141    }
142
143    public Ref withResourceName(Label resourceName) {
144        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
145    }
146
147    public Ref withQualifier(Qualifier qualifier) {
148        return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName);
149    }
150
151    public Label provider() {
152        return provider;
153    }
154
155    public Stage stage() {
156        return stage;
157    }
158
159    public Label scope() {
160        return scope;
161    }
162
163    public Fixed scopeVersion() {
164        return scopeVersion;
165    }
166
167    public Label resourceNs() {
168        return resourceNs;
169    }
170
171    public Fixed resourceType() {
172        return resourceType;
173    }
174
175    public Label resourceName() {
176        return resourceName;
177    }
178
179    public Qualifier qualifier() {
180        return qualifier;
181    }
182
183    public Label resourceLabel() {
184        requireNonNull(resourceNs, "resourceNs required");
185        requireNonNull(resourceType, "resourceType required");
186        requireNonNull(resourceName, "resourceName required");
187
188        return Label.NULL
189                .with(resourceNs)
190                .with(Label.of(resourceType.value()))
191                .with(resourceName);
192    }
193
194    public Label label() {
195        requireNonNull(provider, "provider required");
196        requireNonNull(qualifier, "qualifier required");
197        requireNonNull(scope, "scope required");
198        requireNonNull(scopeVersion, "scopeVersion required");
199        requireNonNull(resourceNs, "resourceNs required");
200        requireNonNull(resourceType, "resourceType required");
201        requireNonNull(resourceName, "resourceName required");
202
203        return Label.of("ref")
204                .with(provider)
205                .with(qualifier)
206                .with(stage)
207                .with(scope)
208                .with(scopeVersion)
209                .with(resourceNs)
210                .with(Label.of(resourceType.value()))
211                .with(resourceName);
212    }
213
214    private static void requireNonNull(Label label, String message) {
215        Objects.requireNonNull(label, message);
216
217        if (label.isNull()) {
218            throw new NullPointerException(message);
219        }
220    }
221
222
223    public String exportName() {
224        return label().lowerColonPath();
225    }
226
227    @Override
228    public String toString() {
229        return exportName();
230    }
231
232    @Override
233    public boolean equals(Object o) {
234        if (this == o) return true;
235        if (o == null || getClass() != o.getClass()) return false;
236        Ref ref = (Ref) o;
237        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;
238    }
239
240    @Override
241    public int hashCode() {
242        return Objects.hash(provider, stage, scope, scopeVersion, resourceNs, resourceType, resourceName, qualifier);
243    }
244
245    public enum Qualifier implements Label.EnumLabel {
246        Name,
247        Id,
248        Arn;
249    }
250}