Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve error messages #491

Merged
merged 8 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/mix/lib/src/attributes/border/border_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/mix_error.dart';

part 'border_dto.g.dart';

@immutable
Expand Down Expand Up @@ -151,6 +153,10 @@ extension BoxBorderExt on BoxBorder {
final self = this;
if (self is Border) return (self).toDto();
if (self is BorderDirectional) return (self).toDto();
throw UnimplementedError();

throw MixError.unsupportedTypeInDto(
BoxBorder,
['Border', 'BorderDirectional'],
);
}
}
18 changes: 8 additions & 10 deletions packages/mix/lib/src/attributes/border/border_radius_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/diagnostic_properties_builder_ext.dart';
import '../../internal/mix_error.dart';

part 'border_radius_dto.g.dart';

Expand Down Expand Up @@ -147,16 +148,13 @@ final class BorderRadiusDirectionalDto

extension BorderRadiusGeometryMixExt on BorderRadiusGeometry {
BorderRadiusGeometryDto toDto() {
if (this is BorderRadius) {
return (this as BorderRadius).toDto();
}
if (this is BorderRadiusDirectional) {
return (this as BorderRadiusDirectional).toDto();
}
throw ArgumentError.value(
this,
'radius',
'BorderRadiusGeometry type is not supported',
final self = this;
if (self is BorderRadius) return self.toDto();
if (self is BorderRadiusDirectional) return self.toDto();

throw MixError.unsupportedTypeInDto(
BorderRadiusGeometry,
['BorderRadius', 'BorderRadiusDirectional'],
);
}
}
21 changes: 13 additions & 8 deletions packages/mix/lib/src/attributes/border/shape_border_dto.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// ignore_for_file: prefer_relative_imports, avoid-importing-entrypoint-exports
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';
Expand Down Expand Up @@ -302,14 +303,18 @@ extension ShapeBorderExt on ShapeBorder {
if (self is StarBorder) return (self).toDto();
if (self is MixOutlinedBorder) return (self).toDto();

throw ArgumentError.value(
this,
'shapeBorder',
'Unsupported ShapeBorder type.\n'
'If you are trying to create a custom ShapeBorder, it must extend MixOutlinedBorder.'
' Otherwise, use a built-in Mix shape.\n'
'Custom ShapeBorders that do not extend MixOutlinedBorder will not work with Mix.',
);
throw FlutterError.fromParts([
ErrorSummary('Unsupported ShapeBorder type.'),
ErrorDescription(
'If you are trying to create a custom ShapeBorder, it must extend MixOutlinedBorder. '
'Otherwise, use a built-in Mix shape such as BeveledRectangleBorder, CircleBorder, '
'ContinuousRectangleBorder, LinearBorder, RoundedRectangleBorder, StadiumBorder, or StarBorder.',
),
ErrorHint(
'Custom ShapeBorders that do not extend MixOutlinedBorder will not work with Mix.',
),
DiagnosticsProperty<ShapeBorder>('The unsupported ShapeBorder was', this),
]);
}
}

Expand Down
15 changes: 9 additions & 6 deletions packages/mix/lib/src/attributes/decoration/decoration_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/diagnostic_properties_builder_ext.dart';
import '../../internal/mix_error.dart';

part 'decoration_dto.g.dart';

Expand Down Expand Up @@ -265,12 +266,14 @@ ShapeDecorationDto _toShapeDecorationDto(BoxDecorationDto dto) {

extension DecorationMixExt on Decoration {
DecorationDto toDto() {
if (this is BoxDecoration) {
return (this as BoxDecoration).toDto();
} else if (this is ShapeDecoration) {
return (this as ShapeDecoration).toDto();
}
throw Exception('Unknown decoration type: $runtimeType');
final self = this;
if (self is BoxDecoration) return self.toDto();
if (self is ShapeDecoration) return self.toDto();

throw MixError.unsupportedTypeInDto(
Decoration,
['BoxDecoration', 'ShapeDecoration'],
);
}
}

Expand Down
36 changes: 26 additions & 10 deletions packages/mix/lib/src/attributes/gradient/gradient_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/constants.dart';
import '../../internal/mix_error.dart';

part 'gradient_dto.g.dart';

/// Represents a base Data transfer object of [Gradient]
Expand Down Expand Up @@ -153,7 +156,10 @@ extension GradientExt on Gradient {
if (self is RadialGradient) return (self).toDto();
if (self is SweepGradient) return (self).toDto();

throw UnimplementedError();
throw MixError.unsupportedTypeInDto(
Gradient,
['LinearGradient', 'RadialGradient', 'SweepGradient'],
);
}
}

