TanklesXL / glint

Gleam command-line argument parsing with flags and automated help text generation.
https://hex.pm/packages/glint
Apache License 2.0
50 stars 7 forks source link

Consider adding enum-flag #11

Closed JohnBjrk closed 1 month ago

JohnBjrk commented 1 year ago

I did some very quick proof on concept on how to add an enum-flag. Maybe this can be a starting-point for the design.

import gleam/io
import gleam/map

pub type Enum {
  Foo
  Bar
}

pub fn main() {
  let type_map = map.from_list([#("foo", Foo), #("bar", Bar)])

  assert Ok(enum) =
    type_map
    |> map.get("bar")

  use_enum(enum)
}

fn use_enum(enum: Enum) {
  enum
  |> io.debug()
}

The idea is that the user provides a type_map with the config which can be used to parse and construct enum-values based on a string argument. I think the mapping needs to come from userland. I had an other idea where I could use atoms to construct the enum values on erlang-side, but could not find a way to that on js.

JohnBjrk commented 1 year ago

Thinking a bit more on this (in reference to your point about Glint not being generic on the flags) maybe this is not a feasible path. It would have been really nice to get a specific custom type back from Glint, but I don't really see how this can be done in a good way without a rather drastic rework of the api (which would probably result in worse dx on the most common usage).

So another thought on this, maybe it is good enough to have some guarantees on the values accepted by Glint and then have the actual type transformation happen on user side. One could imagine having something more in the Value type to give some indication which are the possible values for a parsed argument. Something like:

pub type Value {
// ... other values
E(String, List(String))
}

where the list in E is the possible values. Still it would be up to the user to actually use these values and it's also a breaking change to the api, so maybe it's enough to just be able to specify the possible values and get some error from Glint if an argument is invalid, and maybe also make it possible to specify a description for each possible value that can be used in the help message. With this approach I also think that it could be possible to extend this to support for example allowed ranges for numeric arguments.

If I find the time I can make a draft PR for something like this, if you think it sounds like it should fit in the overall design of Glint.

TanklesXL commented 1 year ago

I have some thoughts stewing related to this, I've been wanting a way to apply contraints to flag values and have some ideas as to how I would go about it. It would require a breaking change in the flag api though 🤔

JohnBjrk commented 1 year ago

Nice! What is the reason that it will break the flag api? I had a quick look and and since the list of flags are represented as the opaque Content I thought that maybe it would be possible to just add something like a EnumContent(value: Value, possibleValues: List(Value), description: String). But I guess that there is something that complicates this that I don't see (not so familiar with the codebase).

TanklesXL commented 1 year ago

The breaking change part of l what I was thinking of requires making the Value type opaque and changing it to load in constraints and validators. This means that instead of flag.get I would expose flag.get_int and others for the other variants.

I mostly don't want to make such a change for so narrow a scope as "allowed values", I would extend the concept to apply any kind of validation/constraint logic on a flag value

TanklesXL commented 1 year ago

I would like to keep it inside of what is currently Value (subject to name change) to keep a certain minimum level of type safety

JohnBjrk commented 1 year ago

Yes that sounds like a good ide! My only concern (except for breaking the api) is that it would be nice if it was possible to support creation of common validation in an easy way (such as for example enum, or range for numbers), but probably this can be done by providing some sort of supplementary api for creating such validators.

TanklesXL commented 1 year ago

Yeah exactly. I had some ideas of providing some convenience functionality like comparisons, variants, ranges etc., and I would let users plug in whichever others they might want

TanklesXL commented 1 year ago

We also don't have many users so I'm not incredibly worried about a breaking change at this point, having a more useful and ergonomic api is worth the hassle (in my opinion)

JohnBjrk commented 1 year ago

We also don't have many users so I'm not incredibly worried about a breaking change at this point, having a more useful and ergonomic api is worth the hassle (in my opinion)

Agree!

I'll close my draft PR (since it was more for discussion) and wait a bit to see what you come up with (sounds like you have a good plan). Just let me know if you want any input from me on the api-design.

TanklesXL commented 1 year ago

Awesome :D Thanks for all of this btw! It's nice to be able to talk through these things instead of spinning off in a random direction with no input, I'll make sure to poke you when I have something to show