001/*
002 * JDrupes Builder
003 * Copyright (C) 2025, 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.time.Instant;
022import java.util.Optional;
023import java.util.stream.Stream;
024import org.jdrupes.builder.api.ExecResult;
025import org.jdrupes.builder.api.Resource;
026import org.jdrupes.builder.api.ResourceProvider;
027
028/// Default implementation of a test result.
029///
030/// @param <T> the generic type
031///
032public class DefaultExecResult<T extends Resource> extends ResourceObject
033        implements ExecResult<T> {
034
035    private final ResourceProvider provider;
036    private final int exitValue;
037    private boolean isFaulty;
038    private final StreamCollector<T> resources;
039    private Instant asOf;
040
041    /// Initializes a new default exec result. Note that an `exitValue`
042    /// different from 0 does not automatically mark the result as faulty. 
043    ///
044    /// @param provider the provider
045    /// @param name the name
046    /// @param exitValue the exit value
047    ///
048    @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
049    protected DefaultExecResult(ResourceProvider provider,
050            String name, int exitValue) {
051        name(name);
052        this.provider = provider;
053        this.exitValue = exitValue;
054        resources = new StreamCollector<>(true);
055    }
056
057    /// Returns the [ResourceProvider] that generated this result.
058    ///
059    /// @return the resource provider
060    ///
061    public ResourceProvider provider() {
062        return provider;
063    }
064
065    @Override
066    public int exitValue() {
067        return exitValue;
068    }
069
070    @Override
071    public boolean isFaulty() {
072        return isFaulty;
073    }
074
075    @Override
076    public DefaultExecResult<T> setFaulty() {
077        isFaulty = true;
078        return this;
079    }
080
081    @Override
082    public Stream<T> resources() {
083        return resources.stream();
084    }
085
086    /// Sets the resources associated with this result.
087    ///
088    /// @param resources the resources
089    /// @return the default exec result
090    ///
091    public DefaultExecResult<T> resources(Stream<T> resources) {
092        this.resources.add(resources);
093        return this;
094    }
095
096    @Override
097    public DefaultExecResult<T> asOf(Instant asOf) {
098        if (this.asOf != null) {
099            throw new IllegalStateException("asOf() may only be called once.");
100        }
101        this.asOf = asOf;
102        return this;
103    }
104
105    @Override
106    public Optional<Instant> asOf() {
107        if (asOf == null) {
108            return Optional.empty();
109        }
110        return Optional.of(asOf);
111    }
112
113    @Override
114    public String toString() {
115        return ExecResult.class.getSimpleName()
116            + " from " + provider.name()
117            + name().map(n -> ": " + n).orElse("")
118            + " {exitValue=" + exitValue + "}";
119    }
120}