···11+@rem
22+@rem Copyright 2015 the original author or authors.
33+@rem
44+@rem Licensed under the Apache License, Version 2.0 (the "License");
55+@rem you may not use this file except in compliance with the License.
66+@rem You may obtain a copy of the License at
77+@rem
88+@rem https://www.apache.org/licenses/LICENSE-2.0
99+@rem
1010+@rem Unless required by applicable law or agreed to in writing, software
1111+@rem distributed under the License is distributed on an "AS IS" BASIS,
1212+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313+@rem See the License for the specific language governing permissions and
1414+@rem limitations under the License.
1515+@rem
1616+@rem SPDX-License-Identifier: Apache-2.0
1717+@rem
1818+1919+@if "%DEBUG%"=="" @echo off
2020+@rem ##########################################################################
2121+@rem
2222+@rem Gradle startup script for Windows
2323+@rem
2424+@rem ##########################################################################
2525+2626+@rem Set local scope for the variables with windows NT shell
2727+if "%OS%"=="Windows_NT" setlocal
2828+2929+set DIRNAME=%~dp0
3030+if "%DIRNAME%"=="" set DIRNAME=.
3131+@rem This is normally unused
3232+set APP_BASE_NAME=%~n0
3333+set APP_HOME=%DIRNAME%
3434+3535+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
3636+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
3737+3838+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
3939+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
4040+4141+@rem Find java.exe
4242+if defined JAVA_HOME goto findJavaFromJavaHome
4343+4444+set JAVA_EXE=java.exe
4545+%JAVA_EXE% -version >NUL 2>&1
4646+if %ERRORLEVEL% equ 0 goto execute
4747+4848+echo. 1>&2
4949+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
5050+echo. 1>&2
5151+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
5252+echo location of your Java installation. 1>&2
5353+5454+goto fail
5555+5656+:findJavaFromJavaHome
5757+set JAVA_HOME=%JAVA_HOME:"=%
5858+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
5959+6060+if exist "%JAVA_EXE%" goto execute
6161+6262+echo. 1>&2
6363+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
6464+echo. 1>&2
6565+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
6666+echo location of your Java installation. 1>&2
6767+6868+goto fail
6969+7070+:execute
7171+@rem Setup the command line
7272+7373+7474+7575+@rem Execute Gradle
7676+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
7777+7878+:end
7979+@rem End local scope for the variables with windows NT shell
8080+if %ERRORLEVEL% equ 0 goto mainEnd
8181+8282+:fail
8383+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
8484+rem the _cmd.exe /c_ return code!
8585+set EXIT_CODE=%ERRORLEVEL%
8686+if %EXIT_CODE% equ 0 set EXIT_CODE=1
8787+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
8888+exit /b %EXIT_CODE%
8989+9090+:mainEnd
9191+if "%OS%"=="Windows_NT" endlocal
9292+9393+:omega
+21
license.txt
···11+MIT License
22+33+Copyright (c) 2026 nayrid
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+21
license_header.txt
···11+This file is part of registry, licensed under the MIT License.
22+33+Copyright (c) 2026 nayrid
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry;
2525+2626+import com.nayrid.common.Validate;
2727+import com.nayrid.describe.RecordStyleFormatter;
2828+import com.nayrid.registry.entry.RegistryEntry;
2929+import org.jetbrains.annotations.Contract;
3030+import org.jetbrains.annotations.UnmodifiableView;
3131+import org.jspecify.annotations.Nullable;
3232+3333+import java.util.Collections;
3434+import java.util.HashMap;
3535+import java.util.Map;
3636+import java.util.Set;
3737+3838+/**
3939+ * An abstract base class for registry implementations.
4040+ *
4141+ * @param <K> the type of keys
4242+ * @param <V> the type of values
4343+ * @since 1.0.0
4444+ */
4545+public abstract class AbstractRegistry<K, V> implements Registry<K, V> {
4646+4747+ protected final K registryKey;
4848+4949+ protected final Map<K, RegistryEntry<K, V>> byKey = new HashMap<>();
5050+ protected final @UnmodifiableView Set<K> keySet = Collections.unmodifiableSet(this.byKey.keySet());
5151+ protected final Map<V, RegistryEntry<K, V>> byValue = new HashMap<>();
5252+5353+ private static <V> String valueAlreadyRegistered(final V value) {
5454+ return "Value '" + value + "' already registered";
5555+ }
5656+5757+ private static <K> String keyAlreadyRegistered(final K key) {
5858+ return "Key '" + key + "' already registered";
5959+ }
6060+6161+ /**
6262+ * Creates a new registry with the given key.
6363+ *
6464+ * @param registryKey the registry key
6565+ * @throws NullPointerException if {@code registryKey} is {@code null}
6666+ * @since 1.0.0
6767+ */
6868+ @Contract("null -> fail")
6969+ protected AbstractRegistry(final K registryKey) {
7070+ this.registryKey = Validate.nonNull(registryKey, "registryKey");
7171+ }
7272+7373+ @Override
7474+ public RegistryEntry<K, V> register(final K key, final V value) {
7575+ Validate.nonNull(key, "key");
7676+ Validate.nonNull(value, "value");
7777+ final RegistryEntry<K, V> existingEntry = this.byKey.get(key);
7878+ if (existingEntry != null) {
7979+ if (existingEntry instanceof RegistryEntry.Delayed<K, V> delayedEntry) {
8080+ if (this.byValue.containsKey(value)) {
8181+ throw new IllegalStateException(valueAlreadyRegistered(value));
8282+ }
8383+ if (delayedEntry.hasValue()) {
8484+ throw new IllegalStateException(keyAlreadyRegistered(key));
8585+ }
8686+ if (delayedEntry.set(value)) {
8787+ this.byValue.put(value, delayedEntry);
8888+ return delayedEntry;
8989+ } else {
9090+ throw new RuntimeException("Failed to set value on '" + key + "'");
9191+ }
9292+ } else {
9393+ throw new IllegalStateException(keyAlreadyRegistered(key));
9494+ }
9595+ }
9696+9797+ if (this.byValue.containsKey(value)) {
9898+ throw new IllegalStateException(valueAlreadyRegistered(value));
9999+ }
100100+101101+ final RegistryEntry<K, V> newEntry = RegistryEntry.immediate(key, value);
102102+ this.byKey.put(key, newEntry);
103103+ this.byValue.put(value, newEntry);
104104+ return newEntry;
105105+ }
106106+107107+ @Override
108108+ public @Nullable RegistryEntry<K, V> get(final @Nullable K key) {
109109+ if (key == null) {
110110+ return null;
111111+ }
112112+ return this.byKey.get(key);
113113+ }
114114+115115+ @Override
116116+ public RegistryEntry<K, V> getOrCreate(final K key) {
117117+ if (this.byKey.containsKey(key)) {
118118+ return this.byKey.get(key);
119119+ } else {
120120+ final RegistryEntry.Delayed<K, V> entry = RegistryEntry.delayed(key);
121121+ this.byKey.put(key, entry);
122122+ return entry;
123123+ }
124124+ }
125125+126126+ @Override
127127+ public Set<K> keySet() {
128128+ return this.keySet;
129129+ }
130130+131131+ @Override
132132+ public final K registryKey() {
133133+ return this.registryKey;
134134+ }
135135+136136+ @Override
137137+ public String toString() {
138138+ return RecordStyleFormatter.noDepthLimit().format(this);
139139+ }
140140+141141+ @Override
142142+ public boolean equals(final Object object) {
143143+ return this == object ||
144144+ (object instanceof final Registry<?, ?> that) &&
145145+ this.registryKey().equals(that.registryKey());
146146+ }
147147+148148+ @Override
149149+ public int hashCode() {
150150+ return 31 * this.registryKey().hashCode();
151151+ }
152152+153153+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry;
2525+2626+2727+import com.nayrid.describe.Describable;
2828+import com.nayrid.describe.Descriptor;
2929+import com.nayrid.registry.entry.RegistryEntry;
3030+import org.jetbrains.annotations.Contract;
3131+import org.jspecify.annotations.Nullable;
3232+3333+import java.util.NoSuchElementException;
3434+import java.util.Optional;
3535+import java.util.Set;
3636+import java.util.function.Supplier;
3737+3838+/**
3939+ * A simple, immutable mapping from keys to values.
4040+ *
4141+ * @param <K> the type of keys
4242+ * @param <V> the type of values
4343+ * @since 1.0.0
4444+ */
4545+public interface Registry<K, V> extends Describable {
4646+4747+ /**
4848+ * Registers a new entry with the given key and value.
4949+ *
5050+ * <p>If the entry was created with {@link #getOrCreate(Object)}, this will set the
5151+ * value if it has not already been set.</p>
5252+ *
5353+ * @param key the lookup key
5454+ * @param value the value to register
5555+ * @return the entry
5656+ * @throws NullPointerException if the key or value is {@code null}
5757+ * @throws IllegalStateException if the key or value are already registered
5858+ * @since 1.0.0
5959+ */
6060+ @Contract(value = "null, _ -> fail; _, null -> fail", mutates = "this")
6161+ RegistryEntry<K, V> register(K key, V value);
6262+6363+ /**
6464+ * Gets the entry associated with the given key, or {@code null} if none exists.
6565+ *
6666+ * @param key the lookup key
6767+ * @return the matching entry, or {@code null} if not found
6868+ * @since 1.0.0
6969+ */
7070+ @Nullable RegistryEntry<K, V> get(@Nullable K key);
7171+7272+ /**
7373+ * Gets or creates a new entry for the given key.
7474+ *
7575+ * @param key the lookup key
7676+ * @return the matching entry
7777+ * @throws NullPointerException if the key is {@code null}
7878+ * @since 1.0.0
7979+ */
8080+ @SuppressWarnings("checkstyle:MethodName")
8181+ RegistryEntry<K, V> getOrCreate(K key);
8282+8383+ /**
8484+ * Gets an {@link Optional} containing the entry for the given key, or an empty Optional.
8585+ *
8686+ * @param key the lookup key
8787+ * @return an Optional of the matching entry
8888+ * @since 1.0.0
8989+ */
9090+ @SuppressWarnings("checkstyle:MethodName")
9191+ default Optional<RegistryEntry<K, V>> getOptionally(final K key) {
9292+ return Optional.ofNullable(this.get(key));
9393+ }
9494+9595+ /**
9696+ * Gets the entry for the given key, or throws if not present.
9797+ *
9898+ * @param key the lookup key
9999+ * @param exceptionMessageSupplier supplier for the exception message
100100+ * @return the matching entry
101101+ * @throws NoSuchElementException if no entry is found
102102+ * @since 1.0.0
103103+ */
104104+ @SuppressWarnings("checkstyle:MethodName")
105105+ default RegistryEntry<K, V> getOrThrow(
106106+ final K key,
107107+ final Supplier<String> exceptionMessageSupplier
108108+ ) {
109109+ final RegistryEntry<K, V> entry = this.get(key);
110110+ if (entry == null) {
111111+ throw new NoSuchElementException(exceptionMessageSupplier.get());
112112+ }
113113+ return entry;
114114+ }
115115+116116+ /**
117117+ * Gets the entry for the given key, or throws with a default message.
118118+ *
119119+ * @param key the lookup key
120120+ * @return the matching entry
121121+ * @throws NoSuchElementException if no entry is found
122122+ * @since 1.0.0
123123+ */
124124+ @SuppressWarnings("checkstyle:MethodName")
125125+ default RegistryEntry<K, V> getOrThrow(final K key) {
126126+ return this.getOrThrow(
127127+ key,
128128+ () -> "No entry found for key '%s' in registry '%s'".formatted(key, this.registryKey())
129129+ );
130130+ }
131131+132132+ /**
133133+ * Gets an unmodifiable {@link Set} of all registry keys.
134134+ *
135135+ * @return the set of keys
136136+ * @since 1.0.0
137137+ */
138138+ Set<K> keySet();
139139+140140+ /**
141141+ * Gets this registry's identifier.
142142+ *
143143+ * @return the registry key
144144+ * @since 1.0.0
145145+ */
146146+ K registryKey();
147147+148148+ @Override
149149+ default Descriptor describe() {
150150+ return Descriptor.of(this)
151151+ .field("registryKey", this.registryKey())
152152+ .field("size", this.keySet().size())
153153+ .build();
154154+ }
155155+156156+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.entry;
2525+2626+import com.nayrid.common.Validate;
2727+import com.nayrid.describe.RecordStyleFormatter;
2828+import org.jspecify.annotations.Nullable;
2929+3030+import java.util.Objects;
3131+3232+final class DelayedEntryImpl<K, V> implements RegistryEntry.Delayed<K, V> {
3333+3434+ private final K key;
3535+3636+ private @Nullable V value;
3737+3838+ DelayedEntryImpl(final K key) {
3939+ this.key = Validate.nonNull(key, "key");
4040+ }
4141+4242+ @Override
4343+ public K key() {
4444+ return this.key;
4545+ }
4646+4747+ @Override
4848+ public @Nullable V value() {
4949+ return this.value;
5050+ }
5151+5252+ @Override
5353+ public boolean set(final @Nullable V value) {
5454+ if (this.value == null && value != null) {
5555+ this.value = value;
5656+ return true;
5757+ } else {
5858+ return false;
5959+ }
6060+ }
6161+6262+ @Override
6363+ public boolean equals(final Object o) {
6464+ if (o == null || getClass() != o.getClass())
6565+ return false;
6666+ final DelayedEntryImpl<?, ?> that = (DelayedEntryImpl<?, ?>) o;
6767+ return Objects.equals(this.key, that.key) && Objects.equals(this.value, that.value);
6868+ }
6969+7070+ @Override
7171+ public int hashCode() {
7272+ return Objects.hash(this.key, this.value);
7373+ }
7474+7575+ @Override
7676+ public String toString() {
7777+ return RecordStyleFormatter.noDepthLimit().format(this.describe());
7878+ }
7979+8080+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.entry;
2525+2626+import com.nayrid.common.Validate;
2727+import com.nayrid.describe.RecordStyleFormatter;
2828+2929+record ImmediateEntryImpl<K, V>(K key, V value) implements RegistryEntry.Immediate<K, V> {
3030+3131+ ImmediateEntryImpl(final K key, final V value) {
3232+ this.key = Validate.nonNull(key, "key");
3333+ this.value = Validate.nonNull(value, "value");
3434+ }
3535+3636+ @Override
3737+ public String toString() {
3838+ return RecordStyleFormatter.noDepthLimit().format(this.describe());
3939+ }
4040+4141+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.entry;
2525+2626+import com.nayrid.common.Validate;
2727+import com.nayrid.describe.Describable;
2828+import com.nayrid.describe.Descriptor;
2929+import com.nayrid.registry.Registry;
3030+import org.jetbrains.annotations.ApiStatus;
3131+import org.jetbrains.annotations.Contract;
3232+import org.jspecify.annotations.Nullable;
3333+3434+import java.util.Optional;
3535+import java.util.function.Consumer;
3636+import java.util.function.Function;
3737+3838+/**
3939+ * A key-value pair within a {@link Registry}.
4040+ *
4141+ * @param <K> the type of keys
4242+ * @param <V> the type of values
4343+ * @since 1.0.0
4444+ */
4545+public sealed interface RegistryEntry<K, V> extends Describable permits RegistryEntry.Delayed,
4646+ RegistryEntry.Immediate {
4747+4848+ /**
4949+ * Creates a {@link Delayed} entry with the given key. The value is not set at this time.
5050+ *
5151+ * @param key the key to set
5252+ * @param <K> the type of keys
5353+ * @param <V> the type of values
5454+ * @return a new {@link Delayed} entry
5555+ * @throws NullPointerException if the key is {@code null}
5656+ * @since 1.0.0
5757+ */
5858+ @Contract("null -> fail; _ -> new")
5959+ static <K, V> Delayed<K, V> delayed(final K key) {
6060+ return new DelayedEntryImpl<>(key);
6161+ }
6262+6363+ /**
6464+ * Creates an {@link Immediate} entry with the given key and value.
6565+ *
6666+ * @param key the key to set
6767+ * @param value the value to set
6868+ * @param <K> the type of keys
6969+ * @param <V> the type of values
7070+ * @return a new {@link Immediate} entry
7171+ * @throws NullPointerException if the key or value are {@code null}
7272+ * @since 1.0.0
7373+ */
7474+ @Contract("null, _ -> fail; _, null -> fail; _, _ -> new")
7575+ static <K, V> RegistryEntry<K, V> immediate(final K key, final V value) {
7676+ return new ImmediateEntryImpl<>(key, value);
7777+ }
7878+7979+ /**
8080+ * Gets this entry's key.
8181+ *
8282+ * @return the entry key
8383+ * @since 1.0.0
8484+ */
8585+ K key();
8686+8787+ /**
8888+ * Gets this entry's value, or {@code null} if unset.
8989+ *
9090+ * @return the entry value, or {@code null}
9191+ * @since 1.0.0
9292+ */
9393+ @Nullable V value();
9494+9595+ /**
9696+ * Gets this entry's value wrapped in an {@link Optional}.
9797+ *
9898+ * @return an Optional containing the value, or empty if unset
9999+ * @since 1.0.0
100100+ */
101101+ default Optional<V> valueOptionally() {
102102+ return Optional.ofNullable(this.value());
103103+ }
104104+105105+ /**
106106+ * Gets this entry's value, or throws a {@link NullPointerException} with a message supplied
107107+ * by the given function if unset.
108108+ *
109109+ * @param messageFn function that generates the exception message using the key
110110+ * @return the entry value
111111+ * @throws NullPointerException if the value is {@code null} or if the {@code messageFn} is {@code null}
112112+ * @since 1.0.0
113113+ */
114114+ @Contract
115115+ default V valueOrThrow(final Function<K, String> messageFn) {
116116+ Validate.nonNull(messageFn, "messageFn");
117117+ final V value = this.value();
118118+ if (value == null) {
119119+ throw new NullPointerException(messageFn.apply(this.key()));
120120+ }
121121+ return value;
122122+ }
123123+124124+ /**
125125+ * Gets this entry's value, or throws a {@link NullPointerException}.
126126+ *
127127+ * @return the entry value
128128+ * @throws NullPointerException if the value is {@code null}
129129+ * @since 1.0.0
130130+ */
131131+ default V valueOrThrow() {
132132+ return this.valueOrThrow(key -> String.format("No value present for key '%s'", key));
133133+ }
134134+135135+ /**
136136+ * Gets if this entry has a non-null value.
137137+ *
138138+ * @return {@code true} if value is present, {@code false} otherwise
139139+ * @since 1.0.0
140140+ */
141141+ default boolean hasValue() {
142142+ return this.value() != null;
143143+ }
144144+145145+ /**
146146+ * If a value is present, invokes the given consumer with the value.
147147+ *
148148+ * @param consumer the function to call with the value
149149+ * @throws NullPointerException if {@code consumer} is {@code null}
150150+ * @since 1.0.0
151151+ */
152152+ default void ifPresent(final Consumer<V> consumer) {
153153+ Validate.nonNull(consumer, "consumer");
154154+ final V value = this.value();
155155+ if (value != null) {
156156+ consumer.accept(value);
157157+ }
158158+ }
159159+160160+ @Override
161161+ default Descriptor describe() {
162162+ return Descriptor.of(this)
163163+ .field("key", this.key())
164164+ .field("value", this.value())
165165+ .build();
166166+ }
167167+168168+ /**
169169+ * An entry whose value is not immediately available and may be set later, but once set, it will
170170+ * never be {@code null}.
171171+ *
172172+ * @param <K> the type of keys
173173+ * @param <V> the type of values
174174+ * @since 1.0.0
175175+ */
176176+ sealed interface Delayed<K, V> extends RegistryEntry<K, V> permits DelayedEntryImpl {
177177+178178+ /**
179179+ * Attempts to set this entry's value. If the value is already set, this method will
180180+ * return {@code false}.
181181+ *
182182+ * @param value the new value to set
183183+ * @return {@code true} if the value was set, {@code false} otherwise
184184+ * @since 1.0.0
185185+ */
186186+ @ApiStatus.Internal
187187+ boolean set(final V value);
188188+189189+ @Override
190190+ default Descriptor describe() {
191191+ return Descriptor.of(this, "RegistryEntry.Delayed")
192192+ .merge(RegistryEntry.super.describe())
193193+ .build();
194194+ }
195195+196196+ }
197197+198198+ /**
199199+ * An entry whose {@link #value()} is immediately available and never null.
200200+ *
201201+ * @param <K> the type of keys
202202+ * @param <V> the type of values
203203+ * @since 1.0.0
204204+ */
205205+ sealed interface Immediate<K, V> extends RegistryEntry<K, V> permits ImmediateEntryImpl {
206206+207207+ @Override
208208+ default Descriptor describe() {
209209+ return Descriptor.of(this, "RegistryEntry.Immediate")
210210+ .merge(RegistryEntry.super.describe())
211211+ .build();
212212+ }
213213+214214+ }
215215+216216+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.DataResult;
2828+import com.mojang.serialization.DynamicOps;
2929+import com.mojang.serialization.Keyable;
3030+import com.mojang.serialization.Lifecycle;
3131+import com.nayrid.common.Validate;
3232+import com.nayrid.registry.AbstractRegistry;
3333+import com.nayrid.registry.entry.RegistryEntry;
3434+import org.jetbrains.annotations.Contract;
3535+import org.jspecify.annotations.Nullable;
3636+3737+import java.util.Optional;
3838+import java.util.function.Function;
3939+import java.util.stream.Stream;
4040+4141+/**
4242+ * An abstract base class for {@link Keyable} registry implementations.
4343+ *
4444+ * @param <K> the type of keys
4545+ * @param <V> the type of values
4646+ * @since 1.0.0
4747+ */
4848+public abstract class AbstractKeyableRegistry<K, V> extends AbstractRegistry<K, V> implements KeyableRegistry<K, V> {
4949+5050+ private final Lifecycle lifecycle;
5151+ private final Codec<K> keyCodec;
5252+ private final Function<K, String> keyToStringFn;
5353+5454+ /**
5555+ * Creates a new registry with the given key.
5656+ *
5757+ * @param registryKey the registry key
5858+ * @param lifecycle the lifecycle
5959+ * @param keyCodec the key's Codec
6060+ * @param keyToStringFn function to convert key type to String
6161+ * @throws NullPointerException if any argument is {@code null}
6262+ * @since 1.0.0
6363+ */
6464+ @Contract("null, _, _, _ -> fail")
6565+ protected AbstractKeyableRegistry(final K registryKey,
6666+ final Lifecycle lifecycle,
6767+ final Codec<K> keyCodec, final Function<K, String> keyToStringFn) {
6868+ super(registryKey);
6969+ this.lifecycle = Validate.nonNull(lifecycle, "lifecycle");
7070+ this.keyCodec = Validate.nonNull(keyCodec, "keyCodec");
7171+ this.keyToStringFn = Validate.nonNull(keyToStringFn, "keyToStringFn");
7272+ }
7373+7474+ @SuppressWarnings({"NullableProblems", "DataFlowIssue"}) // false positive afaict
7575+ @Override
7676+ public Codec<V> codec() {
7777+ return this.entryCodec().flatComapMap(RegistryEntry::value, this::dataResultEntryByValue);
7878+ }
7979+8080+ @Override
8181+ public Codec<RegistryEntry<K, V>> entryCodec() {
8282+ return this.keyCodec.comapFlatMap(key -> this.getOptionally(key).map(DataResult::success)
8383+ .orElseGet(() -> DataResult.error(() -> "Unknown key in " + this + ": " + key)),
8484+ RegistryEntry::key).withLifecycle(this.lifecycle);
8585+ }
8686+8787+ @SuppressWarnings("NullableProblems") // false positive afaict
8888+ private DataResult<RegistryEntry<K, V>> dataResultEntryByValue(final V value) {
8989+ return Optional.ofNullable(this.get(this.key(value))).map(DataResult::success).orElseGet(() -> DataResult.error(() -> "Unknown value in " + this + ": " + value));
9090+ }
9191+9292+ private @Nullable K key(final @Nullable V value) {
9393+ final var entry = this.byValue.get(value);
9494+ if (entry == null) {
9595+ return null;
9696+ }
9797+ return entry.key();
9898+ }
9999+100100+ @Override
101101+ public <T> Stream<T> keys(final DynamicOps<T> ops) {
102102+ return this.keySet().stream().map(k ->
103103+ ops.createString(this.keyToStringFn.apply(k))
104104+ );
105105+ }
106106+107107+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.Keyable;
2828+import com.nayrid.registry.Registry;
2929+import com.nayrid.registry.entry.RegistryEntry;
3030+3131+/**
3232+ * A registry that is compatible with DataFixerUpper.
3333+ *
3434+ * @param <K> the type of keys
3535+ * @param <V> the type of values
3636+ * @since 1.0.0
3737+ */
3838+public interface KeyableRegistry<K, V> extends Registry<K, V>, Keyable {
3939+4040+ /**
4141+ * Returns the codec for serializing {@code V}.
4242+ *
4343+ * @return the codec for serializing {@code V}.
4444+ * @since 1.0.0
4545+ */
4646+ Codec<V> codec();
4747+4848+ /**
4949+ * Returns the codec for serializing the registry entry of {@code V}.
5050+ *
5151+ * @return the codec for serializing the registry entry of {@code V}.
5252+ * @since 1.0.0
5353+ */
5454+ Codec<RegistryEntry<K, V>> entryCodec();
5555+5656+}
···11+/**
22+ * Registries with support for DataFixerUpper and {@link com.mojang.serialization.Codec#dispatch(java.util.function.Function, java.util.function.Function) Codec#dispatch}.
33+ */
44+@NullMarked
55+package com.nayrid.registry.dfu;
66+77+import org.jspecify.annotations.NullMarked;
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu;
2525+2626+import com.google.gson.JsonElement;
2727+import com.mojang.serialization.DataResult;
2828+import com.mojang.serialization.JsonOps;
2929+import com.nayrid.registry.dfu.bean.Bean;
3030+import com.nayrid.registry.dfu.bean.BeanTypes;
3131+import com.nayrid.registry.dfu.bean.CountingBean;
3232+import com.nayrid.registry.dfu.bean.StringyBean;
3333+import org.junit.jupiter.api.Assertions;
3434+import org.junit.jupiter.api.Test;
3535+3636+import static org.junit.jupiter.api.Assertions.assertEquals;
3737+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
3838+3939+public final class CodecRegistryTests {
4040+4141+ @Test
4242+ void testCountingBean() {
4343+ final CountingBean countingBean = new CountingBean(123);
4444+4545+ final DataResult<JsonElement> result = Bean.CODEC.encodeStart(JsonOps.INSTANCE, countingBean);
4646+ final JsonElement element = result.getOrThrow();
4747+4848+ assertEquals("{\"counting_number\":123,\"type\":\"counting_bean\"}", element.toString());
4949+5050+ final Bean decodedBean = Bean.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow();
5151+5252+ Assertions.assertEquals(BeanTypes.COUNTING_BEAN, decodedBean.type());
5353+ assertInstanceOf(CountingBean.class, decodedBean);
5454+ final CountingBean castedBean = (CountingBean) decodedBean;
5555+ assertEquals(countingBean.countingNumber(), castedBean.countingNumber());
5656+ }
5757+5858+ @Test
5959+ void testStringyBean() {
6060+ final StringyBean stringyBean = new StringyBean("Hello World");
6161+6262+ final DataResult<JsonElement> result = Bean.CODEC.encodeStart(JsonOps.INSTANCE, stringyBean);
6363+ final JsonElement element = result.getOrThrow();
6464+6565+ assertEquals("{\"stringy_string\":\"Hello World\",\"type\":\"stringy_bean\"}", element.toString());
6666+6767+ final Bean decodedBean = Bean.CODEC.parse(JsonOps.INSTANCE, element).getOrThrow();
6868+6969+ Assertions.assertEquals(BeanTypes.STRINGY_BEAN, decodedBean.type());
7070+ assertInstanceOf(StringyBean.class, decodedBean);
7171+ final StringyBean castedBean = (StringyBean) decodedBean;
7272+ assertEquals(stringyBean.stringyString(), castedBean.stringyString());
7373+ }
7474+7575+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.Lifecycle;
2828+2929+import java.util.function.Function;
3030+3131+public final class KeyableRegistryImpl<K, V> extends AbstractKeyableRegistry<K, V> {
3232+3333+ public KeyableRegistryImpl(final K registryKey, final Lifecycle lifecycle, final Codec<K> keyCodec, final Function<K, String> keyToStringFn) {
3434+ super(registryKey, lifecycle, keyCodec, keyToStringFn);
3535+ }
3636+3737+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu.bean;
2525+2626+import com.mojang.serialization.Codec;
2727+2828+public interface Bean {
2929+3030+ Codec<Bean> CODEC = BeanType.REGISTRY.codec()
3131+ .dispatch("type", Bean::type, BeanType::codec);
3232+3333+ BeanType<?> type();
3434+3535+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu.bean;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.Lifecycle;
2828+import com.mojang.serialization.MapCodec;
2929+import com.nayrid.registry.dfu.KeyableRegistry;
3030+import com.nayrid.registry.dfu.KeyableRegistryImpl;
3131+3232+import java.util.function.Function;
3333+3434+public record BeanType<T extends Bean>(MapCodec<T> codec) {
3535+3636+ public static final KeyableRegistry<String, BeanType<?>> REGISTRY = new KeyableRegistryImpl<>(
3737+ "beans", Lifecycle.stable(), Codec.STRING, Function.identity());
3838+3939+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu.bean;
2525+2626+public final class BeanTypes {
2727+2828+ private BeanTypes() {
2929+ }
3030+3131+ public static final BeanType<StringyBean> STRINGY_BEAN = register("stringy_bean", new BeanType<>(StringyBean.CODEC));
3232+ public static final BeanType<CountingBean> COUNTING_BEAN = register("counting_bean", new BeanType<>(CountingBean.CODEC));
3333+3434+ @SuppressWarnings("unchecked")
3535+ public static <T extends Bean> BeanType<T> register(final String key, final BeanType<T> beanType) {
3636+ return (BeanType<T>) BeanType.REGISTRY.register(key, beanType).valueOrThrow();
3737+ }
3838+3939+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu.bean;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.MapCodec;
2828+import com.mojang.serialization.codecs.RecordCodecBuilder;
2929+3030+public record CountingBean(int countingNumber) implements Bean {
3131+3232+ public static final MapCodec<CountingBean> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
3333+ Codec.INT.fieldOf("counting_number").forGetter(CountingBean::countingNumber))
3434+ .apply(instance, CountingBean::new));
3535+3636+ @Override
3737+ public BeanType<?> type() {
3838+ return BeanTypes.COUNTING_BEAN;
3939+ }
4040+4141+}
···11+/*
22+ * This file is part of registry, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.registry.dfu.bean;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.mojang.serialization.MapCodec;
2828+import com.mojang.serialization.codecs.RecordCodecBuilder;
2929+3030+public record StringyBean(String stringyString) implements Bean {
3131+3232+ public static final MapCodec<StringyBean> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
3333+ Codec.STRING.fieldOf("stringy_string").forGetter(StringyBean::stringyString))
3434+ .apply(instance, StringyBean::new));
3535+3636+ @Override
3737+ public BeanType<?> type() {
3838+ return BeanTypes.STRINGY_BEAN;
3939+ }
4040+4141+}