go-enum icon indicating copy to clipboard operation
go-enum copied to clipboard

Add HumanString method

Open mirrorru opened this issue 5 months ago • 12 comments

Please add generation of "HumanString" method to make enum items "names", which differs from code-name. Its helpful to change "Meaning" on items, without changing code-name

// ENUM(new,active)
type ClientStatus byte

func (x ClientStatus) HumanString() string {
	switch x {
	case ClientStatusNew:
		return "Not approved"
	case ClientStatusActive:
		return "Allowed to work"
	default:
		return ""
	}
}

mirrorru avatar Aug 01 '25 13:08 mirrorru

If it's acceptable to have ClientStatus be a string, you can do this

// ENUM(
// new    = "Not approved"
// active = "Allowed to work"
// )
type ClientStatus string

Otherwise you need to propose a way to specify the output of "HumanString".

IMO it's not clear what a "human string" is supposed to mean. You might want several different string representations of the same value depending on context, and I don't think just String and HumanString is going to map neatly onto that. If you want a method that returns a different string value from String(), you can always implement it yourself (use the exhaustive lint rule to be sure you have covered all values)

How about allowing the String representation of integer enums to be changed? Like this:

// ENUM(
// new=0,    string="Not approved"
// active=1, string="Allowed to work"
// )
type ClientStatus byte

jonathangjertsen avatar Aug 04 '25 13:08 jonathangjertsen

At the moment, I'm additionally creating template methods for "HumanString" and the request was to get rid of it.

But I think something similar to option // new=0, string="Not approved" would be correct, because option // new = "Not approved" leads to a change in the data type to a string.

mirrorru avatar Aug 04 '25 13:08 mirrorru

If you think that the name "HumanString" doesn't sound very good, then I agree with you, it was a conventional name. Probably "Label" will be more correct and understandable.

// new=0, label="Not approved"

mirrorru avatar Aug 07 '25 12:08 mirrorru

Sorry if there's confusion, I'm not a maintainer/contributor, I just saw the issue while posting my own.

@abice

jonathangjertsen avatar Aug 07 '25 12:08 jonathangjertsen

@jonathangjertsen I agreed with your previous comments, which is why I didn't step in. Appreciate your help in that matter.

I still am unclear as to what situation these alternate names would be used. Is there a reason not to have those enum names be the same as the labels that you're creating?

abice avatar Aug 08 '25 12:08 abice

I didn't realize before now that the enum names you specify don't have to be valid identifiers.

// ENUM(
//		Not approved=0
//	 Allowed to work=1
// )
type ClientStatus byte

-->

const (
	ClientStatusNotApproved ClientStatus = iota
	ClientStatusAllowedToWork
)

and String returns whatever you put in the comment. So my suggestion for changing the string representation isn't needed

jonathangjertsen avatar Aug 08 '25 13:08 jonathangjertsen

Situation:

"Labels" are needed so that they can be easily transferred to the user interface (frontend or GUI/TUI), so that you can give a list of acceptable values in the form of Key=Value with minimal programming. The labels will be convenient for preparing the text of "human-readable" errors, and much more, where not some kind of designation is needed, but something understandable to an ordinary person.

type ClientStatus byte

type ClientStatusForUI struct {
    Status ClientStatus 
    Label string
}

func GetStatusesListForUI() (result []ClientStatusForUI)  {
   // If there is something generated that will give a similar ready-made structure,
   // then this will also be very good. Like Values() or Names()
    for _, v:= range ClientStatusValues() {
        result = append(result, ClientStatusForUI{v, v.Label()}
    }
    return result
} 

func ErrClientStatusInvalid(status ClientStatus ) error {
    return fmt.Errorf("Status %s is invalid", status.Label())
}

@abice

mirrorru avatar Aug 09 '25 18:08 mirrorru

At what point are you using the non human readable strings? Like mentioned before, you can use the human readable string as the enum, and this will still work.

abice avatar Aug 09 '25 19:08 abice

If the label is not in pure Latin and is long, then the code looks bad. I would like to have short English-language identifiers.

// ENUM(
//Můj dlouhý název pro značku, další velmi dlouhý název
// )
type SomeStatus byte


//...

const (
	SomeStatusMůjDlouhýNázevProZnačku SomeStatus = iota
	SomeStatusDalšíVelmiDlouhýNázev
)

mirrorru avatar Aug 09 '25 19:08 mirrorru

I won't insist any more. My colleagues and I always have a question about labels when we talk about generation for enumerations.

The label text can be changed without changing the IDs. This is a very important point.

@abice

mirrorru avatar Aug 09 '25 19:08 mirrorru

Would you expect that json or any other marshaling is affected by this change? Or it's just a matter of adding a function for translating to some label, or alias, or display value?

I just need to know the use case so I can properly assess how to implement it. It might be that instead of me adding a one off functionality for your use case, that maybe I add a tagging structure similar to go, and then pass those tags into the templates (built in or custom) and you could have the template generate it for you instead of me doing it.

Does that make sense?

abice avatar Aug 09 '25 22:08 abice

Yes, it's just a matter of adding a function to convert to some kind of label, alias, or display value. No influence on marshalling is required. Initially, one method was required that would return the item's label. I would not like to use templates for this.

It will probably be possible to get the slice []{value, label} from the template. The value=label map will probably end up in the generated code, or maked from template.

mirrorru avatar Aug 10 '25 07:08 mirrorru