Expand All @@ -176,15 +182,25 @@ final class GradientUtility<T extends Attribute>
///
/// Throws an [UnimplementedError] if the given gradient type is not supported.
T as(Gradient gradient) {
if (gradient is RadialGradient) {
return radial.as(gradient);
} else if (gradient is LinearGradient) {
return linear.as(gradient);
} else if (gradient is SweepGradient) {
return sweep.as(gradient);
switch (gradient) {
case RadialGradient():
return radial.as(gradient);
case LinearGradient():
return linear.as(gradient);
case SweepGradient():
return sweep.as(gradient);
}
throw UnimplementedError(
'Cannot create $T from gradient of type ${gradient.runtimeType}',
);

throw FlutterError.fromParts([
ErrorSummary('Mix does not support custom gradient implementations.'),
ErrorDescription(
'The provided gradient of type ${gradient.runtimeType} is not supported.',
),
ErrorHint(
'If you believe this gradient type should be supported, please open an issue at '
'$mixIssuesUrl with details about your implementation '
'and its use case.',
),
]);
}
}
14 changes: 13 additions & 1 deletion packages/mix/lib/src/attributes/shadow/shadow_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,19 @@ final class ElevationUtility<T extends Attribute>
///
/// Throws an [AssertionError] if the provided [value] is not a valid elevation value.
T call(int value) {
assert(kElevationToShadow.containsKey(value), 'Invalid elevation value');
if (!kElevationToShadow.containsKey(value)) {
throw FlutterError.fromParts(
[
ErrorSummary('Invalid elevation value provided.'),
ErrorDescription(
'The elevation value $value is not a valid predefined elevation.',
),
ErrorHint(
'Please use one of the predefined elevation values: ${kElevationToShadow.keys.join(", ")}.',
),
],
);
}

final boxShadows = kElevationToShadow[value]!.map((e) => e.toDto());

Expand Down
16 changes: 7 additions & 9 deletions packages/mix/lib/src/attributes/spacing/edge_insets_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/diagnostic_properties_builder_ext.dart';
import '../../internal/mix_error.dart';

part 'edge_insets_dto.g.dart';

Expand Down Expand Up @@ -156,16 +157,13 @@ final class EdgeInsetsDirectionalDto

extension EdgeInsetsGeometryExt on EdgeInsetsGeometry {
SpacingDto toDto() {
if (this is EdgeInsetsDirectional) {
return (this as EdgeInsetsDirectional).toDto();
} else if (this is EdgeInsets) {
return (this as EdgeInsets).toDto();
}
final self = this;
if (self is EdgeInsetsDirectional) return self.toDto();
if (self is EdgeInsets) return self.toDto();

throw ArgumentError.value(
this,
'edgeInsets',
'Must be either EdgeInsets or EdgeInsetsDirectional',
throw MixError.unsupportedTypeInDto(
EdgeInsetsGeometry,
['EdgeInsetsDirectional', 'EdgeInsets'],
);
}
}
16 changes: 13 additions & 3 deletions packages/mix/lib/src/attributes/text_style/text_style_dto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../internal/constants.dart';
import '../../internal/diagnostic_properties_builder_ext.dart';

part 'text_style_dto.g.dart';
Expand All @@ -16,9 +17,18 @@ final class TextStyleDataRef extends TextStyleData {
@override
TextStyleDataRef merge(covariant TextStyleDataRef? other) {
if (other == null) return this;
throw UnsupportedError(
'Cannot merge $this with $other, most likely there is an error on Mix',
);
throw FlutterError.fromParts([
ErrorSummary('Cannot merge TextStyleDataRef instances'),
ErrorDescription(
'An attempt was made to merge incompatible TextStyleDataRef objects. '
'Attempted to merge: $this with $other',
),
ErrorHint('This is likely due to an internal error in the Mix library.'),
ErrorHint(
'Please open an issue on GitHub: $mixIssuesUrl, '
'Explain how you encountered this error, and provide the code that triggered it.',
),
]);
}

@override
Expand Down
43 changes: 28 additions & 15 deletions packages/mix/lib/src/core/factory/style_mix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,34 @@ class Style with EqualityMixin {
final styleList = <StyledAttribute>[];

for (final attribute in attributes) {
if (attribute is StyledAttribute) {
styleList.add(attribute);
} else if (attribute is VariantAttribute) {
applyVariants.add(attribute);
} else if (attribute is NestedStyleAttribute) {
applyVariants.addAll(attribute.value.variants.values);
styleList.addAll(attribute.value.styles.values);
} else if (attribute is SpecUtility) {
if (attribute.attributeValue != null) {
final nestedStyle = Style.create([attribute.attributeValue!]);
styleList.addAll(nestedStyle.styles.values);
applyVariants.addAll(nestedStyle.variants.values);
}
} else {
throw UnsupportedError('Unsupported attribute type: $attribute');
switch (attribute) {
case StyledAttribute():
styleList.add(attribute);
case VariantAttribute():
applyVariants.add(attribute);
case NestedStyleAttribute():
applyVariants.addAll(attribute.value.variants.values);
styleList.addAll(attribute.value.styles.values);
case SpecUtility():
if (attribute.attributeValue != null) {
final nestedStyle = Style.create([attribute.attributeValue!]);
styleList.addAll(nestedStyle.styles.values);
applyVariants.addAll(nestedStyle.variants.values);
}
default:
throw FlutterError.fromParts([
ErrorSummary(
'Unsupported attribute type encountered in Style creation.',
),
ErrorDescription(
'The attribute of type ${attribute.runtimeType} is not supported.',
),
ErrorHint(
'Custom Attributes must be subclasses of one of the following types: '
'StyledAttribute, VariantAttribute, NestedStyleAttribute, or SpecUtility. '
'Please ensure your attribute implements one of these supported types.',
),
]);
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/mix/lib/src/internal/constants.dart
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
const kDefaultAnimationDuration = Duration(milliseconds: 200);

const mixIssuesUrl = 'https://github.com/conceptadev/mix/issues/new/choose';
33 changes: 33 additions & 0 deletions packages/mix/lib/src/internal/mix_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:flutter/foundation.dart';

abstract class MixError {
static FlutterError unsupportedTypeInDto(
Type type,
List<String> supportedTypes,
) {
final supportedTypesFormated =
'${supportedTypes.sublist(0, supportedTypes.length - 1).join(', ')} and ${supportedTypes.last}';

return FlutterError.fromParts([
ErrorSummary('Unsupported $type type'),
ErrorDescription(
'The toDto() method is not implemented for this $type subclass.',
),
ErrorHint('Only $supportedTypesFormated are currently supported.'),
]);
}

static FlutterError accessTokenValue(String token, String field) {
return FlutterError.fromParts([
ErrorSummary('Invalid access: $token cannot access field $field'),
ErrorDescription(
'The $field field cannot be directly accessed through TextStyleRef. '
'Ensure you are using the appropriate methods or properties provided by TextStyleToken to interact with the style properties.',
),
ErrorHint(
'Consider using TextStyleToken.resolve() to access the $field. '
'This method ensures that $token is used within the appropriate context where $field is available.',
),
]);
}
}
17 changes: 3 additions & 14 deletions packages/mix/lib/src/theme/tokens/text_style_token.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';

import '../../internal/mix_error.dart';
import '../mix/mix_theme.dart';
import 'mix_token.dart';

Expand Down Expand Up @@ -163,18 +164,6 @@ final class TextStyleRef extends TextStyle with TokenRef<TextStyleToken> {
int get hashCode => token.name.hashCode;
}

TokenFieldAccessError _e(String token, String field) {
return TokenFieldAccessError(token, field);
}

class TokenFieldAccessError extends Error {
final String tokenName;
final String fieldName;

TokenFieldAccessError(this.tokenName, this.fieldName);

@override
String toString() {
return '$tokenName cannot have field $fieldName because it is outside of context';
}
FlutterError _e(String token, String field) {
return MixError.accessTokenValue(token, field);
}
14 changes: 12 additions & 2 deletions packages/mix/lib/src/variants/context_variant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,18 @@ final class ContextVariantBuilder extends VariantAttribute<ContextVariant> {

@override
@protected
Style get value => throw UnimplementedError(
'This is a ContextVariantBuilder you need to call build(context)',
Style get value => throw FlutterError.fromParts(
[
ErrorSummary(
'Attempted to access value of ContextVariantBuilder directly.',
),
ErrorDescription(
'This is a ContextVariantBuilder and requires a BuildContext to resolve.',
),
ErrorHint(
'Use the build(context) method instead of accessing value directly.',
),
],
);

@override
Expand Down
Loading
Loading