xsahil03x / super_enum Goto Github PK
View Code? Open in Web Editor NEWCreate super-powered dart enums similar to sealed classes in Kotlin
Home Page: https://pub.dev/packages/super_enum
License: MIT License
Create super-powered dart enums similar to sealed classes in Kotlin
Home Page: https://pub.dev/packages/super_enum
License: MIT License
Some issues which we are trying to solve ->
This will be a breaking
change and will be released as v1.0.0
Currently, enum values annotated with @Data()
generates a const immutable class. In order to call it a proper DataClass
, we also need to generate hashcode
, ==
, toString
and copyWith
functions for the generated class.
We can use Equatable for simplicity.
I would like to generate a super enum Type from a String.
I would expect something like:
MyEnum enum = MyEnum.fromString(String Name);
Actually, support for the roundtrip String conversion should be supported from and to String.
Currently, we don't have any option or property to mark a DataField
to be ignored from equatable props
. We should add a property for the same.
Thanks for this great work. I'm looking forward to using the required
property on DataField
s, but I see that's not released on DartPub yet.
eg :
abstract class LoginResponse {
// Not necessary when case classes are public,
// but I usually put factories and constants on the base class.
factory LoginResponse.success(authToken) = LoginSuccess;
static const invalidCredentials = const InvalidLoginCredentials();
static const noNetwork = const NoNetworkForLogin();
factory LoginResponse.unexpectedException(Exception exception) = UnexpectedLoginException;
}
so i'm getting these weird errors because i use whenPartial
in my bloc's mapEventToState
.
yield* event.whenPartial(
someEvent: (_) async* {
// some logic
return State.someState();
}
);
not sure why it's not working anymore
Latest update (0.4.0, released several hours ago) has a very bad bug.
Any data field with a capital 'T' in the name appears to break code generation, yielding the standard comment header and the single line:
/*State must be annotated with @generic*/
To reproduce:
The following works properly -- exactly as you would expect:
import 'package:super_enum/super_enum.dart';
part 'super_enum_sample.g.dart';
class SomeVee {
String data;
}
@superEnum
enum _StatesGood {
@Data(fields: [
DataField<SomeVee>('fieldName'),
])
State
}
The following fails, and is the same other than the type of field, now being SomeTee instead of SomeVee (T in the name vs no T in the name).
import 'package:super_enum/super_enum.dart';
part 'super_enum_sample.g.dart';
class SomeTee {
String data;
}
@superEnum
enum _StatesBad {
@Data(fields: [
DataField<SomeTee>('fieldName'),
])
State
}
The problem stems from line 377 of class_generator.dart
if (_classFields
.any((e) => type_processor.dataFieldType(e).contains('T'))) {
if (!isGeneric) {
throw InvalidGenerationSourceError(
'${field.name} must be annotated with @generic');
}
}
(Hairy debug session to find this... 'my T' was in a complex type using built values and I had a lot of suspicions to track down before I got to "does it include a capital T' ;-)
Love all the new features, thanks for all your hard work!
Given I have a super_enum definition like this:
@superEnum
enum _ContactUsAction {
CallPhone,
StartLiveChat
}
Then the generated when method looks like this:
R when<R>(
{@required
FutureOr<R> Function(CallPhone) callPhone,
@required
FutureOr<R> Function(StartLiveChat) startLiveChat}) {
...
could this possibly be simplified to:
R when<R>(
{@required
FutureOr<R> Function() callPhone,
@required
FutureOr<R> Function() startLiveChat}) {
...
As from my point of view, the event has no associated values and thus does not need to be passed into the when method.
Hey,
I really love your package ! Thanks a lot for putting so much effort into it !
I only have a super small suggestion for improvement:
You have const constructors but you don't use them in the factorys (for @object's).
For example:
@immutable
class Empty extends ExampleState {
const Empty._() : super(ExampleState .Empty);
factory Empty() {
_instance ??= Empty._(); // -> _instance ??= const Empty._()
return _instance;
}
static Empty _instance;
}
There is no problem in that, but my linting settings mark this as an warning :)
Sometimes I share classes between multiple superEnums. This is currently only possible by wrapping them each time I use them.
class ApiError { /*...*/ }
@superEnum
enum _ResultA {
@Data(fields: [
DataField('error', ApiError),
])
Error,
}
@superEnum
enum _ResultB {
@Data(fields: [
DataField('error', ApiError),
])
Error,
}
I'd prefer to use some classes directly without having them auto-generated and wrapped.
class ApiError { /*...*/ }
@superEnum
enum _ResultA {
@UserClass(ApiError)
Error,
}
@superEnum
enum _ResultB {
@UserClass(ApiError)
Error,
}
It would be very helpful when comments on the enum values would also be rendered to the generated source
@superEnum
enum _Result {
/// Comment #1
@generic
@Data(fields: [
DataField('data', Generic),
DataField('message', String),
])
Success,
@object
Error,
}
abstract class Result<T> extends Equatable {
const Result(this._type);
/// Comment #1
factory Result.success({@required T data, @required String message}) =
Success<T>;
// ...
}
/// Comment #1
class Success<T> extends Result<T> {
/// Comment #1
const Success({@required this.data, @required this.message})
: super(_Result.Success);
I'm trying to do this:
@superEnum
enum _SelectedContact {
@Data(fields: [DataField('user', User)])
InternalUser,
@Data(fields: [DataField('contact', Contact)])
AddressBook,
}
Where Contact is another super enum defined in a different file. When building this, the generated file only contains the following comment, and no code:
/*FormatException: Not an instance of Type.*/
Is reusing enums in other enums not allowed?
This is more of a question than anything else.
It would be really cool if we could define generic types like so:
@superEnum
enum _Result<T> {
@object
NotLoaded,
@object
Loading,
@object
Error,
@Data(fields: [
DataField('data', T),
])
Loaded,
}
This would be extremely useful as we could use the same _Result
type to handle multiple model classes.
However I assume this can't be done as Dart doesn't support enums with generics?
Would this be achievable by using standard classes?
Great library btw!
The generated code sometimes provokes the analyzer (under pedantic settings) to issue the warning: "DO use curly braces for all flow control structures".
It can be fixed either by generating braces when you have an if with a single line body (these single line bodies currently appear when you throw).
Or faster, you could take the approach that built_value does and just suppress warnings at the file level by generating the following line at the bottom of each .g.dart file:
// ignore_for_file: curly_braces_in_flow_control_structures
Details:
It happens sometimes where the generated code includes throws clauses that aren't surrounded by braces, e.g.:
//ignore: missing_return
FutureOr<R> when<R>(
{@required FutureOr<R> Function(UpdateFilter) updateFilter,
@required FutureOr<R> Function(UpdateFeatures) updateFeatures}) {
assert(() {
if (updateFilter == null || updateFeatures == null)
throw 'check for all possible cases';
return true;
}());
....
Here, it happens because throw 'check for all possible cases';
is formatted alone on a line.
It only causes linter warnings sometimes, because oddly, if the formatter keeps the throw on the same line as the if
or an ||
as shown below, there is no linter warning.
if (updateFilter == null || ... || ...
|| updateFeatures == null) throw 'check for all possible cases';
Why does the generator rename enum fields? For example, if I define the following enum
@superEnum
enum _Result {
@object
Success,
@object
Error
}
...Success
becomes success
and Error
becomes error
. Is there a reason for this? I think it would be better to keep the original names to avoid confusion.
Currently the generator adds "@required" to all DataField objects.
We should have a parameter to indicate if this is really required parameter, like:
DataField<String>("errorMessage", required: false),
This way, the library will be 100% ready for the NNBD next release of Dart language:
dart-lang/language#110
After upgrading packages, built_value_generator ^7.0.8 and super_enum_generator ^0.3.0 are conflicting on analyzer version:
Because super_enum_generator >=0.2.0+1 depends on analyzer >=0.37.1 <0.39.1 and
built_value_generator >=7.0.8 depends on analyzer ^0.39.3, super_enum_generator >=0.2.0+1
is incompatible with built_value_generator >=7.0.8.
pub upgrade failed (1; So, because ___ depends on both built_value_generator ^7.0.8
and super_enum_generator ^0.3.0, version solving failed.)
the "extension"s has been added since Dart 2.6 so it would be great if we have Enums with default values. for example:
enum Country {
UnitedArabEmirates,
Argentina,
Austria,
Australia,
Belgium,
Bulgaria,
Brazil,
}
extension CountryExtension on Country {
String getValue() {
switch (this) {
case Country.UnitedArabEmirates:
return "ae";
case Country.Argentina:
return "ar";
case Country.Austria:
return "at";
case Country.Australia:
return "au";
case Country.Belgium:
return "be";
case Country.Bulgaria:
return "bg";
case Country.Brazil:
return "br";
default:
return "COUNTRY NOT FOUND";
}
}
}
main(){
var au = Country.Australia;
print('the country code of Australia is ${au}');
}
as I represented, we can do it our selves but it would be nice if we can define that like this:
@superEnum
enum Country {
@object(value: "ae")
UnitedArabEmirates,
@object(value: "ar")
Argentina,
@object(value: "at")
Austria,
@object(value: "au")
Australia,
@object(value: "be")
Belgium,
@object(value: "bg")
Bulgaria,
@object(value: "br")
Brazil,
}
main(){
var au = Country.Australia;
print('the country code of Australia is ${au.value}');
}
Thnaks,
I would like to keep some of the types generated by the super enum being private and not to expose some bloc events to the outside rather keeping them private to the package.
Should be pretty simple to generate those types with an understood like _SomeTyperGenerated. Well seems like _SomeTyperGenerated is only private in the own file. Wondering what else I could do.
Hi,
Do you have any plan to release null safe version of the library ?
Thanks.
I do get a linter error 'return_of_invalid_type' in the generated R when<R>
, R whenOrElse<R>
etc. methods. you are returning a FutureOr and not R.
(Flutter (Channel beta, v1.14.6, on Mac OS X 10.15.3 19D76, locale de-DE))
Hey guys,
I just ran into another code generation error.
When using the same Class in @useClass() the build_runner generates the wrapper twice.
For example this:
@superEnum
enum _RevenueState {
@object
Empty,
@UseClass(RevenueLoaded)
YearLoaded,
@UseClass(RevenueLoaded)
MonthLoaded,
@Data(fields: [DataField<String>('message')])
Error,
}
class RevenueLoaded {
final Revenue revenue;
const RevenueLoaded({
@required this.revenue,
});
}
Generates the RevenueLoadedwWrapper twice
Have a nice day !
When using generics, I am getting a type casting exceptions given the following example
@superEnum
enum _ModelState {
@generic
@Data(fields: [DataField('data', Generic)])
Success,
@object
Error,
@object
Loading,
@object
Empty,
}
//Somewhere else in code
Stream<ModelState<int>> getSomething() {
return Stream.fromFuture(Future.value(1))
.publishReplay(maxSize: 1)
.refCount()
.map((data) => ModelState.loading());
}
The error:
Unhandled Exception: type 'Loading<dynamic>' is not a subtype of type 'Loading<int>'
Environment:
Flutter 1.12.13+hotfix.6
Framework • revision 18cd7a3601 (7 days ago) • 2019-12-11 06:35:39 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0
Dependencies:
dependencies:
super_enum: ^0.2.0
dev_dependencies:
super_enum_generator: ^0.2.0+1
build_runner: ^1.7.1
The current version of super enum will not work with dart 2.10, analyzer 0.40.1, and build_runner 2.10.3 because there is a breaking change on usedClass.toTypeValue().getDisplayString()
API which needs the withNullability
to pass.
Also, overriding analyzer version will not help.
Related Issues:
As object classes do not contain any fields, they should be converted into the singleton classes.
such that print(movie.copyWith(name: null))
should print Movie(name:null)
The update to 0.3 modified when() and its new sibling methods to return FutureOr.
The Effective Dart advice is to use FutureOr as a parameter, but not to return it. They advise instead to just return Future, as it will be more clear to users (who need to await the result either way).
https://dart.dev/guides/language/effective-dart/design#avoid-using-futureort-as-a-return-type
So the generator would produce
Future<R> when<R>(
{@required FutureOr<R> Function(Success) success,
@required FutureOr<R> Function(Error) error}) async {
}
with Future / async instead of FutureOr
FutureOr<R> when<R>(
{@required FutureOr<R> Function(Success) success,
@required FutureOr<R> Function(Error) error}) {
}
(That may also save you from having to document how to use the return types, as many of your users won't have experience dealing with a FutureOr return.)
Thanks for the new whenXXX methods, and for fixing the generation of null props for @objects
!
I'm running on a project that uses super_enum, along with other codegen packages.
Relevant portion of my pubspec.yaml
:
environment:
sdk: ">=2.5.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
json_annotation: any
provider: any
http: any
logger: any
intl: any
super_enum: any
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: any
json_serializable: any
super_enum_generator: any
Output of flutter packages get
:
[weather_app_flutter] flutter packages get
Running "flutter pub get" in weather_app_flutter...
The current Dart SDK version is 2.5.0.
Because json_serializable >=2.0.1 <2.0.2 depends on analyzer >=0.32.2 <0.35.0 and json_serializable >=1.5.1 <2.0.1 depends on analyzer >=0.32.2 <0.34.0, json_serializable >=1.5.1 <2.0.2 requires analyzer >=0.32.2 <0.35.0.
And because json_serializable >=1.0.0 <1.5.1 depends on analyzer ^0.32.2 and json_serializable >=0.5.8+1 <1.4.0 depends on build ^0.12.6, json_serializable >=0.5.8+1 <2.0.2 requires analyzer >=0.32.2 <0.35.0 or build ^0.12.6.
And because every version of super_enum_generator depends on build ^1.2.1 and json_serializable >=3.2.1 <3.2.3 depends on analyzer >=0.33.3 <0.39.0, if super_enum_generator any and json_serializable >=0.5.8+1 <2.0.2-∞ or >=3.2.1 <3.2.3-∞ then analyzer >=0.32.2 <0.39.0.
And because json_serializable >=3.1.0 <3.2.1 depends on analyzer >=0.33.3 <0.38.0 and json_serializable >=2.1.1 <3.1.0 depends on analyzer >=0.33.3 <0.37.0, if super_enum_generator any and json_serializable >=0.5.8+1 <2.0.2-∞ or >=2.1.1 <3.2.3-∞ then analyzer >=0.32.2 <0.39.0.
And because json_serializable >=2.0.2 <2.1.1 depends on analyzer >=0.33.3 <0.36.0 and json_serializable <=0.5.8 requires SDK version >=1.22.1 <2.0.0-∞, if super_enum_generator any and json_serializable <3.2.3 then analyzer >=0.32.2 <0.39.0.
And because no versions of json_serializable match >3.2.3 and every version of super_enum_generator depends on analyzer ^0.39.1, super_enum_generator is incompatible with json_serializable.
So, because weather_app_flutter depends on both json_serializable any and super_enum_generator any, version solving failed.
pub get failed (1)
exit code 1
Output of flutter doctor
:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.9.1+hotfix.4, on Mac OS X 10.14.4 18E226, locale en-GB)
[!] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 11.1)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.40.2)
[✓] Connected device (1 available)
! Doctor found issues in 1 category.
Any guidance on how to solve the incompatibilities issues?
A consequence of generating List get props => null;
is that (with the current Equatable package: equatable 1.0.1), calling hashCode on a generated @object throws NoSuchMethodError.
To reproduce:
import 'package:super_enum/super_enum.dart';
part "my_state.g.dart";
@superEnum
enum _MyState {
@object
Success,
@object
Error,
}
...
// any running code
var success = MyState.success();
success.hashCode;
BOOM!
NoSuchMethodError: The method 'fold' was called on null.
Receiver: null
Tried calling: fold(0, Closure: (int, dynamic) => i
I got bit pretty hard by this tonight, in test code checking emitsInOrder on a StreamController, but hashCode is called in plenty of situations.
The bug arises because the generator produces
List get props => null;
Instead the generator needs to produce:
List get props => const [];
I see you have an outstanding pull request on an unrelated issue that silently fixes this: #23
(small nit: that fix assigns props []
and not const []
).
Not knowing the timeline for that PR, I wanted to raise this as a separate (and urgent) issue.
Would like the ability to specify the name to use when computing sealed class. This would remove collisions when creating similarly named classes. See the following example taken from sealed_unions_generator
@Seal('WeatherState')
abstract class WeatherModel {
@Sealed()
void initial();
@Sealed()
void loading();
@Sealed()
void loaded(int temperature);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'weather_model.dart';
// **************************************************************************
// SealedUnionsGenerator
// **************************************************************************
class WeatherState extends Union3Impl<WeatherStateInitial, WeatherStateLoading,
WeatherStateLoaded> {
static final Triplet<WeatherStateInitial, WeatherStateLoading,
WeatherStateLoaded> _factory =
const Triplet<WeatherStateInitial, WeatherStateLoading,
WeatherStateLoaded>();
WeatherState._(
Union3<WeatherStateInitial, WeatherStateLoading, WeatherStateLoaded>
union)
: super(union);
factory WeatherState.initial() =>
WeatherState._(_factory.first(WeatherStateInitial()));
factory WeatherState.loading() =>
WeatherState._(_factory.second(WeatherStateLoading()));
factory WeatherState.loaded(int temperature) =>
WeatherState._(_factory.third(WeatherStateLoaded(temperature)));
}
class WeatherStateInitial {
WeatherStateInitial();
}
class WeatherStateLoading {
WeatherStateLoading();
}
class WeatherStateLoaded {
final int temperature;
WeatherStateLoaded(this.temperature);
}
Hello, Is there a way to override == data classes?
I have trouble using a specific type of generic type in DataField. Let's take List<int>
for example.
@superEnum
class _Event {
@Data(fields: [DataField('foo', List<int>)])
Foo,
}
The above code won't compile, with following error message:
error • In constant expressions, operands of this operator must be of type 'num' •
error • The operator '<' isn't defined for the class 'Type' •
error • A comparison expression can't be an operand of another comparison expression •
error • Arguments of a constant creation must be constant expressions •
...
Seems like dart compiler takes <>
as two separate part. With some hard trying, I changed the code to
@superEnum
enum _Event {
@Data(fields: [DataField('foo', <int>[].runtimeType)])
Foo,
}
That was much better, with the following error message though:
error • Arguments of a constant creation must be constant expressions •
error • The values in a const list literal must be constants •
BTW, the following code do compile without using super_enum:
class Data {
final List<DataField> fields;
const Data({this.fields});
}
class DataField {
final String name;
final Type type;
const DataField(this.name, this.type);
}
void main() {
print(Data(fields: [DataField('scanResult', <String>[].runtimeType)]));
}
VS Studio for some reason is not detecting the build task runner. Could be something wrong with the library or my version of VS Studio(I updated to the latest version)?
Currently whenPartial only generates with a 'Future' return type, whereas its counterparts whenOrElse or when generate a normal R return type which is much more useful
For example if you have enums like [Success, Error] and you only want to do something if it is an [Error], you have to use when and leave the success empty which can get ugly when there are many enums.
I think it would be good to generate functions like whenSuccess, whenError to deal with that, instead of having to import generated files.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.