const std = @import("std");
const expect = std.testing.expect;
const eql = std.mem.eql;

// Declare an enum.
const Type = enum { ok, not_ok };

// Declare a specific instance of the enum variant.
const c = Type.ok;

test "enum ordinal value" {
    // If you want access to the ordinal value of an enum,
    // you can specify the tag type.
    const Value = enum(u2) { zero, one, two };
    const Value1 = enum { zero, one, two };

    // Now you can cast between u2 and Value.
    // The ordinal value starts from 0, counting up for each member.
    expect(@enumToInt(Value.zero) == 0);
    expect(@enumToInt(Value.one) == 1);
    expect(@enumToInt(Value.two) == 2);

    // Tag type needs not be explicitly specified:
    expect(@enumToInt(Value1.zero) == 0);
    expect(@enumToInt(Value1.one) == 1);
    expect(@enumToInt(Value1.two) == 2);
}

// You can override the ordinal value for an enum.
const Value2 = enum(u32) { hundred = 100, thousand = 1000, million = 1000000 };
test "set enum ordinal value" {
    expect(@enumToInt(Value2.hundred) == 100);
    expect(@enumToInt(Value2.thousand) == 1000);
    expect(@enumToInt(Value2.million) == 1000000);
}

// Enums can have methods, the same as structs and unions.
// Enum methods are not special, they are only namespaced
// functions that you can call with dot syntax.
const Suit = enum {
    clubs,
    spades,
    diamonds,
    hearts,

    pub fn isClubs(self: Suit) bool {
        return self == Suit.clubs;
    }
};
test "enum method" {
    const p = Suit.spades;
    expect(!p.isClubs());
}

// An enum variant of different types can be switched upon.
const Foo = enum {
    string,
    number,
    none,
};
test "enum variant switch" {
    const p = Foo.number;
    const what_is_it = switch (p) {
        Foo.string => "this is a string",
        Foo.number => "this is a number",
        Foo.none => "this is a none",
    };
    expect(eql(u8, what_is_it, "this is a number"));
}

// @TagType can be used to access the integer tag type of an enum.
const Small = enum { one, two, three, four };
test "@TagType" {
    expect(@TagType(Small) == u2);
}

// @typeInfo tells us the field count and the fields names:
test "@typeInfo" {
    expect(@typeInfo(Small).Enum.fields.len == 4);
    expect(eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
}

// @tagName gives a []const u8 representation of an enum value:
test "@tagName" {
    expect(eql(u8, @tagName(Small.three), "three"));
}