diff --git a/spec/std/enum_spec.cr b/spec/std/enum_spec.cr index 284af9619797..9495303edee8 100644 --- a/spec/std/enum_spec.cr +++ b/spec/std/enum_spec.cr @@ -11,6 +11,13 @@ enum SpecEnum2 FORTY_FOUR end +private enum PrivateEnum + FOO = 0 + BAR = 1 + BAZ = 2 + QUX = 0 +end + @[Flags] enum SpecEnumFlags One @@ -18,6 +25,13 @@ enum SpecEnumFlags Three end +@[Flags] +private enum PrivateFlagsEnum + FOO + BAR + BAZ +end + enum SpecBigEnum : Int64 TooBig = 4294967296i64 # == 2**32 end @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/src/enum.cr b/src/enum.cr index c47fc75a5db9..a8716d686aa5 100644 --- a/src/enum.cr +++ b/src/enum.cr @@ -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 @@ -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 - {{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 @@ -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 %} @@ -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 @@ -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 @@ -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