diff options
| -rw-r--r-- | README.md | 73 | ||||
| -rw-r--r-- | example/comptime-clap.zig | 35 | ||||
| -rw-r--r-- | example/streaming-clap.zig | 10 | ||||
| -rw-r--r-- | src/index.zig | 134 |
4 files changed, 226 insertions, 26 deletions
| @@ -107,3 +107,76 @@ zig-clap/example/comptime-clap.zig:41:18: note: called from here | |||
| 107 | ``` | 107 | ``` |
| 108 | 108 | ||
| 109 | Ofc, this limits you to use only parameters that are comptime known. | 109 | Ofc, this limits you to use only parameters that are comptime known. |
| 110 | |||
| 111 | ### `help` | ||
| 112 | |||
| 113 | The `help` and `helpEx` are functions for printing a simple list of all parameters the | ||
| 114 | program can take. | ||
| 115 | |||
| 116 | ```rust | ||
| 117 | const stderr_file = try std.io.getStdErr(); | ||
| 118 | var stderr_out_stream = stderr_file.outStream(); | ||
| 119 | const stderr = &stderr_out_stream.stream; | ||
| 120 | |||
| 121 | try clap.help( | ||
| 122 | stderr, | ||
| 123 | []clap.Param([]const u8){ | ||
| 124 | clap.Param([]const u8).flag( | ||
| 125 | "Display this help and exit.", | ||
| 126 | clap.Names.prefix("help"), | ||
| 127 | ), | ||
| 128 | clap.Param([]const u8).flag( | ||
| 129 | "Output version information and exit.", | ||
| 130 | clap.Names.prefix("version"), | ||
| 131 | ), | ||
| 132 | }, | ||
| 133 | ); | ||
| 134 | ``` | ||
| 135 | |||
| 136 | ``` | ||
| 137 | -h, --help Display this help and exit. | ||
| 138 | -v, --version Output version information and exit. | ||
| 139 | ``` | ||
| 140 | |||
| 141 | The `help` function is the simplest to call. It only takes an `OutStream` and a slice of | ||
| 142 | `Param([]const u8)`. This function assumes that the id of each parameter is the help message. | ||
| 143 | |||
| 144 | The `clap.helpEx` is the generic version of `help`. It can print a help message for any | ||
| 145 | `Param`, but requires some extra arguments for it to work. | ||
| 146 | |||
| 147 | ```rust | ||
| 148 | fn getHelp(_: void, param: clap.Param(u8)) error{}![]const u8 { | ||
| 149 | return switch (param.id) { | ||
| 150 | 'h' => "Display this help and exit.", | ||
| 151 | 'v' => "Output version information and exit.", | ||
| 152 | else => unreachable, | ||
| 153 | }; | ||
| 154 | } | ||
| 155 | |||
| 156 | fn getValue(_: void, param: clap.Param(u8)) error{}![]const u8 { | ||
| 157 | return ""; | ||
| 158 | } | ||
| 159 | |||
| 160 | const stderr_file = try std.io.getStdErr(); | ||
| 161 | var stderr_out_stream = stderr_file.outStream(); | ||
| 162 | const stderr = &stderr_out_stream.stream; | ||
| 163 | |||
| 164 | try stderr.print("\n"); | ||
| 165 | try clap.helpEx( | ||
| 166 | stderr, | ||
| 167 | u8, | ||
| 168 | []clap.Param(u8){ | ||
| 169 | clap.Param(u8).flag('h', clap.Names.prefix("help")), | ||
| 170 | clap.Param(u8).flag('v', clap.Names.prefix("version")), | ||
| 171 | }, | ||
| 172 | error{}, | ||
| 173 | {}, | ||
| 174 | getHelp, | ||
| 175 | getValue, | ||
| 176 | ); | ||
| 177 | ``` | ||
| 178 | |||
| 179 | ``` | ||
| 180 | -h, --help Display this help and exit. | ||
| 181 | -v, --version Output version information and exit. | ||
| 182 | ``` | ||
diff --git a/example/comptime-clap.zig b/example/comptime-clap.zig index 3b7b42b..b275dc7 100644 --- a/example/comptime-clap.zig +++ b/example/comptime-clap.zig | |||
| @@ -4,25 +4,25 @@ const clap = @import("clap"); | |||
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub fn main() !void { |
| 7 | const stdout_file = try std.io.getStdOut(); | ||
| 8 | var stdout_out_stream = stdout_file.outStream(); | ||
| 9 | const stdout = &stdout_out_stream.stream; | ||
| 10 | |||
| 7 | var direct_allocator = std.heap.DirectAllocator.init(); | 11 | var direct_allocator = std.heap.DirectAllocator.init(); |
| 8 | const allocator = &direct_allocator.allocator; | 12 | const allocator = &direct_allocator.allocator; |
| 9 | defer direct_allocator.deinit(); | 13 | defer direct_allocator.deinit(); |
| 10 | 14 | ||
| 11 | // First we specify what parameters our program can take. | 15 | // First we specify what parameters our program can take. |
| 12 | const params = comptime []clap.Param(void){ | 16 | const params = comptime []clap.Param([]const u8){ |
| 13 | // Param.init takes 3 arguments. | 17 | clap.Param([]const u8).flag( |
| 14 | // * An "id", which can be any type specified by the argument to Param. The | 18 | "Display this help and exit.", |
| 15 | // ComptimeClap expects clap.Param(void) only. | 19 | clap.Names.prefix("help") |
| 16 | // * A bool which determins wether the parameter takes a value. | 20 | ), |
| 17 | // * A "Names" struct, which determins what names the parameter will have on the | 21 | clap.Param([]const u8).option( |
| 18 | // commandline. Names.prefix inits a "Names" struct that has the "short" name | 22 | "An option parameter, which takes a value.", |
| 19 | // set to the first letter, and the "long" name set to the full name. | 23 | clap.Names.prefix("number"), |
| 20 | clap.Param(void).flag({}, clap.Names.prefix("help")), | 24 | ), |
| 21 | clap.Param(void).option({}, clap.Names.prefix("number")), | 25 | clap.Param([]const u8).positional(""), |
| 22 | |||
| 23 | // Names.positional returns a "Names" struct where neither the "short" or "long" | ||
| 24 | // name is set. | ||
| 25 | clap.Param(void).positional({}), | ||
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | // We then initialize an argument iterator. We will use the OsIterator as it nicely | 28 | // We then initialize an argument iterator. We will use the OsIterator as it nicely |
| @@ -35,11 +35,14 @@ pub fn main() !void { | |||
| 35 | const exe = try iter.next(); | 35 | const exe = try iter.next(); |
| 36 | 36 | ||
| 37 | // Finally we can parse the arguments | 37 | // Finally we can parse the arguments |
| 38 | var args = try clap.ComptimeClap(void, params).parse(allocator, clap.args.OsIterator.Error, iter); | 38 | var args = try clap.ComptimeClap([]const u8, params).parse(allocator, clap.args.OsIterator.Error, iter); |
| 39 | defer args.deinit(); | 39 | defer args.deinit(); |
| 40 | 40 | ||
| 41 | // clap.help is a function that can print a simple help message, given a | ||
| 42 | // slice of Param([]const u8). There is also a helpEx, which can print a | ||
| 43 | // help message for any Param, but it is more verbose to call. | ||
| 41 | if (args.flag("--help")) | 44 | if (args.flag("--help")) |
| 42 | debug.warn("Help!\n"); | 45 | return try clap.help(stdout, params); |
| 43 | if (args.option("--number")) |n| | 46 | if (args.option("--number")) |n| |
| 44 | debug.warn("--number = {}\n", n); | 47 | debug.warn("--number = {}\n", n); |
| 45 | for (args.positionals()) |pos| | 48 | for (args.positionals()) |pos| |
diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig index 013c1d4..57ebe71 100644 --- a/example/streaming-clap.zig +++ b/example/streaming-clap.zig | |||
| @@ -10,18 +10,8 @@ pub fn main() !void { | |||
| 10 | 10 | ||
| 11 | // First we specify what parameters our program can take. | 11 | // First we specify what parameters our program can take. |
| 12 | const params = []clap.Param(u8){ | 12 | const params = []clap.Param(u8){ |
| 13 | // Param.init takes 3 arguments. | ||
| 14 | // * An "id", which can be any type specified by the argument to Param. Here, we | ||
| 15 | // use a "u8" as the "id" type. | ||
| 16 | // * A bool which determins wether the parameter takes a value. | ||
| 17 | // * A "Names" struct, which determins what names the parameter will have on the | ||
| 18 | // commandline. Names.prefix inits a "Names" struct that has the "short" name | ||
| 19 | // set to the first letter, and the "long" name set to the full name. | ||
| 20 | clap.Param(u8).flag('h', clap.Names.prefix("help")), | 13 | clap.Param(u8).flag('h', clap.Names.prefix("help")), |
| 21 | clap.Param(u8).option('n', clap.Names.prefix("number")), | 14 | clap.Param(u8).option('n', clap.Names.prefix("number")), |
| 22 | |||
| 23 | // Names.positional returns a "Names" struct where neither the "short" or "long" | ||
| 24 | // name is set. | ||
| 25 | clap.Param(u8).positional('f'), | 15 | clap.Param(u8).positional('f'), |
| 26 | }; | 16 | }; |
| 27 | 17 | ||
diff --git a/src/index.zig b/src/index.zig index 225eb9c..0914176 100644 --- a/src/index.zig +++ b/src/index.zig | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const io = std.io; | ||
| 5 | const mem = std.mem; | ||
| 4 | 6 | ||
| 5 | pub const @"comptime" = @import("comptime.zig"); | 7 | pub const @"comptime" = @import("comptime.zig"); |
| 6 | pub const args = @import("args.zig"); | 8 | pub const args = @import("args.zig"); |
| @@ -106,3 +108,135 @@ pub fn Param(comptime Id: type) type { | |||
| 106 | } | 108 | } |
| 107 | }; | 109 | }; |
| 108 | } | 110 | } |
| 111 | |||
| 112 | /// Will print a help message in the following format: | ||
| 113 | /// -s, --long=value_text help_text | ||
| 114 | /// -s, help_text | ||
| 115 | /// --long help_text | ||
| 116 | pub fn helpEx( | ||
| 117 | stream: var, | ||
| 118 | comptime Id: type, | ||
| 119 | params: []const Param(Id), | ||
| 120 | comptime Error: type, | ||
| 121 | context: var, | ||
| 122 | help_text: fn(@typeOf(context), Param(Id)) Error![]const u8, | ||
| 123 | value_text: fn(@typeOf(context), Param(Id)) Error![]const u8, | ||
| 124 | ) !void { | ||
| 125 | const max_spacing = blk: { | ||
| 126 | var null_stream = io.NullOutStream.init(); | ||
| 127 | var res: usize = 0; | ||
| 128 | for (params) |param| { | ||
| 129 | var counting_stream = io.CountingOutStream(io.NullOutStream.Error).init(&null_stream.stream); | ||
| 130 | try printParam(&counting_stream.stream, Id, param, Error, context, value_text); | ||
| 131 | if (res < counting_stream.bytes_written) | ||
| 132 | res = counting_stream.bytes_written; | ||
| 133 | } | ||
| 134 | |||
| 135 | break :blk res; | ||
| 136 | }; | ||
| 137 | |||
| 138 | for (params) |param| { | ||
| 139 | if (param.names.short == null and param.names.long == null) | ||
| 140 | continue; | ||
| 141 | |||
| 142 | var counting_stream = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); | ||
| 143 | try stream.print("\t"); | ||
| 144 | try printParam(&counting_stream.stream, Id, param, Error, context, value_text); | ||
| 145 | try stream.writeByteNTimes(' ', max_spacing - counting_stream.bytes_written); | ||
| 146 | try stream.print("\t{}\n", try help_text(context, param)); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | fn printParam( | ||
| 151 | stream: var, | ||
| 152 | comptime Id: type, | ||
| 153 | param: Param(Id), | ||
| 154 | comptime Error: type, | ||
| 155 | context: var, | ||
| 156 | value_text: fn(@typeOf(context), Param(Id)) Error![]const u8, | ||
| 157 | ) @typeOf(stream.*).Error!void { | ||
| 158 | if (param.names.short) |s| { | ||
| 159 | try stream.print("-{c}", s); | ||
| 160 | } else { | ||
| 161 | try stream.print(" "); | ||
| 162 | } | ||
| 163 | if (param.names.long) |l| { | ||
| 164 | if (param.names.short) |_| { | ||
| 165 | try stream.print(", "); | ||
| 166 | } else { | ||
| 167 | try stream.print(" "); | ||
| 168 | } | ||
| 169 | |||
| 170 | try stream.print("--{}", l); | ||
| 171 | } | ||
| 172 | if (param.takes_value) | ||
| 173 | try stream.print("={}", value_text(context, param)); | ||
| 174 | } | ||
| 175 | |||
| 176 | /// A wrapper around helpEx that takes a Param([]const u8) and uses the string id | ||
| 177 | /// as the help text for each paramter. | ||
| 178 | pub fn help(stream: var, params: []const Param([]const u8)) !void { | ||
| 179 | try helpEx(stream, []const u8, params, error{}, {}, getHelpSimple, getValueSimple); | ||
| 180 | } | ||
| 181 | |||
| 182 | fn getHelpSimple(context: void, param: Param([]const u8)) error{}![]const u8 { | ||
| 183 | return param.id; | ||
| 184 | } | ||
| 185 | |||
| 186 | fn getValueSimple(context: void, param: Param([]const u8)) error{}![]const u8 { | ||
| 187 | return "VALUE"; | ||
| 188 | } | ||
| 189 | |||
| 190 | |||
| 191 | test "clap.help" { | ||
| 192 | var buf: [1024]u8 = undefined; | ||
| 193 | var slice_stream = io.SliceOutStream.init(buf[0..]); | ||
| 194 | try help( | ||
| 195 | &slice_stream.stream, | ||
| 196 | []Param([]const u8){ | ||
| 197 | Param([]const u8).flag( | ||
| 198 | "Short flag.", | ||
| 199 | Names.short('a'), | ||
| 200 | ), | ||
| 201 | Param([]const u8).option( | ||
| 202 | "Short option.", | ||
| 203 | Names.short('b'), | ||
| 204 | ), | ||
| 205 | Param([]const u8).flag( | ||
| 206 | "Long flag.", | ||
| 207 | Names.long("aa"), | ||
| 208 | ), | ||
| 209 | Param([]const u8).option( | ||
| 210 | "Long option.", | ||
| 211 | Names.long("bb"), | ||
| 212 | ), | ||
| 213 | Param([]const u8).flag( | ||
| 214 | "Both flag.", | ||
| 215 | Names.prefix("cc"), | ||
| 216 | ), | ||
| 217 | Param([]const u8).option( | ||
| 218 | "Both option.", | ||
| 219 | Names.prefix("dd"), | ||
| 220 | ), | ||
| 221 | Param([]const u8).positional( | ||
| 222 | "Positional. This should not appear in the help message." | ||
| 223 | ), | ||
| 224 | }, | ||
| 225 | ); | ||
| 226 | |||
| 227 | const expected = | ||
| 228 | "\t-a \tShort flag.\n" ++ | ||
| 229 | "\t-b=VALUE \tShort option.\n" ++ | ||
| 230 | "\t --aa \tLong flag.\n" ++ | ||
| 231 | "\t --bb=VALUE\tLong option.\n" ++ | ||
| 232 | "\t-c, --cc \tBoth flag.\n" ++ | ||
| 233 | "\t-d, --dd=VALUE\tBoth option.\n"; | ||
| 234 | |||
| 235 | if (!mem.eql(u8, slice_stream.getWritten(), expected)) { | ||
| 236 | debug.warn("============ Expected ============\n"); | ||
| 237 | debug.warn("{}", expected); | ||
| 238 | debug.warn("============= Actual =============\n"); | ||
| 239 | debug.warn("{}", slice_stream.getWritten()); | ||
| 240 | return error.NoMatch; | ||
| 241 | } | ||
| 242 | } | ||