001/*
002 * Copyright 2022-2026 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet;
018
019import org.jspecify.annotations.NonNull;
020import org.jspecify.annotations.Nullable;
021
022import javax.annotation.concurrent.ThreadSafe;
023
024import static java.util.Objects.requireNonNull;
025
026/**
027 * Immutable outcome of localizing one MCP presentation field.
028 *
029 * <p>
030 * This is a closed domain; exhaustive switches are supported, and any new alternative requires an explicit reviewed contract amendment.
031 *
032 * @author <a href="https://www.revetkn.com">Mark Allen</a>
033 */
034@ThreadSafe
035public sealed interface McpLocalizationResult
036                permits McpLocalizationResult.Localized,
037                McpLocalizationResult.UseDefaultText,
038                McpLocalizationResult.Failure {
039        /**
040         * Creates text successfully resolved for the localization context. The
041         * provider owns locale matching and fallback, so the text may come from the
042         * selected locale, a parent locale, or a localization-library fallback.
043         *
044         * @param text localized nonblank text
045         * @return localized result
046         * @throws NullPointerException if {@code text} is null
047         * @throws IllegalArgumentException if {@code text} is blank
048         */
049        @NonNull
050        static Localized localized(@NonNull String text) {
051                return new Localized(text);
052        }
053
054        /** @return result requesting the canonical default field */
055        @NonNull
056        static UseDefaultText useDefaultText() {
057                return UseDefaultText.INSTANCE;
058        }
059
060        /**
061         * Creates the data-free outcome for an unexpected localization lookup
062         * failure. This outcome applies the localizer's whole-response failure
063         * policy; it is not an ordinary missing-translation result.
064         *
065         * @return failure result
066         */
067        @NonNull
068        static Failure failure() {
069                return Failure.INSTANCE;
070        }
071
072        /**
073         * Text successfully resolved for the localization context. Resolution may
074         * use the selected locale, a parent locale, or a localization-library
075         * fallback; Soklet does not require or retain per-field resolution
076         * provenance. The result carries localized nonblank text.
077         *
078         * @author <a href="https://www.revetkn.com">Mark Allen</a>
079         */
080        @ThreadSafe
081        public final class Localized implements McpLocalizationResult {
082                @NonNull
083                private final String text;
084
085                /**
086                 * Validates the localized text.
087                 *
088                 * @param text localized nonblank text
089                 */
090                private Localized(@NonNull String text) {
091                        this.text = McpContentValueSupport.requireWellFormedString(text);
092                        if (text.isBlank())
093                                throw new IllegalArgumentException(
094                                                "Localized MCP text must not be blank.");
095                }
096
097                /** @return localized nonblank text */
098                @NonNull
099                public String getText() {
100                        return this.text;
101                }
102
103                /** @return whether this result contains the same localized text */
104                @Override
105                public boolean equals(@Nullable Object other) {
106                        if (this == other)
107                                return true;
108                        if (!(other instanceof Localized localized))
109                                return false;
110                        return this.text.equals(localized.text);
111                }
112
113                /** @return value-based hash code */
114                @Override
115                public int hashCode() {
116                        return this.text.hashCode();
117                }
118
119                /** @return redacted diagnostic rendering */
120                @Override
121                @NonNull
122                public final String toString() {
123                        return "Localized{text=<redacted>}";
124                }
125        }
126
127        /**
128         * Requests publication of the canonical default text for this field.
129         *
130         * @author <a href="https://www.revetkn.com">Mark Allen</a>
131         */
132        @ThreadSafe
133        public final class UseDefaultText implements McpLocalizationResult {
134                @NonNull
135                private static final UseDefaultText INSTANCE = new UseDefaultText();
136
137                private UseDefaultText() {
138                }
139
140                /** @return whether the other value also requests default text */
141                @Override
142                public boolean equals(@Nullable Object other) {
143                        return other instanceof UseDefaultText;
144                }
145
146                /** @return value-based hash code */
147                @Override
148                public int hashCode() {
149                        return 0;
150                }
151
152                /** @return safe diagnostic rendering */
153                @Override
154                @NonNull
155                public String toString() {
156                        return "UseDefaultText{}";
157                }
158        }
159
160        /**
161         * Reports an unexpected lookup failure without carrying a throwable,
162         * message, key, coordinate, locale, revision, or other diagnostic data.
163         *
164         * @author <a href="https://www.revetkn.com">Mark Allen</a>
165         */
166        @ThreadSafe
167        public final class Failure implements McpLocalizationResult {
168                @NonNull
169                private static final Failure INSTANCE = new Failure();
170
171                private Failure() {
172                }
173
174                /** @return whether the other value is also a localization failure */
175                @Override
176                public boolean equals(@Nullable Object other) {
177                        return other instanceof Failure;
178                }
179
180                /** @return value-based hash code */
181                @Override
182                public int hashCode() {
183                        return 0;
184                }
185
186                /** @return safe diagnostic rendering */
187                @Override
188                @NonNull
189                public String toString() {
190                        return "Failure{}";
191                }
192        }
193}