Skip to content

Commit

Permalink
feat(command,flags): add integer type (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4spar committed May 6, 2021
1 parent ef3df5e commit 2cc7e57
Show file tree
Hide file tree
Showing 16 changed files with 257 additions and 71 deletions.
3 changes: 3 additions & 0 deletions command/command.ts
Expand Up @@ -61,6 +61,7 @@ import type {
ITypeOptions,
IVersionHandler,
} from "./types.ts";
import { IntegerType } from "./types/integer.ts";

interface IDefaultOption<
// deno-lint-ignore no-explicit-any
Expand Down Expand Up @@ -916,6 +917,8 @@ export class Command<
this.type("string", new StringType(), { global: true });
!this.types.has("number") &&
this.type("number", new NumberType(), { global: true });
!this.types.has("integer") &&
this.type("integer", new IntegerType(), { global: true });
!this.types.has("boolean") &&
this.type("boolean", new BooleanType(), { global: true });

Expand Down
53 changes: 30 additions & 23 deletions command/test/type/boolean_test.ts
Expand Up @@ -4,103 +4,110 @@ import { Command } from "../../command.ts";
const cmd = new Command()
.throwErrors()
.name("test-command")
.arguments("[foo]")
.option("-f, --flag [value:boolean]", "description ...")
.option("--no-flag", "description ...")
.action(() => {});

Deno.test("command typeString flag", async () => {
const cmd2 = new Command()
.throwErrors()
.name("test-command")
.option("-f, --flag [value:boolean]", "description ...")
.action(() => {});

Deno.test("command - type - boolean - with no value", async () => {
const { options, args } = await cmd.parse(["-f"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - long flag with no value", async () => {
const { options, args } = await cmd.parse(["--flag"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - with true value", async () => {
const { options, args } = await cmd.parse(["-f", "true"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - long flag with true value", async () => {
const { options, args } = await cmd.parse(["--flag", "true"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - with false value", async () => {
const { options, args } = await cmd.parse(["-f", "false"]);

assertEquals(options, { flag: false });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
const { options, args } = await cmd.parse(["--flag", "false"]);
Deno.test("command - type - boolean - long flag with false value and argument", async () => {
const { options, args } = await cmd.parse(["--flag", "false", "unknown"]);

assertEquals(options, { flag: false });
assertEquals(args, []);
assertEquals(args, ["unknown"]);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - with 1 value", async () => {
const { options, args } = await cmd.parse(["-f", "1"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - long flag with 1 value", async () => {
const { options, args } = await cmd.parse(["--flag", "1"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - boolean - with 0 value", async () => {
const { options, args } = await cmd.parse(["-f", "0"]);

assertEquals(options, { flag: false });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
const { options, args } = await cmd.parse(["--flag", "0"]);
Deno.test("command - type - boolean - long flag with 0 value and argument", async () => {
const { options, args } = await cmd.parse(["--flag", "0", "unknown"]);

assertEquals(options, { flag: false });
assertEquals(args, []);
assertEquals(args, ["unknown"]);
});

Deno.test("command typeString flagValue", async () => {
const { options, args } = await cmd.parse(["--no-flag"]);
Deno.test("command - type - boolean - negatable option with argument", async () => {
const { options, args } = await cmd.parse(["--no-flag", "unknown"]);

assertEquals(options, { flag: false });
assertEquals(args, []);
assertEquals(args, ["unknown"]);
});

Deno.test("command optionStandalone flagCombineLong", async () => {
Deno.test("command - type - boolean - with invalid value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "true", "unknown"]);
await cmd.parse(["-f", "unknown"]);
},
Error,
`No arguments allowed for command "test-command".`,
`Option "--flag" must be of type "boolean", but got "unknown".`,
);
});

Deno.test("command optionStandalone flagCombineLong", async () => {
Deno.test("command - type - boolean - no arguments allowed", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "unknown"]);
await cmd2.parse(["-f", "true", "unknown"]);
},
Error,
`Option "--flag" must be of type "boolean", but got "unknown".`,
`No arguments allowed for command "test-command".`,
);
});
10 changes: 5 additions & 5 deletions command/test/type/custom_test.ts
Expand Up @@ -29,28 +29,28 @@ const cmd = new Command()
.description("...")
.action(() => {});

Deno.test("command with custom type", async () => {
Deno.test("command - type - custom - with valid value", async () => {
const { options, args } = await cmd.parse(["-e", "my@email.com"]);

assertEquals(options, { email: "my@email.com" });
assertEquals(args, []);
});

Deno.test("command with global custom type", async () => {
Deno.test("command - type - custom - long flag with valid value", async () => {
const { options, args } = await cmd.parse(["-E", "my@email.com"]);

assertEquals(options, { email2: "my@email.com" });
assertEquals(args, []);
});

Deno.test("sub command global custom type", async () => {
Deno.test("command - type - custom - child command with valid value", async () => {
const { options, args } = await cmd.parse(["init", "-E", "my@email.com"]);

assertEquals(options, { email2: "my@email.com" });
assertEquals(args, []);
});

Deno.test("sub command none global custom type", async () => {
Deno.test("command - type - custom - with unknown type", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["init", "-e", "my@email.com"]);
Expand All @@ -60,7 +60,7 @@ Deno.test("sub command none global custom type", async () => {
);
});

Deno.test("sub command with global custom type and invalid value", async () => {
Deno.test("command - type - custom - with invalid value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["init", "-E", "my @email.com"]);
Expand Down
62 changes: 62 additions & 0 deletions command/test/type/integer_test.ts
@@ -0,0 +1,62 @@
import { assertEquals, assertThrowsAsync } from "../../../dev_deps.ts";
import { Command } from "../../command.ts";

const cmd = new Command()
.throwErrors()
.option("-f, --flag [value:integer]", "description ...")
.option("-F, --flag2 <value:integer>", "description ...")
.action(() => {});

Deno.test("command - type - integer - with no value", async () => {
const { options, args } = await cmd.parse(["-f"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command - type - integer - with valid value", async () => {
const { options, args } = await cmd.parse(["--flag", "123"]);

assertEquals(options, { flag: 123 });
assertEquals(args, []);
});

Deno.test("command - type - integer - with argument", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "123", "unknown"]);
},
Error,
`No arguments allowed for command "COMMAND".`,
);
});

Deno.test("command - type - integer - with missing value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-F"]);
},
Error,
`Missing value for option "--flag2".`,
);
});

Deno.test("command - type - integer - with invalid string value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "abc"]);
},
Error,
`Option "--flag" must be of type "integer", but got "abc".`,
);
});

Deno.test("command - type - integer - with invalid float value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "1.23"]);
},
Error,
`Option "--flag" must be of type "integer", but got "1.23".`,
);
});
12 changes: 5 additions & 7 deletions command/test/type/no_value_test.ts
@@ -1,28 +1,26 @@
import { assertEquals, assertThrowsAsync } from "../../../dev_deps.ts";
import { Command } from "../../command.ts";
import { CompletionsCommand } from "../../completions/mod.ts";
import { HelpCommand } from "../../help/mod.ts";

const cmd = new Command()
.throwErrors()
.option("-f, --flag", "description ...")
.action(() => {})
.command("help", new HelpCommand())
.command("completions", new CompletionsCommand());
.command("help", new HelpCommand());

Deno.test("command: types - no value short flag", async () => {
Deno.test("command - type - no value - short flag without argument", async () => {
const { options, args } = await cmd.parse(["-f"]);
assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command: types - no value long flag", async () => {
Deno.test("command - type - no value - long flag without argument", async () => {
const { options, args } = await cmd.parse(["--flag"]);
assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command: types - no value short flag invalid arg", async () => {
Deno.test("command - type - no value - short flag with argument", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "true"]);
Expand All @@ -32,7 +30,7 @@ Deno.test("command: types - no value short flag invalid arg", async () => {
);
});

Deno.test("command: types - no value long flag invalid arg", async () => {
Deno.test("command - type - no value - long flag with argument", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["--flag", "true"]);
Expand Down
19 changes: 15 additions & 4 deletions command/test/type/number_test.ts
Expand Up @@ -4,24 +4,25 @@ import { Command } from "../../command.ts";
const cmd = new Command()
.throwErrors()
.option("-f, --flag [value:number]", "description ...")
.option("-F, --flag2 <value:number>", "description ...")
.option("--no-flag", "description ...")
.action(() => {});

Deno.test("command typeString flag", async () => {
Deno.test("command - type - number - with no value", async () => {
const { options, args } = await cmd.parse(["-f"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - number - with valid value", async () => {
const { options, args } = await cmd.parse(["--flag", "123"]);

assertEquals(options, { flag: 123 });
assertEquals(args, []);
});

Deno.test("command optionStandalone flagCombineLong", async () => {
Deno.test("command - type - number - with argument", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "123", "unknown"]);
Expand All @@ -31,7 +32,17 @@ Deno.test("command optionStandalone flagCombineLong", async () => {
);
});

Deno.test("command optionStandalone flagCombineLong", async () => {
Deno.test("command - type - number - with missing value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-F"]);
},
Error,
`Missing value for option "--flag2".`,
);
});

Deno.test("command - type - number - with invalid string value", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "abc"]);
Expand Down
6 changes: 3 additions & 3 deletions command/test/type/string_test.ts
Expand Up @@ -7,21 +7,21 @@ const cmd = new Command()
.option("--no-flag", "description ...")
.action(() => {});

Deno.test("command typeString flag", async () => {
Deno.test("command - type - string - with no value", async () => {
const { options, args } = await cmd.parse(["-f"]);

assertEquals(options, { flag: true });
assertEquals(args, []);
});

Deno.test("command typeString flagValue", async () => {
Deno.test("command - type - string - with valid value", async () => {
const { options, args } = await cmd.parse(["--flag", "value"]);

assertEquals(options, { flag: "value" });
assertEquals(args, []);
});

Deno.test("command optionStandalone flagCombineLong", async () => {
Deno.test("command - type - string - no arguments allowed", async () => {
await assertThrowsAsync(
async () => {
await cmd.parse(["-f", "value", "unknown"]);
Expand Down
11 changes: 11 additions & 0 deletions command/types/integer.ts
@@ -0,0 +1,11 @@
import { Type } from "../type.ts";
import type { ITypeInfo } from "../types.ts";
import { integer } from "../../flags/types/integer.ts";

/** Integer type. */
export class IntegerType extends Type<number> {
/** Parse integer type. */
public parse(type: ITypeInfo): number {
return integer(type);
}
}
2 changes: 2 additions & 0 deletions flags/flags.ts
Expand Up @@ -23,10 +23,12 @@ import { boolean } from "./types/boolean.ts";
import { number } from "./types/number.ts";
import { string } from "./types/string.ts";
import { validateFlags } from "./validate_flags.ts";
import { integer } from "./types/integer.ts";

const Types: Record<string, ITypeHandler<unknown>> = {
[OptionType.STRING]: string,
[OptionType.NUMBER]: number,
[OptionType.INTEGER]: integer,
[OptionType.BOOLEAN]: boolean,
};

Expand Down

0 comments on commit 2cc7e57

Please sign in to comment.