Skip to content

Commit

Permalink
Merge pull request #8 from 00JCIV00/docs
Browse files Browse the repository at this point in the history
Polished Readme, Docs, & Guides
  • Loading branch information
00JCIV00 authored Jul 9, 2023
2 parents c00b990 + 9fdcbbc commit 25cbab0
Show file tree
Hide file tree
Showing 47 changed files with 14,577 additions and 12,406 deletions.
147 changes: 78 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# cova
Commands, Options, Values, Arguments. A simple yet robust command line argument parsing library for Zig.
Commands, Options, Values, Arguments. A simple yet robust cross-platform command line argument parsing library for Zig.
___

## Overview
Expand All @@ -10,11 +10,11 @@ Cova is based on the idea that Arguments will fall into one of three types: Comm

## Features
- **Comptime Setup. Runtime Use.**
- Cova is designed to have Argument types set up at Compile Time so they can be validated during each compilation, thus providing the library user with immediate feedback.
- Once validated, Argument types are initialized to memory for Runtime use where app user arguments are parsed then made ready to be analyzed by library user code.
- Cova is designed to have Argument Types set up at Compile Time so they can be validated during each compilation, thus providing the library user with immediate feedback.
- Once validated, Argument Types are initialized to memory for Runtime use where end user argument tokens are parsed then made ready to be analyzed by library user code.
- **Simple Design:**
- All Argument tokens are parsed to Commands, Options, or Values.
- These Argument types can be Created From or Converted To Structs and their corresponding Fields.
- These Argument Types can be Created From or Converted To Structs and their corresponding Fields.
- The most Basic Setup requires only Cova imports, a library user Struct, and a few function calls for parsing.
- POSIX Compliant (as defined [here](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html)) by default.
- Multiplatform. Tested across:
Expand Down Expand Up @@ -46,7 +46,7 @@ Cova is based on the idea that Arguments will fall into one of three types: Comm
- Can be given multiple times (i.e. `my-cmd "string val 1" "string val 2" "string val 3"`)
- Can be Delimited (i.e. `my-cmd 50,100,68`)
- **Granular, Robust Customization:**
- Cova offers deep customization through the Fields of the Argument types as well as several Config Structs, allowing library users to only configure what they need.
- Cova offers deep customization through the Fields of the Argument Types as well as several Config Structs, allowing library users to only configure what they need.
- Parsing:
- Mandate all Values be filled.
- Customize Separator Character(s) between Options and their Values.
Expand Down Expand Up @@ -91,7 +91,8 @@ Cova is based on the idea that Arguments will fall into one of three types: Comm
- [x] Compatible non-nullable fields become Values.

### Post Public Release
- [ ] Pull Argument type metadata via AST Parsing of struct/field comments.
- [ ] Support all Int/Float types. (This is technically possible now by adding them to the `cova.Value.Generic` union, but there should be simpler way if reified declarations become allowed in Zig.)
- [ ] Pull Argument Type metadata via AST Parsing of struct/field comments.
- [ ] Tab Completion (long-term goal).

## Documentation
Expand Down Expand Up @@ -130,7 +131,7 @@ exe.addModule("cova", cova_mod);
.dependencies = .{
.cova = .{
.url = "https://github.com/00JCIV00/cova/archive/5199fec02e34f11ac2b36b91a087f232076eb9fc.tar.gz",
.hash = "hash from step 3 here",
.hash = "hash from step 3 here",
},
},
```
Expand All @@ -145,6 +146,7 @@ zig build demo
```
3. Try it out!
```bash
cd bin
./covademo help
```

