001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.assertions;
017
018import static java.util.Objects.requireNonNull;
019import static org.assertj.core.api.Assertions.assertThat;
020
021import io.github.ascopes.jct.diagnostics.TraceDiagnostic;
022import io.github.ascopes.jct.repr.TraceDiagnosticRepresentation;
023import java.util.Locale;
024import javax.tools.JavaFileObject;
025import org.apiguardian.api.API;
026import org.apiguardian.api.API.Status;
027import org.assertj.core.api.AbstractAssert;
028import org.assertj.core.api.AbstractInstantAssert;
029import org.assertj.core.api.AbstractLongAssert;
030import org.assertj.core.api.AbstractStringAssert;
031import org.jspecify.annotations.Nullable;
032
033/**
034 * Assertions for an individual {@link TraceDiagnostic trace diagnostic}.
035 *
036 * @author Ashley Scopes
037 * @since 0.0.1
038 */
039@API(since = "0.0.1", status = Status.STABLE)
040public final class TraceDiagnosticAssert
041    extends AbstractAssert<TraceDiagnosticAssert, TraceDiagnostic<? extends JavaFileObject>> {
042
043  /**
044   * Initialize this assertion type.
045   *
046   * @param value the value to assert on.
047   */
048  public TraceDiagnosticAssert(@Nullable TraceDiagnostic<? extends JavaFileObject> value) {
049    super(value, TraceDiagnosticAssert.class);
050    info.useRepresentation(TraceDiagnosticRepresentation.getInstance());
051  }
052
053  /**
054   * Get assertions for the kind of the diagnostic.
055   *
056   * @return the assertions for the diagnostic kind.
057   * @throws AssertionError if the diagnostic is null.
058   */
059  public DiagnosticKindAssert kind() {
060    isNotNull();
061
062    return new DiagnosticKindAssert(actual.getKind());
063  }
064
065  /**
066   * Get assertions for the source of the diagnostic.
067   *
068   * <p>If no source is present, then the value in the returned assertions may be {@code null}.
069   *
070   * @return the assertions for the source of the diagnostic.
071   * @throws AssertionError if the diagnostic is null.
072   */
073  public JavaFileObjectAssert source() {
074    isNotNull();
075
076    return new JavaFileObjectAssert(actual.getSource());
077  }
078
079  /**
080   * Get assertions for the position of the diagnostic.
081   *
082   * <p>The value may be -1 if no information is available.
083   *
084   * @return the assertions for the position of the diagnostic.
085   * @throws AssertionError if the diagnostic is null.
086   */
087  public AbstractLongAssert<?> position() {
088    return assertPosition(actual.getPosition(), "position");
089  }
090
091  /**
092   * Get assertions for the start position of the diagnostic.
093   *
094   * <p>The value may be -1 if no information is available.
095   *
096   * @return the assertions for the start position of the diagnostic.
097   * @throws AssertionError if the diagnostic is null.
098   */
099  public AbstractLongAssert<?> startPosition() {
100    return assertPosition(actual.getPosition(), "startPosition");
101  }
102
103  /**
104   * Get assertions for the end position of the diagnostic.
105   *
106   * <p>The value may be -1 if no information is available.
107   *
108   * @return the assertions for the end position of the diagnostic.
109   * @throws AssertionError if the diagnostic is null.
110   */
111  public AbstractLongAssert<?> endPosition() {
112    return assertPosition(actual.getEndPosition(), "endPosition");
113  }
114
115  /**
116   * Get assertions for the line number of the diagnostic.
117   *
118   * <p>The value may be -1 if no information is available.
119   *
120   * @return the assertions for the line number of the diagnostic.
121   * @throws AssertionError if the diagnostic is null.
122   */
123  public AbstractLongAssert<?> lineNumber() {
124    return assertPosition(actual.getLineNumber(), "lineNumber");
125  }
126
127  /**
128   * Get assertions for the column number of the diagnostic.
129   *
130   * <p>The value may be -1 if no information is available.
131   *
132   * @return the assertions for the column number of the diagnostic.
133   * @throws AssertionError if the diagnostic is null.
134   */
135  public AbstractLongAssert<?> columnNumber() {
136    return assertPosition(actual.getColumnNumber(), "columnNumber");
137  }
138
139  /**
140   * Get assertions for the code of the diagnostic.
141   *
142   * @return the assertions for the code of the diagnostic.
143   * @throws AssertionError if the diagnostic is null.
144   */
145  public AbstractStringAssert<?> code() {
146    isNotNull();
147
148    return assertThat(actual.getCode());
149  }
150
151  /**
152   * Get assertions for the message of the diagnostic, assuming the default locale.
153   *
154   * @return the assertions for the message of the diagnostic.
155   * @throws AssertionError if the diagnostic is null.
156   */
157  public AbstractStringAssert<?> message() {
158    isNotNull();
159
160    return assertThat(actual.getMessage(null));
161  }
162
163  /**
164   * Get assertions for the message of the diagnostic.
165   *
166   * @param locale the locale to use.
167   * @return the assertions for the message of the diagnostic.
168   * @throws AssertionError if the diagnostic is null.
169   */
170  public AbstractStringAssert<?> message(Locale locale) {
171    requireNonNull(locale, "locale must not be null");
172
173    isNotNull();
174
175    return assertThat(actual.getMessage(locale));
176  }
177
178  /**
179   * Get assertions for the timestamp of the diagnostic.
180   *
181   * @return the assertions for the timestamp of the diagnostic.
182   * @throws AssertionError if the diagnostic is null.
183   */
184  public AbstractInstantAssert<?> timestamp() {
185    isNotNull();
186
187    return assertThat(actual.getTimestamp());
188  }
189
190  /**
191   * Get assertions for the thread ID of the thread that reported the diagnostic to the compiler.
192   *
193   * @return the assertions for the thread ID.
194   * @throws AssertionError if the diagnostic is null.
195   */
196  public AbstractLongAssert<?> threadId() {
197    isNotNull();
198
199    return assertThat(actual.getThreadId());
200  }
201
202  /**
203   * Get assertions for the thread name of the thread that reported the diagnostic.
204   *
205   * <p>This may not be present in some situations, in which case the returned assertions will be
206   * performed on a null value instead.
207   *
208   * @return the assertions for the thread name.
209   * @throws AssertionError if the diagnostic is null.
210   */
211  public AbstractStringAssert<?> threadName() {
212    isNotNull();
213
214    return assertThat(actual.getThreadName());
215  }
216
217  /**
218   * Get assertions for the stack trace of the location the diagnostic was reported to.
219   *
220   * @return the assertions for the stack trace.
221   * @throws AssertionError if the diagnostic is null.
222   */
223  public StackTraceAssert stackTrace() {
224    isNotNull();
225
226    return new StackTraceAssert(actual.getStackTrace());
227  }
228
229  private AbstractLongAssert<?> assertPosition(long position, String name) {
230    isNotNull();
231    return assertThat(position).describedAs(name);
232  }
233}