Coder Social home page Coder Social logo

6thsolution / dart_sealed Goto Github PK

View Code? Open in Web Editor NEW
17.0 17.0 9.0 935 KB

Dart and Flutter sealed class generator and annotations, with match methods and other utilities. There is also super_enum compatible API.

Home Page: https://pub.dev/packages/sealed_annotations

License: MIT License

Dart 94.00% Shell 2.95% Batchfile 3.06%
annotation annotations dart equality example flutter generator generic legacy null-safe null-safety sealed sealed-class

dart_sealed's Issues

Unnamed constructor

Add ability to define unnamed constructor.

An easy way is to provide an annotation like @UnNamed() or to reserve a method name like noNamed for this purpose.

Another way is to use constructors on manifest class to denote no named constructor.

Complexities arise when implementing match methods. But we do not need cast methods for in named constructor as it means that we do not need unnamed sub class entirely.

Special thanks to @iamarnas for his recommendations.

Support for nullable optional parameters

After working with this package for a while, I have noticed that the package doesn't support for optional parameters.
I think users will have a better experience if they do not have to submit optional parameters.

Suggestion to add map method and...

Hi ๐Ÿ‘‹

I would recommend to add map methods if the user generates everything @WithWrap(). For now, I am forced every time to add this manually. Map method is useful by copyWith() method.

//...
  R map<R extends Object?>({
    required R Function() loading,
    required R Function(ResultError<T, E> error) error,
    required R Function(ResultData<T, E> data) data,
  }) {
    final result = this;
    if (result is ResultLoading<T, E>) {
      return loading();
    } else if (result is ResultError<T, E>) {
      return error(result);
    } else if (result is ResultData<T, E>) {
      return data(result);
    } else {
      throw AssertionError();
    }
  }

  R mapOrElse<R extends Object?>({
    R Function()? loading,
    R Function(ResultError<T, E> error)? error,
    R Function(ResultData<T, E> data)? data,
    required R Function(Result<T, E> result) orElse,
  }) {
    final result = this;
    if (result is ResultLoading<T, E>) {
      return loading != null ? loading() : orElse(result);
    } else if (result is ResultError<T, E>) {
      return error != null ? error(result) : orElse(result);
    } else if (result is ResultData<T, E>) {
      return data != null ? data(result) : orElse(result);
    } else {
      return orElse(result);
    }
  }
//...

Instead of functions, I think the getters are better for cleaner code syntax. Example:

  bool get isLoading => this is ResultLoading<T>;
  bool get isError => this is ResultError<T>;
  bool get isData => this is ResultData<T>;

  ResultLoading<T> get asLoading => this as ResultLoading<T>;
  ResultError<T> get asError => this as ResultError<T>;
  ResultData<T> get asData => this as ResultData<T>;

  ResultLoading<T>? get asLoadingOrNull {
    final result = this;
    return result is ResultLoading<T> ? result : null;
  }

  ResultError<T>? get asErrorOrNull {
    final result = this;
    return result is ResultError<T> ? result : null;
  }

  ResultData<T>? get asDataOrNull {
    final result = this;
    return result is ResultData<T> ? result : null;
  }

and then you can use it.

final value = data.asData.value;
// Instead
final value = data.asData().value; 

Of course, it's a taste thing. It's just my opinion.

And Dart Sealed Class Generator needs updated analyzer to 1.7.0 => 2.0.0 it conflicts with other packages for example with most one popular Freezed or json_serializable. It would make easier life to config pubspec.yaml file for beginners ๐Ÿ˜ƒ

And factory constructors prefer declaring const constructors on @immutable classes.

Functional types are not supported as field types

here is the code for the sealed class

import 'package:flutter/material.dart';
import 'package:rekab_delivery/src/util/consumable.dart';
import 'package:rekab_delivery/src/util/typedefs.dart';
import 'package:rekab_delivery/src/widget/src/utils/message.dart';
import 'package:sealed_annotations/sealed_annotations.dart';

part 'login_state.sealed.dart';

@Sealed()
abstract class _LoginState {
  void initial();
  void loading();
  void success(List<String> countries);
  void error(Message msg);
  void invalidNumber();
  void retry(VoidCallback callback);
}