Expand All @@ -160,15 +162,15 @@ const BaseCommand = cova.Command.Base();
const utils = cova.utils;
pub const ProjectStruct = struct {
pub const SubStruct = struct {
sub_uint: ?u8 = 5,
sub_string: []const u8,
},
subcmd: SubStruct = .{},
int: ?i4 = 10,
flag: ?bool = false,
strings: [3]const []const u8 = .{ "Three", "default", "strings." },
pub const SubStruct = struct {
sub_uint: ?u8 = 5,
sub_string: []const u8,
},
subcmd: SubStruct = .{},
int: ?i4 = 10,
flag: ?bool = false,
strings: [3]const []const u8 = .{ "Three", "default", "strings." },
};
const setup_cmd = BaseCommand.from(ProjectStruct);
Expand Down Expand Up @@ -208,23 +210,23 @@ const utils = cova.utils;
...
// This comptime struct is valid to be parsed into a cova Command.
pub const ProjectStruct = struct {
// This nested struct is also valid.
pub const SubStruct = struct {
// Optional Primitive type fields will be converted into cova Options.
// By default, Options will be given a long name and a short name based on the field name. (i.e. int = `-i` or `--int`)
sub_uint: ?u8 = 5,
// Primitive type fields will be converted into Values.
sub_string: []const u8,
},
// Struct fields will be converted into cova Commands.
subcmd: SubStruct = .{},
// The default values of Primitive type fields will be applied as the default value of the converted Option or Value.
int: ?i4 = 10,
// Optional Booleans will become cova Options that don't take a Value and are set to true simply by calling the Option's short or long name.
flag: ?bool = false,
// Arrays will be turned into Multi-Values or Multi-Options based on the array's child type.
strings: [3]const []const u8 = .{ "Three", "default", "strings." },
// This nested struct is also valid.
pub const SubStruct = struct {
// Optional Primitive type fields will be converted into cova Options.
// By default, Options will be given a long name and a short name based on the field name. (i.e. int = `-i` or `--int`)
sub_uint: ?u8 = 5,
// Primitive type fields will be converted into Values.
sub_string: []const u8,
},
// Struct fields will be converted into cova Commands.
subcmd: SubStruct = .{},
// The default values of Primitive type fields will be applied as the default value of the converted Option or Value.
int: ?i4 = 10,
// Optional Booleans will become cova Options that don't take a Value and are set to true simply by calling the Option's short or long name.
flag: ?bool = false,
// Arrays will be turned into Multi-Values or Multi-Options based on the array's child type.
strings: [3]const []const u8 = .{ "Three", "default", "strings." },
};
...
```
Expand All @@ -242,36 +244,36 @@ const setup_cmd = BaseCommand.from(ProjectStruct);
```zig
...
pub fn main() !void {
...
...
// The `init()` method of a Command instance will Validate the Command's Argument Types for correctness and distinct names, then it will return a memory allocated copy of the Command for argument token parsing and follow on analysis.
// The `init()` method of a Command instance will Validate the Command's Argument Types for correctness and distinct names, then it will return a memory allocated copy of the Command for argument token parsing and follow on analysis.
const main_cmd = &(try setup_cmd.init(alloc, .{}));
defer main_cmd.deinit();
...
...
}
```

- Set up the Argument Iterator.
```zig
pub fn main() {
...
...
// The ArgIteratorGeneric is used to step through argument tokens. By default (using `init()`), it will provide Zig's native, cross-platform ArgIterator with app user argument tokens. There's also cova's RawArgIterator that can be used to parse any slice of strings as argument tokens.
// The ArgIteratorGeneric is used to step through argument tokens. By default (using `init()`), it will provide Zig's native, cross-platform ArgIterator with end user argument tokens. There's also cova's RawArgIterator that can be used to parse any slice of strings as argument tokens.
var args_iter = try cova.ArgIteratorGeneric.init(alloc);
defer args_iter.deinit();
...
...
}
```

- Parse argument tokens and Display the result.
```zig
pub fn main() !void {
...
...
/// The `parseArgs()` function will parse the provided ArgIterator's (`&args_iter`) tokens into Argument types within the provided Command (`main_cmd`).
/// The `parseArgs()` function will parse the provided ArgIterator's (`&args_iter`) tokens into Argument Types within the provided Command (`main_cmd`).
try cova.parseArgs(&args_iter, BaseCommand, main_cmd, stdout, .{});
/// Once parsed, the provided Command will be available for analysis by the project code. Using `utils.displayCmdInfoi()` will create a neat display of the parsed Command for debugging.
/// Once parsed, the provided Command will be available for analysis by the project code. Using `utils.displayCmdInfoi()` will create a neat display of the parsed Command for debugging.
try utils.displayCmdInfo(BaseCommand, main_cmd, alloc, stdout);
}
```
Expand All @@ -284,32 +286,32 @@ const cova = @import("cova");
// Using Command.Custom() allows for custom configurations that apply to all Commands of this type, which will usually mean all Commands within a project.
const CustomCommand = cova.Command.Custom(.{
// The Global Help Prefix of this Command Type will be used as the default prefix for the auto-generated Help and Usage messages of a Command and all of its Sub Commands.
.global_help_prefix = "CovaDemo w/ Advanced Features",
// The Sub Commands Help Format changes how Sub Commands are listed in Help auto-generated Help messages. There are similar fields for Options and Values as well as all of their corresponding Usage messages.
.subcmds_help_fmt = "'{s}' -> {s}",
// The Global Help Prefix of this Command Type will be used as the default prefix for the auto-generated Help and Usage messages of a Command and all of its Sub Commands.
.global_help_prefix = "CovaDemo w/ Advanced Features",
// The Sub Commands Help Format changes how Sub Commands are listed in Help auto-generated Help messages. There are similar fields for Options and Values as well as all of their corresponding Usage messages.
.subcmds_help_fmt = "'{s}' -> {s}",
});
const Value = cova.Value;
const utils = cova.utils;
```

