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

feat: Derive name of positional arguments from property name #6

Merged
merged 7 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions sources/advanced/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export abstract class Command<Context extends BaseContext = BaseContext> {
});
} else {
this.registerDefinition(prototype, command => {
command.addPositional({required: descriptor.required});
command.addPositional({name: propertyName, required: descriptor.required});
});

this.registerTransformer(prototype, (state, command) => {
Expand Down Expand Up @@ -201,7 +201,7 @@ export abstract class Command<Context extends BaseContext = BaseContext> {
static Rest({required = 0}: {required?: number} = {}) {
return <Context extends BaseContext>(prototype: Command<Context>, propertyName: string) => {
this.registerDefinition(prototype, command => {
command.addRest({required});
command.addRest({name: propertyName, required});
});

this.registerTransformer(prototype, (state, command) => {
Expand Down
67 changes: 32 additions & 35 deletions sources/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,9 +458,9 @@ export const reducers = {
// ------------------------------------------------------------------------

export type ArityDefinition = {
leading: number;
extra: number;
trailing: number;
leading: string[];
extra: string[] | undefined;
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
trailing: string[];
proxy: boolean;
};

Expand All @@ -475,7 +475,7 @@ export class CommandBuilder<Context> {
public readonly cliOpts: Readonly<CliOptions>;

private readonly allOptionNames: string[] = [];
private readonly arity: ArityDefinition = {leading: 0, trailing: 0, extra: 0, proxy: false};
private readonly arity: ArityDefinition = {leading: [], trailing: [], extra: [], proxy: false};
private readonly options: OptDefinition[] = [];
private readonly paths: string[][] = [];

Expand All @@ -494,31 +494,31 @@ export class CommandBuilder<Context> {
Object.assign(this.arity, {leading, trailing, extra, proxy});
}

addPositional({required = true}: {required?: boolean} = {}) {
if (!required && this.arity.extra === Infinity)
addPositional({name = 'arg', required = true}: {name?: string, required?: boolean} = {}) {
if (!required && this.arity.extra === undefined)
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`Optional parameters cannot be declared when using .rest() or .proxy()`);
if (!required && this.arity.trailing > 0)
if (!required && this.arity.trailing.length > 0)
throw new Error(`Optional parameters cannot be declared after the required trailing positional arguments`);

if (!required) {
this.arity.extra += 1;
} else if (this.arity.extra === 0) {
this.arity.leading += 1;
if (!required && this.arity.extra !== undefined) {
this.arity.extra.push(name);
} else if (this.arity.extra !== undefined && this.arity.extra.length === 0) {
this.arity.leading.push(name);
} else {
this.arity.trailing += 1;
this.arity.trailing.push(name);
}
}

addRest({required = 0}: {required?: number} = {}) {
if (this.arity.extra === Infinity)
addRest({name = 'arg', required = 0}: {name?: string, required?: number} = {}) {
if (this.arity.extra === undefined)
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`Infinite lists cannot be declared multiple times in the same command`);
if (this.arity.trailing > 0)
if (this.arity.trailing.length > 0)
throw new Error(`Infinite lists cannot be declared after the required trailing positional arguments`);

for (let t = 0; t < required; ++t)
this.addPositional();
this.addPositional({name});

this.arity.extra = Infinity;
this.arity.extra = undefined;
}

addProxy() {
Expand Down Expand Up @@ -554,17 +554,14 @@ export class CommandBuilder<Context> {
segments.push(`[${names.join(`,`)}${args.join(``)}]`);
}

for (let t = 0; t < this.arity.leading; ++t)
segments.push(`<arg>`);
segments.push(...this.arity.leading.map(name => `<${name}>`))

if (this.arity.extra === Infinity)
if (this.arity.extra === undefined)
segments.push(`...`);
else for (let t = 0; t < this.arity.extra; ++t)
segments.push(`[arg]`);
else
segments.push(...this.arity.extra.map(name => `[${name}]`))

for (let t = 0; t < this.arity.trailing; ++t) {
segments.push(`<arg>`);
}
segments.push(...this.arity.trailing.map(name => `<${name}>`))
}

return segments.join(` `);
Expand Down Expand Up @@ -596,43 +593,43 @@ export class CommandBuilder<Context> {
lastPathNode = nextPathNode;
}

if (this.arity.leading > 0 || !this.arity.proxy) {
if (this.arity.leading.length > 0 || !this.arity.proxy) {
const helpNode = injectNode(machine, makeNode());
registerDynamic(machine, lastPathNode, `isHelp`, helpNode, [`useHelp`, this.cliIndex]);
registerStatic(machine, helpNode, END_OF_INPUT, NODE_SUCCESS, [`setSelectedIndex`, HELP_COMMAND_INDEX]);
}

this.registerOptions(machine, lastPathNode);

if (this.arity.leading > 0)
if (this.arity.leading.length > 0)
registerStatic(machine, lastPathNode, END_OF_INPUT, NODE_ERRORED, [`setError`, `Not enough positional arguments`]);

let lastLeadingNode = lastPathNode;
for (let t = 0; t < this.arity.leading; ++t) {
for (let t = 0; t < this.arity.leading.length; ++t) {
const nextLeadingNode = injectNode(machine, makeNode());
this.registerOptions(machine, nextLeadingNode);

if (this.arity.trailing > 0 || t + 1 !== this.arity.leading)
if (this.arity.trailing.length > 0 || t + 1 !== this.arity.leading.length)
registerStatic(machine, nextLeadingNode, END_OF_INPUT, NODE_ERRORED, [`setError`, `Not enough positional arguments`]);

registerDynamic(machine, lastLeadingNode, `isNotOptionLike`, nextLeadingNode, `pushPositional`);
lastLeadingNode = nextLeadingNode;
}

let lastExtraNode = lastLeadingNode;
if (this.arity.extra > 0) {
if (this.arity.extra !== undefined && this.arity.extra.length > 0) {
const extraShortcutNode = injectNode(machine, makeNode());
registerShortcut(machine, lastLeadingNode, extraShortcutNode);

if (this.arity.extra === Infinity) {
if (this.arity.extra === undefined) {
const extraNode = injectNode(machine, makeNode());
this.registerOptions(machine, extraNode);

registerDynamic(machine, lastLeadingNode, positionalArgument, extraNode, `pushExtra`);
registerDynamic(machine, extraNode, positionalArgument, extraNode, `pushExtra`);
registerShortcut(machine, extraNode, extraShortcutNode);
} else {
for (let t = 0; t < this.arity.extra; ++t) {
for (let t = 0; t < this.arity.extra.length; ++t) {
const nextExtraNode = injectNode(machine, makeNode());
this.registerOptions(machine, nextExtraNode);

Expand All @@ -645,15 +642,15 @@ export class CommandBuilder<Context> {
lastExtraNode = extraShortcutNode;
}

if (this.arity.trailing > 0)
if (this.arity.trailing.length > 0)
registerStatic(machine, lastExtraNode, END_OF_INPUT, NODE_ERRORED, [`setError`, `Not enough positional arguments`]);

let lastTrailingNode = lastExtraNode;
for (let t = 0; t < this.arity.trailing; ++t) {
for (let t = 0; t < this.arity.trailing.length; ++t) {
const nextTrailingNode = injectNode(machine, makeNode());
this.registerOptions(machine, nextTrailingNode);

if (t + 1 < this.arity.trailing)
if (t + 1 < this.arity.trailing.length)
registerStatic(machine, nextTrailingNode, END_OF_INPUT, NODE_ERRORED, [`setError`, `Not enough positional arguments`]);

registerDynamic(machine, lastTrailingNode, `isNotOptionLike`, nextTrailingNode, `pushPositional`);
Expand Down
38 changes: 38 additions & 0 deletions tests/advanced.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,42 @@ describe(`Advanced`, () => {

expect(output).to.equal(`Running CommandB\n"hello"\n`);
});

it.only(`derives positional argument names from the property name`, async () => {
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
class CommandA extends Command {
@Command.String()
workspaceName!: string;

@Command.String({required: false})
extra!: string;

@Command.String()
scriptName!: string;

@Command.Path(`workspace`)
async execute() {
throw new Error('not implemented, just testing usage()')
}
}

const cli = Cli.from([CommandA])

expect(cli.usage(CommandA)).to.equal(`\u001b[1m$ \u001b[22m... workspace <workspaceName> [extra] <scriptName>\n`);
});

it.only(`derives rest argument names from the property name`, async () => {
class CommandA extends Command {
@Command.Rest({required: 2})
workspaceNames!: string;

@Command.Path(`clean`)
async execute() {
throw new Error('not implemented, just testing usage()')
}
}

const cli = Cli.from([CommandA])

expect(cli.usage(CommandA)).to.equal(`\u001b[1m$ \u001b[22m... clean <workspaceNames> <workspaceNames> ...\n`);
});
});