blob: 19dcb3b33ab3915536aae8395373f5966ca64a07 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library barback.errors;
import 'package:stack_trace/stack_trace.dart';
import 'asset/asset_id.dart';
import 'transformer/wrapping_aggregate_transformer.dart';
import 'utils.dart';
/// Error thrown when an asset with [id] cannot be found.
class AssetNotFoundException implements Exception {
final AssetId id;
AssetNotFoundException(this.id);
String toString() => "Could not find asset $id.";
}
/// Replaces any occurrences of [AggregateException] in [errors] with the list
/// of errors it contains.
Iterable<BarbackException> flattenAggregateExceptions(
Iterable<BarbackException> errors) {
return errors.expand((error) {
if (error is AggregateException) return error.errors;
return [error];
});
}
/// The interface for exceptions from the barback graph or its transformers.
///
/// These exceptions are never produced by programming errors in barback.
abstract class BarbackException implements Exception {
/// Takes a collection of [BarbackExceptions] and returns a single exception
/// that contains them all.
///
/// If [errors] is empty, returns `null`. If it only has one error, that
/// error is returned. Otherwise, an [AggregateException] is returned.
static BarbackException aggregate(Iterable<BarbackException> errors) {
if (errors.isEmpty) return null;
if (errors.length == 1) return errors.single;
return new AggregateException(errors);
}
}
/// An error that wraps a collection of other [BarbackException]s.
///
/// It implicitly flattens any [AggregateException]s that occur in the list of
/// exceptions it wraps.
class AggregateException implements BarbackException {
final Set<BarbackException> errors;
AggregateException(Iterable<BarbackException> errors)
: errors = flattenAggregateExceptions(errors).toSet();
String toString() {
var buffer = new StringBuffer();
buffer.writeln("Multiple errors occurred:\n");
for (var error in errors) {
buffer.writeln(
prefixLines(error.toString(), prefix: " ", firstPrefix: "- "));
}
return buffer.toString();
}
}
/// Error thrown when two or more transformers both output an asset with [id].
class AssetCollisionException implements BarbackException {
/// All the transforms that output an asset with [id].
///
/// If this only contains a single transform, that indicates that a
/// transformer produced an output that collides with a source asset or an
/// asset from a previous phase.
final Set<TransformInfo> transforms;
final AssetId id;
AssetCollisionException(Iterable<TransformInfo> transforms, this.id)
: transforms = new Set.from(transforms);
String toString() => "Transforms $transforms all emitted asset $id.";
}
/// Error thrown when a transformer requests an input [id] which cannot be
/// found.
class MissingInputException implements BarbackException {
/// The transform that requested [id].
final TransformInfo transform;
final AssetId id;
MissingInputException(this.transform, this.id);
String toString() => "Transform $transform tried to load missing input $id.";
}
/// Error thrown when a transformer outputs an asset to a different package than
/// the primary input's.
class InvalidOutputException implements BarbackException {
/// The transform that output the asset.
final TransformInfo transform;
final AssetId id;
InvalidOutputException(this.transform, this.id);
String toString() => "Transform $transform emitted $id, which wasn't in the "
"same package (${transform.primaryId.package}).";
}
/// Base class for an error that wraps another.
abstract class _WrappedException implements BarbackException {
/// The wrapped exception.
final error;
final Chain stackTrace;
String get message => "$_message: ${getErrorMessage(error)}";
String get _message;
_WrappedException(error, StackTrace stackTrace)
: this.error = error,
this.stackTrace = _getChain(error, stackTrace);
String toString() {
var result = message;
if (stackTrace != null) result = "$result\n${stackTrace.terse}";
return result;
}
}
/// Returns the stack chain for [error] and [stackTrace].
Chain _getChain(error, StackTrace stackTrace) {
if (error is Error && stackTrace == null) stackTrace = error.stackTrace;
if (stackTrace != null) return new Chain.forTrace(stackTrace);
return null;
}
/// Error wrapping an exception thrown by a transform.
class TransformerException extends _WrappedException {
/// The transform that threw the exception.
final TransformInfo transform;
TransformerException(this.transform, error, StackTrace stackTrace)
: super(error, stackTrace);
String get _message => "Transform $transform threw error";
}
/// Error thrown when a source asset [id] fails to load.
///
/// This can be thrown either because the source asset was expected to exist and
/// did not or because reading it failed somehow.
class AssetLoadException extends _WrappedException {
final AssetId id;
AssetLoadException(this.id, error, [StackTrace stackTrace])
: super(error, stackTrace);
String get _message => "Failed to load source asset $id";
}
/// Information about a single transform in the barback graph.
///
/// Identifies a single transformation in the barback graph.
///
/// A transformation is uniquely identified by the ID of its primary input, and
/// the transformer that is applied to it.
class TransformInfo {
/// The transformer that's run for this transform.
///
/// This may be a [Transformer] or a [WrappingAggregateTransformer]. It may
/// also return additional types in the future.
final transformer;
/// The id of this transform's primary asset.
final AssetId primaryId;
TransformInfo(transformer, this.primaryId)
: transformer = transformer is WrappingAggregateTransformer
? transformer.transformer
: transformer;
bool operator ==(other) =>
other is TransformInfo &&
other.transformer == transformer &&
other.primaryId == primaryId;
int get hashCode => transformer.hashCode ^ primaryId.hashCode;
String toString() => "$transformer on $primaryId";
}