001/*
002 * ============================================================================
003 *  Copyright © 2002-2024 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.*;
029
030/**
031 *  Instances of this record are meant to be used as the return values for
032 *  methods that should either return a proper result, or an error code. <br>
033 *  <br>A sample use case may look like this:
034 *  <pre><code>  …
035 *  public final Status&lt;Result,ErrorCode&gt; processInput( final InputStream input ) {…}
036 *  …
037 *
038 *  …
039 *  private final Throwable errorHandler( ErrorCode ) {…}
040 *  …
041 *
042 *  …
043 *  public final Result execute( final File inputFile ) throws Throwable
044 *  {
045 *      ErrorHandler&lt;ErrorCode&gt; errorHandler =  this::errorHandler;
046 *      try( var inputStream = new FileInputStream( inputFile ) )
047 *      {
048 *          return processInput( inputStream ).getOrElse( errorHandler );
049 *      }
050 *  }
051 *  …</code></pre>
052 *
053 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
054 *  @version $Id: Status.java 1119 2024-03-16 09:03:57Z tquadrat $
055 *  @since 0.1.0
056 *
057 *  @UMLGraph.link
058 *
059 *  @param  <V>  The type of the result value.
060 *  @param  <C>  The type of the error code.
061 *  @param  result  The result value; can be {@code null}.
062 *  @param  errorCode   The error code; a value of {@code null} indicates a
063 *      success.
064 */
065@SuppressWarnings( {"ProhibitedExceptionDeclared", "ProhibitedExceptionThrown"} )
066@ClassVersion( sourceVersion = "$Id: Status.java 1119 2024-03-16 09:03:57Z tquadrat $" )
067@API( status = STABLE, since = "0.1.0" )
068public record Status<V,C>( V result, C errorCode )
069{
070        /*---------*\
071    ====** Methods **==========================================================
072        \*---------*/
073    /**
074     *  Returns the result in case of a success, otherwise executes the given
075     *  error handler and throws the exception determined by it.
076     *
077     *  @param  errorHandler    The error handler.
078     *  @return The result.
079     *  @throws RuntimeException    Any exception that is determined by the
080     *      error handler.
081     *
082     *  @since 0.2.1
083     */
084    @API( status = STABLE, since = "0.2.1" )
085    public final V getOrElseThrow( final ErrorHandler<? super C> errorHandler ) throws RuntimeException
086    {
087        requireNonNullArgument( errorHandler, "errorHandler" );
088
089        if( isFailure() ) throw errorHandler.handleError( errorCode );
090
091        //---* Done *----------------------------------------------------------
092        return result;
093    }   //  getOrElseThrow()
094
095    /**
096     *  Returns whether this {@code Status} instance indicates a failure.
097     *
098     *  @return {@code true} if the status indicates a failure, {@code false}
099     *      otherwise.
100     */
101    public final boolean isFailure() { return nonNull( errorCode ); }
102
103    /**
104     *  Returns whether this {@code Status} instance indicates a success.
105     *
106     *  @return {@code true} if the status indicates a success, {@code false}
107     *      otherwise.
108     */
109    public final boolean isSuccess() { return isNull( errorCode ); }
110
111    /**
112     *  Performs the given conversion on success, otherwise returns
113     *  {@link Optional#empty()}.
114     *
115     *  @param  <R> The type of the conversion result.
116     *  @param  conversion  The conversion.
117     *  @return An instance of
118     *      {@link Optional}
119     *      that holds the converted result in case of a success, or it will
120     *      be
121     *      {@linkplain Optional#empty() empty}
122     *      otherwise.
123     *
124     *  @note As the result can be {@code null}, too (or the result of the
125     *      conversion is {@code null}), an empty return value does not
126     *      necessarily indicate a failure.
127     */
128    public final <R> Optional<R> map( final Function<? super V,? extends R> conversion )
129    {
130        requireNonNullArgument( conversion, "conversion" );
131
132        final Optional<R> retValue = Optional.ofNullable( isSuccess() ? conversion.apply( result ) : null );
133
134        //---* Done *----------------------------------------------------------
135        return retValue;
136    }   //  map()
137
138    /**
139     *  Performs the given action on success, otherwise does nothing.
140     *
141     *  @param  action  The action.
142     */
143    public final void onSuccess( final Consumer<? super V> action )
144    {
145        requireNonNullArgument( action, "action" );
146        if( isSuccess() ) action.accept( result );
147    }   //  onSuccess()
148
149    /**
150     *  Performs the given action on success, otherwise throws the exception
151     *  determined by the error handler.
152     *
153     *  @param  action  The action.
154     *  @param  errorHandler    The error handler.
155     *  @throws RuntimeException    Any exception that is determined by the
156     *      error handler.
157     */
158    public final void onSuccess( final Consumer<? super V> action, final ErrorHandler<? super C> errorHandler ) throws RuntimeException
159    {
160        requireNonNullArgument( action, "action" );
161        requireNonNullArgument( errorHandler, "errorHandler" );
162
163        if( isFailure() ) throw errorHandler.handleError( errorCode );
164        action.accept( result );
165    }   //  onSuccess()
166
167    /**
168     *  Performs the given conversion on success, otherwise throws the
169     *  exception determined by the error handler.
170     *
171     *  @param  <R> The type of the conversion result.
172     *  @param  conversion  The conversion.
173     *  @param  errorHandler    The error handler.
174     *  @return The converted result.
175     *  @throws RuntimeException    Any exception that is determined by the
176     *      error handler.
177     */
178    public final <R> R onSuccess( final Function<? super V,R> conversion, final ErrorHandler<? super C> errorHandler ) throws RuntimeException
179    {
180        requireNonNullArgument( conversion, "conversion" );
181        requireNonNullArgument( errorHandler, "errorHandler" );
182
183        if( isFailure() ) throw errorHandler.handleError( errorCode );
184        final var retValue = conversion.apply( result );
185
186        //---* Done *----------------------------------------------------------
187        return retValue;
188    }   //  onSuccess()
189}
190//  record Status
191
192/*
193 *  End of File
194 */