Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to provide own parsing logic #46

Open
ajafff opened this issue Apr 26, 2018 · 1 comment
Open

Add ability to provide own parsing logic #46

ajafff opened this issue Apr 26, 2018 · 1 comment
Assignees

Comments

@ajafff
Copy link

ajafff commented Apr 26, 2018

While trying to port my CLI, I noticed there is no way to declare options that may not consume the next argument if not necessary.

A popular example are TypeScript's compilerOptions:

tsc --project tsconfig.json
# parses as {strict: undefined, project: 'tsconfig.json'}

tsc --strict --project tsconfig.json
# parses as {strict: true, project: 'tsconfig.json'}

tsc --strict true --project tsconfig.json
# parses as {strict: true, project: 'tsconfig.json'}

tsc --strict false --project tsconfig.json
# parses as {strict: false, project: 'tsconfig.json'}

In my use case this is not limited to a switch option (boolean value). It's actually an optional boolean or number:

wotan foo.ts
# parses as {fix: false, files: ['foo.ts']}

wotan --fix foo.ts
# parses as {fix: true, files: ['foo.ts']}

wotan --fix foo.ts
# parses as {fix: true, files: ['foo.ts']}

wotan --fix false foo.ts
# parses as {fix: false, files: ['foo.ts']}

wotan --fix 5 foo.ts
# parses as {fix: 5, files: ['foo.ts']}

I can think of 2 possible solutions:

Add optional flag

class MyOptions extends Options {
  @option({optional: true, default: false, validator: /^(true|false|\d+)$/})
  public fix: boolean | number;
}

If optional is true and validating the argument using the validator fails, do not consume the argument and use the default value instead.

Add parse function

class MyOptions extends Options {
  @option({
    parse(consume: () => string | undefined, unconsume: (arg: string) => void) {
      const option = consume();
      if (option === undefined)
        return false; // no more arguments available
      if (option === 'true')
        return true;
      if (option === 'false')
        return false;
      const numeric = +option;
      if (!Number.isNaN(numeric))
        return numeric;
      // argument is not in the expected format, push it back so that it may be parsed as another option or parameter
      unconsume(option);
      return false; // use a default value
    },
    validator(value: boolean | number) {
      if (typeof value === 'number' && value < 0)
        throw new ExpectedError('--fix must be a positive number'); 
    }
  })
  public fix: boolean | number;
}

If a parse function is provided, this function is responsible for consuming the arguments, parsing and converting the value. By calling consume() more than once the parser may consume 0..n of the remaining arguments.
validators are still executed after parsing. That means errors about invalid option values can be reported. That's not possible with the optional proposal.

@vilicvane
Copy link
Owner

Hi, thanks for this feature request. I am quite into the second solution (though I might prefer peek and consume), it looks like a more elegant way consuming options while giving developers more flexibility.

Will come out with some refactoring ideas later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants