Export enum

Hi

I am defining an enum such as @enum myenum enumname1 enumname2 in a module using Julia 0.6. it seems I have to explicitly export both enum type name(myenum) and enum elements(enumname1,enumname2). I know this way is more explicit, but usually we want all elements of an enum type be exported too. is there a way to just do export myenum so that type name and all its elements be exported, or should this behavior be the default for export enum?

Alex

Here’s a macro that does it:

macro exported_enum(name, args...)
    esc(quote
        @enum($name, $(args...))
        export $name
        $([:(export $arg) for arg in args]...)
        end)
end

@exported_enum fruit apple banana

this macro is great, should be included in the base. thanks sharing.

In order to deal with the case where you specify the value of each instances, you may use:

julia> module A
           macro exported_enum(T, syms...)
               return esc(quote
                   @enum($T, $(syms...))
                   export $T
                   for inst in Symbol.(instances($T))
                       eval($(Expr(:quote, :(export $(Expr(:$, :inst))))))
                   end
               end)
           end
           @exported_enum fruit apple=2 banana=3
       end
Main.A

julia> using Main.A

julia> fruit
Enum fruit:
apple = 2
banana = 3

sorry for reviving this.
I got the macro to to support a begin end block (with comments)
(I had some help from the ai, as macro definition syntax is a bit of a black art)
eg.

@exported_enum MyEnum begin
    #this is a
    A
    #this is b
    B
end

macro exported_enum(name, args...)
    if length(args) == 1 && args[1] isa Expr && args[1].head == :block
        # Handle the begin ... end block syntax
        block = args[1] 
        # Filter out comments from the block
        enum_members = filter(x -> x isa Symbol, block.args)
        return esc(quote
            @enum $name begin
                $(enum_members...)
            end
            export $name
            $([:(export $arg) for arg in enum_members]...)
        end)
    else
        # Handle the standard @enum syntax
        return esc(quote
            @enum($name, $(args...))
            export $name
            $([:(export $arg) for arg in args]...)
        end)
    end
end

Still this only working for symbols, not expression. Gemini gave me out this. Don’t know how correct it is, for me is working:

"""
    @export_enum_members(EnumType)

Exports all instances (members) of a previously defined enum type from the current module.

This macro automates the process of writing individual `export` statements for each
member of an enum. It is particularly useful for enums with many members or for
maintaining a clean separation between definition and export logic.

Note: This macro only exports the enum's instances; it does **not** export the enum
type itself.

# Usage

First, define your enum as you normally would. Then, call the macro with the name
of the enum type.

```julia
# 1. Define the enum
@enum MyColor begin
    RED
    GREEN
    BLUE
end

# 2. Export only its members using the macro
@export_enum_members MyColor
```

# Examples

#### Standard Use Case:

This is the intended use of the macro.

```julia
module MyDrawingApp

# Define the enum internally
@enum ToolType begin
    PENCIL
    BRUSH
    ERASER
end

# Make the tools available to users, but keep the `ToolType` abstract.
# The user can use `PENCIL`, but not necessarily the type `ToolType`.
@export_enum_members ToolType

end
```

The macro call `@export_enum_members ToolType` automatically expands to the
following code during compilation:
```julia
export PENCIL
export BRUSH
export ERASER
```

#### Comparison with `@exported_enum`:

This macro differs from a combined-definition macro like `@exported_enum` in that
it provides more control by decoupling definition from exportation.

- Use `@exported_enum` when you always want to define and export an enum and all its members in one step.
- Use `@export_enum_members` when you have an existing enum and only want to export its members, potentially keeping the enum type itself private to the module.

# See Also
- `exported_enum`
- `@enum`: The built-in macro for creating enumerations in Julia.
- `export`: The keyword for defining a module's public API.
"""
macro export_enum_members(enum_type)
    # This macro must be called after the enum is defined.
    # We evaluate the enum_type symbol in the context of the module where
    # the macro is called (__module__) to get the actual type object.
    try
        type_obj = Core.eval(__module__, enum_type)

        # Check that we have a valid Enum
        if !(type_obj isa DataType && supertype(type_obj) <: Enum)
            return :(error($(string(enum_type)) * " is not a valid Enum type."))
        end

        # Get the list of instances (e.g., [PENCIL, BRUSH, ...])
        instance_list = instances(type_obj)

        # Create a list of export expressions, e.g., [:(export PENCIL), :(export BRUSH), ...]
        export_expressions = [:(export $(Symbol(inst))) for inst in instance_list]

        # Return the block of expressions, escaped to run in the caller's scope.
        # The `...` splats the array of expressions into the quote block.
        return esc(quote
            $(export_expressions...)
        end)
    catch e
        return :(error("Could not find enum type " * $(string(enum_type)) * ". It must be defined before calling @export_enum_members."))
    end
end

"""
    @exported_enum(name, args...)

Defines an enum and automatically exports both the enum type and all of its instances
from the current module.

This macro is a convenience wrapper around Julia's built-in `@enum` macro. It is designed
to reduce boilerplate code by eliminating the need to manually write `export` statements
for the enum type and each of its members.

# Usage

The macro is called with the same syntax as `@enum`, typically using a `begin ... end`
block.

```julia
@exported_enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end
```

# Examples

Consider a module that needs to provide an enum as part of its public API.

#### Without the macro:

Without this macro, you must define the enum and then manually list the type and every single member in an `export` statement.

```julia
module MyModule

@enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end

# Manual export statements are required, which can be verbose.
export MyEnum, ValueA, ValueB, ValueC

end
```

#### With the `@exported_enum` macro:

The macro handles the definition and all exports in a single, clean expression, making your code more concise and less error-prone.

```julia
module MyModule

@exported_enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end

# No further export statements are needed for the enum.

end
```

The macro call above automatically expands to the following code during compilation:
```julia
@enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end
export MyEnum
export ValueA
export ValueB
export ValueC
```

# See Also
- `export_enum_members`
- `@enum`: The built-in macro for creating enumerations in Julia.
- `export`: The keyword for defining a module's public API.
"""
macro exported_enum(name, args...)
    # Helper function to extract the symbol name from an enum member definition.
    # This correctly handles both `ValueA` and `ValueA = 1`.
    function get_enum_symbol(arg)
        if arg isa Symbol
            return arg
        elseif arg isa Expr && arg.head == :(=) && arg.args[1] isa Symbol
            return arg.args[1]
        else
            return nothing # Ignore things like LineNumberNodes or comments
        end
    end

    local enum_definition_args # The raw arguments for the @enum macro
    local member_names         # The clean symbols for the export list

    if length(args) == 1 && args[1] isa Expr && args[1].head == :block
        # Handle the `begin ... end` block syntax
        block = args[1]
        enum_definition_args = block
        member_names = filter(!isnothing, [get_enum_symbol(arg) for arg in block.args])
    else
        # Handle the standard `@enum(name, ValueA, ValueB=2)` syntax
        enum_definition_args = args
        member_names = filter(!isnothing, [get_enum_symbol(arg) for arg in enum_definition_args])
    end

    # The @enum macro can take the block or the splatted args directly.
    # We construct the appropriate call based on the syntax used.
    enum_definition = if enum_definition_args isa Expr && enum_definition_args.head == :block
        :(@enum $name $enum_definition_args)
    else
        :(@enum $name $(enum_definition_args...))
    end

    # We escape the final generated code block to ensure it is executed
    # in the scope where the macro was called.
    return esc(quote
        $enum_definition
        export $name
        $([:(export $name) for name in member_names]...)
    end)
end