dev.cel.common.CelValidationResult Maven / Gradle / Ivy
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dev.cel.common;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Comparator.comparing;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.InlineMe;
import dev.cel.common.annotations.Internal;
import org.jspecify.nullness.Nullable;
/**
* CelValidationResult encapsulates the {@code CelAbstractSyntaxTree} and {@code CelIssue} set which
* may be generated during the parse and check phases.
*/
@Immutable
public final class CelValidationResult {
private static final Joiner JOINER = Joiner.on('\n');
@SuppressWarnings("Immutable")
private final @Nullable Throwable failure;
private final @Nullable CelAbstractSyntaxTree ast;
private final CelSource source;
private final ImmutableList issues;
private final boolean hasError;
/** Internal: Consumers should not be creating an instance of this class directly. */
@Internal
public CelValidationResult(CelSource source, ImmutableList issues) {
this(/* ast= */ null, source, issues, /* failure= */ null);
}
/** Internal: Consumers should not be creating an instance of this class directly. */
@Internal
public CelValidationResult(CelSource source, Throwable failure, ImmutableList issues) {
this(/* ast= */ null, source, issues, failure);
}
/** Internal: Consumers should not be creating an instance of this class on their own */
@Internal
public CelValidationResult(CelAbstractSyntaxTree ast, ImmutableList issues) {
this(ast, ast.getSource(), issues, /* failure= */ null);
}
private CelValidationResult(
@Nullable CelAbstractSyntaxTree ast,
CelSource source,
ImmutableList issues,
@Nullable Throwable failure) {
this.ast = ast;
this.source = source;
this.issues = ImmutableList.sortedCopyOf(comparing(CelIssue::getSourceLocation), issues);
this.hasError = issues.stream().anyMatch(CelValidationResult::issueIsError) || failure != null;
this.failure = failure;
}
/**
* Returns the validated {@code CelAbstractSyntaxTree} if one exists.
*
* When {@link #hasError} returns {@code true}, this accessor will throw a {@link
* CelValidationException} containing the error set which prevented AST generation.
*/
@CanIgnoreReturnValue
public CelAbstractSyntaxTree getAst() throws CelValidationException {
if (hasError) {
if (failure != null) {
throw new CelValidationException(source, getErrors(), failure.getMessage(), failure);
}
throw new CelValidationException(source, getErrors());
}
return ast;
}
/** Return the {@code CelSource} associated with the result. */
public CelSource getSource() {
return source;
}
/**
* Whether a {@code CelIssue} with an {@link CelIssue.Severity#ERROR} severity was encountered
* during validation.
*/
public boolean hasError() {
return hasError;
}
/** Return the set of {@code CelIssue}s with an {@code ERROR} severity. */
public ImmutableList getErrors() {
return issues.stream().filter(CelValidationResult::issueIsError).collect(toImmutableList());
}
/** Return all {@code CelIssue}s encountered durint validation. */
public ImmutableList getAllIssues() {
return issues;
}
/** Convert all issues to a human-readable string. */
public String getIssueString() {
return JOINER.join(Iterables.transform(issues, iss -> iss.toDisplayString(source)));
}
/**
* Convert the {@code CelIssue} set to a debug string.
*
* @deprecated Use {@link #getIssueString()} instead.
*/
@Deprecated
@InlineMe(replacement = "this.getIssueString()")
public String getDebugString() {
return getIssueString();
}
/** Convert the {@code CelIssue}s with {@code ERROR} severity to an error string. */
public String getErrorString() {
return JOINER.join(Iterables.transform(getErrors(), error -> error.toDisplayString(source)));
}
private static boolean issueIsError(CelIssue iss) {
return iss.getSeverity() == CelIssue.Severity.ERROR;
}
}