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

Fix Enum#to_s for private enum #9126

Merged
merged 5 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
75 changes: 75 additions & 0 deletions spec/std/enum_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,27 @@ enum SpecEnum2
FORTY_FOUR
end

private enum PrivateEnum
FOO = 0
BAR = 1
BAZ = 2
QUX = 0
end

@[Flags]
enum SpecEnumFlags
One
Two
Three
end

@[Flags]
private enum PrivateFlagsEnum
FOO
BAR
BAZ
end

enum SpecBigEnum : Int64
TooBig = 4294967296i64 # == 2**32
end
Expand All @@ -35,6 +49,15 @@ describe Enum do
SpecEnumFlags::All.to_s.should eq("One | Two | Three")
(SpecEnumFlags::One | SpecEnumFlags::Two).to_s.should eq("One | Two")
end

it "for private enum" do
PrivateEnum::FOO.to_s.should eq "FOO"
PrivateFlagsEnum::FOO.to_s.should eq "FOO"
PrivateEnum::QUX.to_s.should eq "FOO"
String.build {|io| PrivateEnum::FOO.to_s(io)}.should eq "FOO"
String.build {|io| PrivateFlagsEnum::FOO.to_s(io)}.should eq "FOO"
String.build {|io| (PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAZ).to_s(io)}.should eq "FOO | BAZ"
end
end

it "creates an enum instance from an auto-casted symbol (#8573)" do
Expand Down Expand Up @@ -95,6 +118,17 @@ describe Enum do
names.should eq([SpecEnumFlags::One, SpecEnumFlags::Three])
values.should eq([SpecEnumFlags::One.value, SpecEnumFlags::Three.value])
end

it "private enum" do
names = [] of PrivateFlagsEnum
values = [] of Int32
(PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAZ).each do |name, value|
names << name
values << value
end
names.should eq([PrivateFlagsEnum::FOO, PrivateFlagsEnum::BAZ])
values.should eq([PrivateFlagsEnum::FOO.value, PrivateFlagsEnum::BAZ.value])
end
end

describe "names" do
Expand Down Expand Up @@ -149,6 +183,10 @@ describe Enum do
SpecEnumFlags.from_value(3).should eq(SpecEnumFlags::One | SpecEnumFlags::Two)
expect_raises(Exception, "Unknown enum SpecEnumFlags value: 8") { SpecEnumFlags.from_value(8) }
end

it "for private enum" do
PrivateEnum.from_value(0).should eq (PrivateEnum::FOO)
end
end

describe "valid?" do
Expand Down Expand Up @@ -193,6 +231,10 @@ describe Enum do
SpecEnum2.parse("FortyFour").should eq(SpecEnum2::FORTY_FOUR)
SpecEnum2.parse("FORTYFOUR").should eq(SpecEnum2::FORTY_FOUR)
SpecEnum2.parse("fortyfour").should eq(SpecEnum2::FORTY_FOUR)

PrivateEnum.parse("FOO").should eq(PrivateEnum::FOO)
PrivateEnum.parse("BAR").should eq(PrivateEnum::BAR)
PrivateEnum.parse("QUX").should eq(PrivateEnum::QUX)
end

it "parses?" do
Expand All @@ -204,6 +246,26 @@ describe Enum do
SpecEnum::One.clone.should eq(SpecEnum::One)
end

describe ".flags" do
it "non-flags enum" do
SpecEnum.flags().should be_nil
SpecEnum.flags(One).should eq SpecEnum::One
SpecEnum.flags(One, Two).should eq SpecEnum::One | SpecEnum::Two
end

it "flags enum" do
SpecEnumFlags.flags().should be_nil
SpecEnumFlags.flags(One).should eq SpecEnumFlags::One
SpecEnumFlags.flags(One, Two).should eq SpecEnumFlags::One | SpecEnumFlags::Two
end

it "private flags enum" do
PrivateFlagsEnum.flags().should be_nil
PrivateFlagsEnum.flags(FOO).should eq PrivateFlagsEnum::FOO
PrivateFlagsEnum.flags(FOO, BAR).should eq PrivateFlagsEnum::FOO | PrivateFlagsEnum::BAR
end
end

describe "each" do
it "iterates each member" do
keys = [] of SpecEnum
Expand All @@ -230,6 +292,19 @@ describe Enum do
keys.should eq([SpecEnumFlags::One, SpecEnumFlags::Two, SpecEnumFlags::Three])
values.should eq([SpecEnumFlags::One.value, SpecEnumFlags::Two.value, SpecEnumFlags::Three.value])
end

it "iterates private enum members" do
keys = [] of PrivateEnum
values = [] of Int32

PrivateEnum.each do |key, value|
keys << key
values << value
end

keys.should eq([PrivateEnum::FOO, PrivateEnum::BAR, PrivateEnum::BAZ, PrivateEnum::QUX])
values.should eq([PrivateEnum::FOO.value, PrivateEnum::BAR.value, PrivateEnum::BAZ.value, PrivateEnum::QUX.value])
end
end

it "different enums classes not eq always" do
Expand Down
27 changes: 14 additions & 13 deletions src/enum.cr
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct Enum
found = false
{% for member in @type.constants %}
{% if member.stringify != "All" %}
if {{@type}}::{{member}}.value != 0 && value.bits_set? {{@type}}::{{member}}.value
if {{@type.constant(member)}} != 0 && value.bits_set? {{@type.constant(member)}}
io << " | " if found
io << {{member.stringify}}
found = true
Expand Down Expand Up @@ -139,14 +139,15 @@ struct Enum
{% if @type.has_attribute?("Flags") %}
String.build { |io| to_s(io) }
{% else %}
case value
{% for member in @type.constants %}
when {{@type}}::{{member}}.value
waj marked this conversation as resolved.
Show resolved Hide resolved
{{member.stringify}}
# Can't use `case` here because case with duplicate values do
# not compile, but enums can have duplicates (such as `enum Foo; FOO = 1; BAR = 1; end`).
{% for member, i in @type.constants %}
if value == {{@type.constant(member)}}
return {{member.stringify}}
end
{% end %}
else
value.to_s
end

value.to_s
{% end %}
end

Expand Down Expand Up @@ -307,8 +308,8 @@ struct Enum
return if value == 0
{% for member in @type.constants %}
{% if member.stringify != "All" %}
if includes?({{@type}}::{{member}})
yield {{@type}}::{{member}}, {{@type}}::{{member}}.value
if includes?(self.class.new({{@type.constant(member)}}))
yield self.class.new({{@type.constant(member)}}), {{@type.constant(member)}}
end
{% end %}
{% end %}
Expand Down Expand Up @@ -359,7 +360,7 @@ struct Enum
return new(value)
{% else %}
{% for member in @type.constants %}
return {{@type}}::{{member}} if {{@type}}::{{member}}.value == value
return new({{@type.constant(member)}}) if {{@type.constant(member)}} == value
{% end %}
{% end %}
nil
Expand Down Expand Up @@ -434,7 +435,7 @@ struct Enum
case string.camelcase.downcase
{% for member in @type.constants %}
when {{member.stringify.camelcase.downcase}}
{{@type}}::{{member}}
new({{@type.constant(member)}})
{% end %}
else
nil
Expand Down Expand Up @@ -470,7 +471,7 @@ struct Enum
def self.each
{% for member in @type.constants %}
{% unless @type.has_attribute?("Flags") && %w(none all).includes?(member.stringify.downcase) %}
yield {{@type}}::{{member}}, {{@type}}::{{member}}.value
yield new({{@type.constant(member)}}), {{@type.constant(member)}}
{% end %}
{% end %}
end
Expand Down