I'm trying to create a macro to help with some boilerplate enum code that I've been repetitively writing. I managed to implement a simple enum (i.e. no arguments) relatively easily using a basic macro_rule. e.g. An excerpt:
macro_rules! enum_helper {
($type:ident, { $( $name:ident ), * }) => {
enum $type {
$(
$name,
)+
}
impl FromSql for $type {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<&type> {
// ... the implementation
}
// ... plus some other internal traits being implemented
}
}
enum_helper!(Step, {
Step1,
Step2
});
I was hoping to extend this macro to also support a mixed set of enum styles, primarily with only one typed argument e.g.
enum_helper!(Step, {
Step1,
Step2,
Step3(i64)
});
Is there a way to represent this "optional" argument using macro_rules? I suspect it involves using a tt however I'm still a bit lost with how tt works in a subnested environment.
Update 1
I'm using $name within some of the trait implementations for pattern matching. For example, I have some code similar to below:
match raw {
$(
$type::$name => { /* a result of some sort */ },
)+
}
Related
I encountered this snippet in some example code. It works fine, but I got a linter error saying that it should be structured as an if-let statement.
match event {
glutin::Event::WindowEvent { event, .. } => match event {
glutin::WindowEvent::Closed => return glutin::ControlFlow::Break,
glutin::WindowEvent::Resized(w, h) => gl_window.resize(w, h),
_ => (),
},
_ => ()
}
This was my attempt to restructure it:
if let _ = glutin::Event::WindowEvent { event, .. } {
match event {
glutin::WindowEvent::Closed => return glutin::ControlFlow::Break,
glutin::WindowEvent::Resized(w, h) => gl_window.resize(w, h),
_ => (),
}
}
Oops, that's a syntax error. What would be the correct way to clear the linter warning?
After looking at the code more closely, I realized that I don't understand the syntax. glutin::Event::WindowEvent { event, .. } looks like the syntax for creating a new instance of WindowEvent but how can that be allowed inside a match statement?
Also, what does the .. mean? I'm familiar with ..Default::default(), but not the double dot by itself.
The syntax that eludes you is called destructuring.
This pattern allows to match certain fields in a struct, enum, or tuple. You therefore cannot just use if let with the destructuring on the right side of the binding.
The code you want is probably:
if let glutin::Event::WindowEvent { event, .. } = event {
match event {
glutin::WindowEvent::Closed => return glutin::ControlFlow::Break,
glutin::WindowEvent::Resized(w, h) => gl_window.resize(w, h),
_ => (),
}
}
There is a possible confusion between the right hand event variable and the one extracted from the pattern.
The use of event in the destructuring is made mandatory because it needs to use struct fields by name.
Directly quoting from The Book, second edition:
The if let syntax lets you combine if and let into a less verbose way to handle values that match one pattern and ignore the rest.
It also provides this example:
if let Some(3) = some_u8_value {
println!("three");
}
The correct syntax is if let «pattern» = «expression» { ... }, and not the opposite written in the question.
if let glutin::Event::WindowEvent { event, .. } = event {
// ...
}
Looking for the proper way to do
if(self.MyProperty) { /* ... */ }
error: attempted access of field MyProperty on type MyType, but no field with that name was found
or
if(self.MyMethod){ /* ... */ }
error: attempted to take value of method MyMethod on type MyType
As a last resort, at least how does one check if a Trait is Implemented?
This concept doesn't exist in Rust. While there is some limited downcast capability through Any, this should be used as a last resort. What you should do is create a new trait that exposes all of these decisions for you.
Reusing your example of a my_method method:
trait YourTrait {
fn try_my_method(&self, arg: SomeArg) -> Option<MyMethodResult> {
None
}
}
impl YourTrait for SomeType {
fn try_my_method(&self, arg: SomeArg) -> Option<MyMethodResult> {
Some(self.my_method(arg))
}
}
In your code you can then call
if let Some(result) = self.try_my_method() {
/* ... */
}
Is there a way to convert a string to an enum?
enum eCommand{fred, joe, harry}
eCommand theCommand= cast(eCommand, 'joe');??
I think I just have to search for the enum (eg loop).
cheers
Steve
I got annoyed with the state of enums and built a library to handle this:
https://pub.dev/packages/enum_to_string
Basic usage:
import 'package:enum_to_string:enum_to_string.dart';
enum TestEnum { testValue1 };
main(){
final result = EnumToString.fromString(TestEnum.values, "testValue1");
// TestEnum.testValue1
}
Still more verbose than I would like, but gets the job done.
I have came up with a solution inspired from https://pub.dev/packages/enum_to_string that can be used as a simple extension on List
extension EnumTransform on List {
String string<T>(T value) {
if (value == null || (isEmpty)) return null;
var occurence = singleWhere(
(enumItem) => enumItem.toString() == value.toString(),
orElse: () => null);
if (occurence == null) return null;
return occurence.toString().split('.').last;
}
T enumFromString<T>(String value) {
return firstWhere((type) => type.toString().split('.').last == value,
orElse: () => null);
}
}
Usage
enum enum Color {
red,
green,
blue,
}
var colorEnum = Color.values.enumFromString('red');
var colorString: Color.values.string(Color.red)
Dart 2.6 introduces methods on enum types. It's much better to call a getter on the Topic or String itself to get the corresponding conversion via a named extension. I prefer this technique because I don't need to import a new package, saving memory and solving the problem with OOP.
I also get a compiler warning when I update the enum with more cases when I don't use default, since I am not handling the switch exhaustively.
Here's an example of that:
enum Topic { none, computing, general }
extension TopicString on String {
Topic get topic {
switch (this) {
case 'computing':
return Topic.computing;
case 'general':
return Topic.general;
case 'none':
return Topic.none;
}
}
}
extension TopicExtension on Topic {
String get string {
switch (this) {
case Topic.computing:
return 'computing';
case Topic.general:
return 'general';
case Topic.none:
return 'none';
}
}
}
This is really easy to use and understand, since I don't create any extra classes for conversion:
var topic = Topic.none;
final string = topic.string;
topic = string.topic;
(-:
As of Dart 2.15, you can use the name property and the byName() method:
enum eCommand { fred, joe, harry }
eCommand comm = eCommand.fred;
assert(comm.name == "fred");
assert(eCommand.values.byName("fred") == comm);
Just be aware that the byName method throws an ArgumentError if the string is not recognized as a valid member in the enumeration.
For the Dart enum this is a bit cumbersome.
See Enum from String for a solution.
If you need more than the most basic features of an enum it's usually better to use old-style enums - a class with const members.
See How can I build an enum with Dart? for old-style enums
This is the fastest way I found to do this :
enum Vegetable { EGGPLANT, CARROT, TOMATO }
Vegetable _getVegetableEnum(dynamic myVegetableObject) {
return Vegetable.values.firstWhere((e) => describeEnum(e) == myVegetableObject);
}
i had the same problem, a small enum and a string, so i did this
dynamic _enum_value = EnumFromString(MyEnum.values, string_to_enum);
dynamic EnumFromString(List values, String comp){
dynamic enumValue = null;
values.forEach((item) {
if(item.toString() == comp){
enumValue = item;
}
});
return enumValue;
}
i know this is not the best solution, but it works.
Static extension methods would make this nice (currently unimplemented).
Then you could have something like this:
extension Value on Keyword {
/// Returns the valid string representation of a [Keyword].
String value() => toString().replaceFirst(r'Keyword.$', '');
/// Returns a [Keyword] for a valid string representation, such as "if" or "class".
static Keyword toEnum(String asString) =>
Keyword.values.firstWhere((kw) => kw.value() == asString);
}
FOUND ANS
Check this article. Very well explained: Link
extension EnumParser on String {
T toEnum<T>(List<T> values) {
return values.firstWhere(
(e) => e.toString().toLowerCase().split(".").last ==
'$this'.toLowerCase(),
orElse: () => null,
),
}
}
The cleanest way is to use built-in functionality:
enum EmailStatus {
accepted,
rejected,
delivered,
failed,
}
EmailStatus.values.byName('failed') == EmailStatus.failed;
=> true
Is there a way to use enum default parameters in Haxe? I get this error:
Parameter default value should be constant
enum AnEnum {
A;
B;
C;
}
class Test {
static function main() {
Test.enumNotWorking();
}
static function enumNotWorking(e:AnEnum = AnEnum.A){}
}
Try Haxe link.
Update: this feature has been added in Haxe 4. The code example from the question now compiles as-is with a regular enum.
Previously, this was only possible if you're willing to use enum abstracts (enums at compile time, but a different type at runtime):
#:enum
abstract AnEnum(Int)
{
var A = 1;
var B = 2;
var C = 3;
}
class Test3
{
static function main()
{
nowItWorks();
}
static function nowItWorks(param = AnEnum.A)
{
trace(param);
}
}
There's nothing special about the values I chose, and you could choose another type (string, or a more complex type) if it better suits your use case. You can treat these just like regular enums (for switch statements, etc.) but note that when you trace it at runtime, you'll get "1", not "A".
More information: http://haxe.org/manual/types-abstract-enum.html
Sadly enums can't be used as default values, because in Haxe enums aren't always constant.
This piece of trivia was on the old website but apparently hasn't made it into the new manual yet:
http://old.haxe.org/ref/enums#using-enums-as-default-value-for-parameters
The workaround is to check for a null value at the start of your function:
static function enumNotWorking(?e:AnEnum){
if (e==null) e=AnEnum.A;
}
Alternatively, an Enum Abstract might work for your case.
I would like to define an enum-like structure in JS, but have two requirements:
The values be read-only, i.e. no users can assign to them.
The values (0, 1, 2, ...) can be mapped back into the names (as with Java's name method)
The methods I know to create enums like this typically meet one requirement or the other, not both.
I've tried:
const MyEnum = {
a: 0,
b: 1,
c: 2
};
The enum itself is constant, but the values are still mutable and I can't map values back to names efficiently.
When writing an enum in Typescript, it outputs:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["a"] = 0] = "a";
MyEnum[MyEnum["b"] = 1] = "b";
MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));
This can map both ways, but still doesn't have constant values.
The only option I've found that meets both requirements would be using getters on a class:
class MyEnum {
get a() {
return 0;
}
...
}
This method dramatically restricts the legal names and has a lot of overhead, especially in browsers that don't inline getters well (or can't).
#Shmiddty suggested freezing an object:
const MyEnum = Object.freeze({
a: 0,
b: 1,
c: 2
});
This meets the constant requirement well, but doesn't provide a great way to map values back to names.
I could write a helper that builds the reverse mapping like:
function reverseEnum(enum) {
Object.keys(enum).forEach(k => {
enum[enum[k]] = k;
});
}
But any kind of programmatic solution to generate the reverse mapping will run into problems if the original object is frozen or otherwise actually constant.
Is there a clean, concise solution to this in JS?
This does a pretty good job, IMHO.
function Enum(a){
let i = Object
.keys(a)
.reduce((o,k)=>(o[a[k]]=k,o),{});
return Object.freeze(
Object.keys(a).reduce(
(o,k)=>(o[k]=a[k],o), v=>i[v]
)
);
} // y u so terse?
const FOO = Enum({
a: 0,
b: 1,
c: "banana"
});
console.log(FOO.a, FOO.b, FOO.c); // 0 1 banana
console.log(FOO(0), FOO(1), FOO("banana")); // a b c
try {
FOO.a = "nope";
}
catch (e){
console.log(e);
}
I'd use a Map so that your enum values can be any type, rather than having them coerced into strings.
function Enum(obj){
const keysByValue = new Map();
const EnumLookup = value => keysByValue.get(value);
for (const key of Object.keys(obj)){
EnumLookup[key] = obj[key];
keysByValue.set(EnumLookup[key], key);
}
// Return a function with all your enum properties attached.
// Calling the function with the value will return the key.
return Object.freeze(EnumLookup);
}
If your enum is all strings, I'd also probably change one line to:
EnumLookup[key] = Symbol(obj[key]);
to ensure that the enum values are being used properly. Using just a string, you have no guarantee that some code hasn't simply passed a normal string that happens to be the same as one of your enum values. If your values are always strings or symbols, you could also swap out the Map for a simple object.
Just recently implemented an Es6 version that works quite well:
const k_VALUES = {}
export class ErrorCode {
constructor(p_apiCode, p_httpCode){
this.apiCode = p_apiCode;
this.httpCode = p_httpCode;
k_VALUES[p_apiCode] = this;
}
static create(p_apiCode){
if(k_VALUES[p_apiCode]){
return k_VALUES[p_apiCode];
}
return ErrorCode.UNKNOWN;
}
}
ErrorCode.UNKNOWN = new ErrorCode(0, 500);
ErrorCode.NOT_FOUND = new ErrorCode(-1000, 404);
ErrorCode.NOT_FOUND_EMAIL = new ErrorCode(-1001, 404);
ErrorCode.BAD_REQUEST = new ErrorCode(-1010, 404);
I wanted to implement a similar pattern as what we do with Java enums. This enables me to use a constructor to pass values. The constructor then freezes the ErrorCode object - nice and convenient.
Usage: first import your enum class...
import {ErrorCode} from "../common/services/errors/ErrorCode";
Now, after importing the enum class, access it like so:
if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}
PS> This is used in conjunction with a Webpack setup using Babel to convert our ES6 classes down for browser compatibility.