and here is the error i get when running build_runner

[INFO] Generating build script...
[INFO] Generating build script completed, took 675ms

[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 112ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 670ms

[INFO] Running build...
[INFO] 1.2s elapsed, 1/3 actions completed.
[INFO] 2.2s elapsed, 1/3 actions completed.
[INFO] 3.3s elapsed, 1/3 actions completed.
[INFO] 10.7s elapsed, 1/3 actions completed.
[INFO] 13.3s elapsed, 2/3 actions completed.
[INFO] 14.5s elapsed, 8/9 actions completed.
[SEVERE] sealed_generators:sealed_generators on lib/src/cubits/login/request/code/login_state.dart:

"SealedError{message={internal error},cause={check failed}}"
[INFO] Running build completed, took 14.6s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 58ms

[SEVERE] Failed after 14.7s
pub finished with exit code 1

I tried different things found out whenever I use the type VoidCallback or a generic type that has that type like Consumable<VoidCallback> as the method argument, code generation fails

Default values for fields

Add a capability to add default values for fields.
for example like this:

import 'package:sealed_annotations/sealed_annotations.dart';

part 'weather.sealed.dart';

@Sealed()
abstract class _Weather {
  void sunny();

  void rainy({int rain = 10});

  void windy({@WithDefault('1e10') double velocity, double? angle = 100.0});
}

there is the question that if it should support dynamic defaults as well ?!

Common fields

add capability to add common fields.
for example:

import 'package:sealed_annotations/sealed_annotations.dart';

part 'common.sealed.dart';

@Sealed()
abstract class _ApiError {
  String get message;

  String? get code;

  void internetError();

  void badRequest();
}

By now the best way to implement such a behavior is to use extension methods on a conventional seald.
like this:

import 'package:sealed_annotations/sealed_annotations.dart';

part 'sobhan.sealed.dart';

@Sealed()
abstract class _ApiError {
  void internetError(String message, String? code);

  void badRequest(String message, String? code);
}

extension on ApiError {
  String get message => map(
        internetError: (e) => e.message,
        badRequest: (e) => e.message,
      );

  String? get code => map(
        internetError: (e) => e.code,
        badRequest: (e) => e.code,
      );
}

or by casting to dynamic in extension function:

extension on ApiError {
  String get message => (this as dynamic).message as String;
 
  String? get code => (this as dynamic).code as String?;
}

also the question about default values remains,
for example consider internetError code is always null or any other value.

Breaks with analyzer 5.0.0

Running dart run build_runner build with a dependency on analyzer v5.0.0 gives the following errors. Dropping back to 4.7.0 works fine.

../../../../.pub-cache/hosted/pub.dartlang.org/sealed_generators-1.13.0/lib/src/manifest/manifest_reader_builder.dart:40:12: Error: The getter 'isEnum' isn't defined for the class 'ClassElement'.
 - 'ClassElement' is from 'package:analyzer/dart/element/element.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-5.0.0/lib/dart/element/element.dart').
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEnum'.
      !cls.isEnum && !cls.isMixin && !cls.isMixinApplication,
           ^^^^^^
../../../../.pub-cache/hosted/pub.dartlang.org/sealed_generators-1.13.0/lib/src/manifest/manifest_reader_builder.dart:40:27: Error: The getter 'isMixin' isn't defined for the class 'ClassElement'.
 - 'ClassElement' is from 'package:analyzer/dart/element/element.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-5.0.0/lib/dart/element/element.dart').
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isMixin'.
      !cls.isEnum && !cls.isMixin && !cls.isMixinApplication,
                          ^^^^^^^
../../../../.pub-cache/hosted/pub.dartlang.org/sealed_generators-1.13.0/lib/src/manifest/manifest_reader.dart:148:28: Error: The getter 'element' isn't defined for the class 'DartType'.
 - 'DartType' is from 'package:analyzer/dart/element/type.dart' ('../../../../.pub-cache/hosted/pub.dartlang.org/analyzer-5.0.0/lib/dart/element/type.dart').