- Create a Command directly in comptime. This will allow for more granular configuration of the Command and its sub Argument types.
- Create a Command directly in comptime. This will allow for more granular configuration of the Command and its sub Argument Types.
```zig
const setup_cmd: CustomCommand = .{
// The Name of the Command is what app users will use. Similar fields exist for Options and Values as well.
.name = "cova_demo",
// The Description of the Command will be displayed in the auto-generated Help message. Similar fields exist for Options and Values as well.
// The Name of the Command is what end users will use. Similar fields exist for Options and Values as well.
.name = "cova_demo",
// The Description of the Command will be displayed in the auto-generated Help message. Similar fields exist for Options and Values as well.
.description = "A demo of a few of the advanced features in the Cova Library.",
// The `sub_cmds` field is a slice of all of the Sub Commands of this Command.
// The `sub_cmds` field is a slice of all of the Sub Commands of this Command.
.sub_cmds = &.{
.{
.name = "hello-cova",
.description = "Hello from Cova!",
},
},
// The `opts` field is a slice of all of the Options of this Command.
// The `opts` field is a slice of all of the Options of this Command.
.opts = &.{
.{
.name = "string_opt",
Expand All @@ -319,18 +321,18 @@ const setup_cmd: CustomCommand = .{
.val = Value.ofType([]const u8, .{
.name = "string_opt_val",
.description = "This is the Option's wrapped Value. It will handle the validation.",
// Validation Functions are a powerful feature to ensure app user input matches what a project expects. Parsing Functions similarly allow a library user to customize how an argument token is parsed into a specific type.
// Validation Functions are a powerful feature to ensure end user input matches what a project expects. Parsing Functions similarly allow a library user to customize how an argument token is parsed into a specific type.
.valid_fn = struct { fn lessThanTen(arg: []const u8) bool { return arg.len < 10; } }.lessThanTen,
}),
},
},
// The `vals` field is a slice of all of the Values of this Command.
// The `vals` field is a slice of all of the Values of this Command.
.vals = &.{
Value.ofType(i16, .{
.name = "int_val",
.description = "This is an integer Value. It can be set/used up to 5 times.",
// The Set Behavior determines what happens when an app user attempts to use an Option or Value multiple times.
// The Set Behavior determines what happens when an end user attempts to use an Option or Value multiple times.
.set_behavior = .Multi,
.max_args = 5,
}),
Expand All @@ -353,13 +355,13 @@ pub fn main() !void {
defer args_iter.deinit();
try cova.parseArgs(&args_iter, CustomCommand, main_cmd, stdout, .{
// This effectively makes Values optional, but when provided they must still appear in the correct order.
.vals_mandatory = false,
// This will determine which characters are valid to separate an Option from its Value.
.opt_val_seps = "=.^",
// This will disallow the abbreviation of Long Options.
.allow_abbreviated_long_opts = false,
});
// This effectively makes Values optional, but when provided they must still appear in the correct order.
.vals_mandatory = false,
// This will determine which characters are valid to separate an Option from its Value.
.opt_val_seps = "=.^",
// This will disallow the abbreviation of Long Options.
.allow_abbreviated_long_opts = false,
});
try utils.displayCmdInfo(CustomCommand, main_cmd, alloc, stdout);
}
```
Expand All @@ -368,8 +370,8 @@ pub fn main() !void {
```zig
const std = import("std");
const CustomCommand = cova.Command.Custom(.{
.global_help_prefix = "CovaDemo w/ Advanced Features",
.subcmds_help_fmt = "'{s}' -> {s}",
.global_help_prefix = "CovaDemo w/ Advanced Features",
.subcmds_help_fmt = "'{s}' -> {s}",
});
const Value = cova.Value;
const utils = cova.utils;
Expand Down Expand Up @@ -419,10 +421,17 @@ pub fn main() !void {
defer args_iter.deinit();
try cova.parseArgs(&args_iter, CustomCommand, main_cmd, stdout, .{
.vals_mandatory = false,
.opt_val_seps = "=.^",
.allow_abbreviated_long_opts = false,
});
.vals_mandatory = false,
.opt_val_seps = "=.^",
.allow_abbreviated_long_opts = false,
});
try utils.displayCmdInfo(CustomCommand, main_cmd, alloc, stdout);
}
```

## Alternatives
- [yazap](https://github.com/PrajwalCH/yazap)
- [zig-args](https://github.com/MasterQ32/zig-args)
- [zig-clap](https://github.com/Hejsil/zig-clap)
- [zig-parse-args](https://github.com/winksaville/zig-parse-args)

1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub fn build(b: *std.Build) void {
const cova_docs = cova_tests;
cova_docs.emit_docs = .emit;
const build_docs = b.addRunArtifact(cova_docs);
build_docs.has_side_effects = true;
const build_docs_step = b.step("docs", "Build the cova library docs");
build_docs_step.dependOn(&build_docs.step);

Expand Down
Binary file modified docs/cova_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/data.js

Large diffs are not rendered by default.

38 changes: 23 additions & 15 deletions docs/guides/arg_types/command.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
# Command
A Command is a container Argument type for sub Commands, Options, and Values. It can contain any mix of those Argument types or none at all if it's to be used as a standalone Command (i.e. `covademo help`).

They can be converted from and to valid Structs for easy setup and analysis. Other functions for analysis include creating a HashMap<Name, Value/Option> for Options or Values using the respective the `getOpts()` or `getVals()` methods, and using the `checkFlag()` method to simply check if an Argument type was set. Usage and Help statements for a Command can also be generated using the `usage()` and `help()` methods respectively.
## Configuring a Command Type
Before a Command is used within a project, a Command Type should be configured. A Command Type is used to set common-to-all properties of each Command created from it. Typically, this will cover the main Command of a project and all of its sub Commands. The easiest way to configure a Command Type is to simply use `cova.Command.Base`() which is the default Command type. To configure a custom Command type, use `cova.Command.Custom`() with a `cova.Command.Config` (`config`) which provides 7 customizations to set up the Option type, Help/Usage messages, and max sub Arguments. Once configured, the Command Type has access to all of the functions under `cova.Command.Custom` and any Command created from the Command Type similarly has access to all of the corresponding methods.

Command's are meant to be set up in Comptime and used in Runtime. This means that the Command and all of its sub Argument types (Commands, Options, and Values) should be Comptime-known, allowing for proper Validation which provides direct feedback to the library user during compilation instead of preventable errors to the app user during Runtime. After they're set up and Validated, Command's are allocated to the heap for Runtime use. At this point, the data within the Command should be treated as read-only by the libary user, so the library is set up to handle initialized Commands as constants (`const`).
## Setting up a Command
Commands are meant to be set up in Comptime and used in Runtime. This means that the Command and all of its sub Argument types (Commands, Options, and Values) should be Comptime-known, allowing for proper Validation which provides direct feedback to the library user during compilation instead of preventable errors to the end user during Runtime.

## Examples:
### Direct Creation
```
There are two ways to set up a Command. The first is to use Zig's standard syntax for creating a struct instance and fill in the fields of the previously configured Command Type. Alternatively, if the project has a Struct Type that can be represented as a Command, the `cova.Command.Custom.from`() function can be used to create the Command.

After they're set up Commands should be Validated and Allocated to the heap for Runtime use. This is accomplished using `cova.Command.Custom.init()`. At this point, the data within the Command should be treated as read-only by the libary user, so the library is set up to handle initialized Commands as constants (`const`).

## Additional Info
Commands can also be converted to valid Structs for easy analysis using the `cova.Command.Custom.to`() function. Other functions for analysis include creating a String HashMap<Name, Value/Option> for Options or Values using the respective `cova.Command.Custom.getOpts`() or `cova.Command.Custom.getVals`() methods, and using the `cova.Command.Custom.checkFlag`() method to simply check if an Argument type was set. Usage and Help statements for a Command can also be generated using the `cova.Command.Custom.usage`() and `cova.Command.Custom.help`() methods respectively.

## Example:
```zig
...
pub const cova = @import("cova");
pub const CustomCommand = cova.CustomCommand(.{ global_help_prefix = "CovaDemo" });
pub const CommandT = cova.CustomCommand(.{ global_help_prefix = "CovaDemo" });
// Comptime Setup
const setup_cmd: CustomCommand = .{
const setup_cmd: CommandT = .{
.name = "covademo",
.description = "A demo of the Cova command line argument parser.",
.sub_cmds = &.{
.{
.name = "sub_cmd",
.description = "This is a Sub Command within the 'covademo' main Command.",
},
command_from_elsewhere,
CustomCommand.from(some_valid_struct),
command_from_elsewhere,
CommandT.from(SomeValidStructType),
}
.opts = { ... },
.vals = { ... }
}
pub fn main() !void {
...
// Runtime Use
const main_cmd = try setup_cmd.init(alloc);
defer main_cmd.deinit();
...
// Runtime Use
const main_cmd = try setup_cmd.init(alloc);
defer main_cmd.deinit();
cova.parseArgs(..., main_cmd, ...);
utils.displayCmdInfo(CustomCommand, main_cmd, alloc, stdout);
cova.parseArgs(..., main_cmd, ...);
utils.displayCmdInfo(CustomCommand, main_cmd, alloc, stdout);
}
```
Loading

0 comments on commit 25cbab0

Please sign in to comment.