001package com.github.dkfellows.notabuilder;
002
003/**
004 * A record that has four fields.
005 * @param foo The foo field.
006 * @param bar The bar field.
007 * @param grill The grill field.
008 * @param quux The quux field.
009 */
010public record ThingRecord(int foo, int bar, double grill, String quux) {
011    private static final Foo DEFAULT_FOO = new Foo(0);
012    private static final Bar DEFAULT_BAR = new Bar(0);
013    private static final Grill DEFAULT_GRILL = new Grill(0.0);
014    private static final Quux DEFAULT_QUUX = new Quux("");
015
016    /** Helper because the canonical constructor must be called as the first statement.
017     * @param <T> The type of argument being extracted.
018     * @param args The argument list to extract from.
019     * @param defaultValue The default for the argument being extracted.
020     *      Note that <em>this also tells the runtime code what argument to extract</em>.
021     * @return The extracted argument, if present, or a default.
022     */
023    private static <T extends Args> T select(Args[] args, T defaultValue) {
024        T val = defaultValue;
025        for (var arg: args) {
026            if (arg.getClass() == val.getClass()) {
027                @SuppressWarnings("unchecked")
028                var thisVal = (T) arg;
029                val = thisVal;
030            }
031        }
032        return val;
033    }
034
035    /**
036     * Make an instance of the record. 
037     * @param args The labelled non-default arguments to pass.
038     * @see ThingRecord.Args#foo(int)
039     * @see ThingRecord.Args#bar(int)
040     * @see ThingRecord.Args#grill(double)
041     * @see ThingRecord.Args#quux(String)
042     */
043    public ThingRecord(Args... args) {
044        this(select(args, DEFAULT_FOO).foo(),
045            select(args, DEFAULT_BAR).bar(),
046            select(args, DEFAULT_GRILL).grill(),
047            select(args, DEFAULT_QUUX).quux());
048    }
049
050    /** Argument labeller. */
051    public sealed interface Args permits Foo, Bar, Grill, Quux {
052        /**
053         * Label a value as a {@link ThingRecord#foo()}.
054         * @param value The value to label.
055         * @return The labelled value.
056         */
057        public static Args foo(int value) {
058            return new Foo(value);
059        }
060    
061        /**
062         * Label a value as a {@link ThingRecord#bar()}.
063         * @param value The value to label.
064         * @return The labelled value.
065         */
066        public static Args bar(int value) {
067            return new Bar(value);
068        }
069    
070        /**
071         * Label a value as a {@link ThingRecord#grill()}.
072         * @param value The value to label.
073         * @return The labelled value.
074         */
075        public static Args grill(double value) {
076            return new Grill(value);
077        }
078    
079        /**
080         * Label a value as a {@link ThingRecord#quux()}.
081         * @param value The value to label.
082         * @return The labelled value.
083         */
084        public static Args quux(String value) {
085            return new Quux(value);
086        }
087    }
088    
089    private record Foo(int foo) implements Args {}
090    private record Bar(int bar) implements Args {}
091    private record Grill(double grill) implements Args {}
092    private record Quux(String quux) implements Args {}
093}