Try correcting the name to the name of an existing getter, or defining a getter or field named 'element'.
    final reference = type.element;
                           ^^^^^^^

How can I convert super enums to this package

I have this super_enum and the advise was to convert this to this package.
I am wondering how all this will convert?
Any help is appreciated...

import 'package:super_enum/super_enum.dart';

part 'emission_test_bloc_state.super.dart';

@superEnum
enum _EmissionTestBlocState {
  @object
  Disconnected,
  @object
  NoVinFailure,

  @object
  NoInternet,

  @Data(fields: [
    DataField<String>('vin'),
  ])
  Waiting,
  @Data(fields: [
    DataField<String>('request'),
  ])
  RequestMILStatus,
  @Data(fields: [
    DataField<String>('request'),
  ])
  RequestDTC,
  @object
  RequestRedinessMonitor,
  @Data(fields: [
    DataField<dynamic>('dtcs'),
    DataField<dynamic>('status'),
  ])
  TestFaliure,
  @Data(fields: [
    DataField<String>('message'),
    DataField<dynamic>('status'),
  ])
  TestPassed,
  @Data(fields: [
    DataField<dynamic>('status'),
  ])
  NotCompleted,
  @Data(fields: [
    DataField<String>('request'),
  ])
  RequestClearDTC,
  @object
  ErrorCodesCleared,
}

copyWith generation

Generate copyWith method for sealed classes.

We should disallow generic type change in copy with method to make life easier.

For nullable types we have two approaches:

For example:

class Base{
void one(int? x);
}

Freezes way of implementation:

this will generate code which needs two levels of inheritance for each copy with method and is not very straight forward.

we have this:

/// @nodoc
abstract class $BaseCopyWith<$Res> {
  factory $BaseCopyWith(Base value, $Res Function(Base) then) =
      _$BaseCopyWithImpl<$Res>;
}

/// @nodoc
class _$BaseCopyWithImpl<$Res> implements $BaseCopyWith<$Res> {
  _$BaseCopyWithImpl(this._value, this._then);

  final Base _value;

  // ignore: unused_field
  final $Res Function(Base) _then;
}

then this:

/// @nodoc
abstract class _$OneCopyWith<$Res> {
  factory _$OneCopyWith(_One value, $Res Function(_One) then) =
      __$OneCopyWithImpl<$Res>;

  $Res call({int? x});
}

/// @nodoc
class __$OneCopyWithImpl<$Res> extends _$BaseCopyWithImpl<$Res>
    implements _$OneCopyWith<$Res> {
  __$OneCopyWithImpl(_One _value, $Res Function(_One) _then)
      : super(_value, (v) => _then(v as _One));

  @override
  _One get _value => super._value as _One;

  @override
  $Res call({
    Object? x = freezed,
  }) {
    return _then(_One(
      x: x == freezed
          ? _value.x
          : x // ignore: cast_nullable_to_non_nullable
              as int?,
    ));
  }
}

we have this:

@JsonKey(ignore: true)
  _$OneCopyWith<_One> get copyWith => throw _privateConstructorUsedError;

which will be overriden by:

  @JsonKey(ignore: true)
  @override
  _$OneCopyWith<_One> get copyWith =>
      __$OneCopyWithImpl<_One>(this, _$identity);

And using optionals:

class Optional<T> {
  final bool isValid;
  final T? _value;

  T get value => _value as T;

  const Optional()
      : isValid = false,
        _value = null;
  const Optional.value(this._value) : isValid = true;
}

class Person {
  final String? name;

  Person(this.name);

  Person copyWith({Optional<String?> name = const Optional()}) =>
      Person(name.isValid ? name.value : this.name);
}

But when you want to set to any thing you should use optional ...

Special thanks to @iamarnas for his recommendations.

Use manifest class constructor for common fields

Add ability to use manifest class constructor for common fields.

@Sealed()
abstract class _ApiError {
  _ApiError({String message, String? code,});

  void internetError();

  void badRequest();

  void internalError(Object? error);
}

Complexities arise when combining constructor defined common fields and member defined ones. Also this feature should default values (static and dynamic) and overridden dynamic types.

Special thanks to @iamarnas for his recommendations.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.