001/*
002 * ============================================================================
003 *  Copyright © 2002-2026 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.lang;
019
020import org.apiguardian.api.API;
021import org.tquadrat.foundation.annotation.ClassVersion;
022
023import java.util.Optional;
024import java.util.function.Consumer;
025import java.util.function.Function;
026
027import static org.apiguardian.api.API.Status.STABLE;
028import static org.tquadrat.foundation.lang.Objects.isNull;
029import static org.tquadrat.foundation.lang.Objects.nonNull;
030import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
031
032/**
033 *  <p>{Instances of this record are meant to be used as the return values for
034 *  methods that should either return a proper result, or an error code.}</p>
035 *  <p>A sample use case may look like this:</p>
036 *  <div class="source-container"><pre>…
037 *  public final Status&lt;Result,ErrorCode&gt; processInput( final InputStream input ) {…}
038 *  …
039 *
040 *  …
041 *  private final Throwable errorHandler( ErrorCode ) {…}
042 *  …
043 *
044 *  …
045 *  public final Result execute( final File inputFile ) throws Throwable
046 *  {
047 *      ErrorHandler&lt;ErrorCode&gt; errorHandler =  this::errorHandler;
048 *      try( var inputStream = new FileInputStream( inputFile ) )
049 *      {
050 *          return processInput( inputStream ).getOrElse( errorHandler );
051 *      }
052 *  }
053 *  …</pre></div>
054 *
055 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
056 *  @version $Id: Status.java 1163 2026-03-20 15:28:33Z tquadrat $
057 *  @since 0.1.0
058 *
059 *  @UMLGraph.link
060 *
061 *  @param  <V>  The type of the result value.
062 *  @param  <C>  The type of the error code.
063 *  @param  result  The result value; can be {@code null}.
064 *  @param  errorCode   The error code; a value of {@code null} indicates a
065 *      success.
066 */
067@SuppressWarnings( {"ProhibitedExceptionDeclared", "ProhibitedExceptionThrown"} )
068@ClassVersion( sourceVersion = "$Id: Status.java 1163 2026-03-20 15:28:33Z tquadrat $" )
069@API( status = STABLE, since = "0.1.0" )
070public record Status<V,C>( V result, C errorCode )
071{
072        /*---------*\
073    ====** Methods **==========================================================
074        \*---------*/
075    /**
076     *  Returns the result in case of a success, otherwise executes the given
077     *  error handler and throws the exception determined by it.
078     *
079     *  @param  errorHandler    The error handler.
080     *  @return The result.
081     *  @throws RuntimeException    Any exception that is determined by the
082     *      error handler.
083     *
084     *  @since 0.2.1
085     */
086    @API( status = STABLE, since = "0.2.1" )
087    public final V getOrElseThrow( final ErrorHandler<? super C> errorHandler ) throws RuntimeException
088    {
089        requireNonNullArgument( errorHandler, "errorHandler" );
090
091        if( isFailure() ) throw errorHandler.handleError( errorCode );
092
093        //---* Done *----------------------------------------------------------
094        return result;
095    }   //  getOrElseThrow()
096
097    /**
098     *  Returns whether this {@code Status} instance indicates a failure.
099     *
100     *  @return {@code true} if the status indicates a failure, {@code false}
101     *      otherwise.
102     */
103    public final boolean isFailure() { return nonNull( errorCode ); }
104
105    /**
106     *  Returns whether this {@code Status} instance indicates a success.
107     *
108     *  @return {@code true} if the status indicates a success, {@code false}
109     *      otherwise.
110     */
111    public final boolean isSuccess() { return isNull( errorCode ); }
112
113    /**
114     *  Performs the given conversion on success, otherwise returns
115     *  {@link Optional#empty()}.
116     *
117     *  @param  <R> The type of the conversion result.
118     *  @param  conversion  The conversion.
119     *  @return An instance of
120     *      {@link Optional}
121     *      that holds the converted result in case of a success, or it will
122     *      be
123     *      {@linkplain Optional#empty() empty}
124     *      otherwise.
125     *
126     *  @note As the result can be {@code null}, too (or the result of the
127     *      conversion is {@code null}), an empty return value does not
128     *      necessarily indicate a failure.
129     */
130    public final <R> Optional<R> map( final Function<? super V,? extends R> conversion )
131    {
132        requireNonNullArgument( conversion, "conversion" );
133
134        final Optional<R> retValue = Optional.ofNullable( isSuccess() ? conversion.apply( result ) : null );
135
136        //---* Done *----------------------------------------------------------
137        return retValue;
138    }   //  map()
139
140    /**
141     *  Performs the given action on success, otherwise does nothing.
142     *
143     *  @param  action  The action.
144     */
145    public final void onSuccess( final Consumer<? super V> action )
146    {
147        requireNonNullArgument( action, "action" );
148        if( isSuccess() ) action.accept( result );
149    }   //  onSuccess()
150
151    /**
152     *  Performs the given action on success, otherwise throws the exception
153     *  determined by the error handler.
154     *
155     *  @param  action  The action.
156     *  @param  errorHandler    The error handler.
157     *  @throws RuntimeException    Any exception that is determined by the
158     *      error handler.
159     */
160    public final void onSuccess( final Consumer<? super V> action, final ErrorHandler<? super C> errorHandler ) throws RuntimeException
161    {
162        requireNonNullArgument( action, "action" );
163        requireNonNullArgument( errorHandler, "errorHandler" );
164
165        if( isFailure() ) throw errorHandler.handleError( errorCode );
166        action.accept( result );
167    }   //  onSuccess()
168
169    /**
170     *  Performs the given conversion on success, otherwise throws the
171     *  exception determined by the error handler.
172     *
173     *  @param  <R> The type of the conversion result.
174     *  @param  conversion  The conversion.
175     *  @param  errorHandler    The error handler.
176     *  @return The converted result.
177     *  @throws RuntimeException    Any exception that is determined by the
178     *      error handler.
179     */
180    public final <R> R onSuccess( final Function<? super V,R> conversion, final ErrorHandler<? super C> errorHandler ) throws RuntimeException
181    {
182        requireNonNullArgument( conversion, "conversion" );
183        requireNonNullArgument( errorHandler, "errorHandler" );
184
185        if( isFailure() ) throw errorHandler.handleError( errorCode );
186        final var retValue = conversion.apply( result );
187
188        //---* Done *----------------------------------------------------------
189        return retValue;
190    }   //  onSuccess()
191}
192//  record Status
193
194/*
195 *  End of File
196 */