summaryrefslogtreecommitdiff
path: root/clap.zig
diff options
context:
space:
mode:
Diffstat (limited to 'clap.zig')
-rw-r--r--clap.zig183
1 files changed, 88 insertions, 95 deletions
diff --git a/clap.zig b/clap.zig
index f7eb5d0..21590a2 100644
--- a/clap.zig
+++ b/clap.zig
@@ -1,24 +1,3 @@
1const std = @import("std");
2
3const builtin = std.builtin;
4const debug = std.debug;
5const heap = std.heap;
6const io = std.io;
7const math = std.math;
8const mem = std.mem;
9const meta = std.meta;
10const process = std.process;
11const testing = std.testing;
12
13pub const args = @import("clap/args.zig");
14pub const parsers = @import("clap/parsers.zig");
15pub const streaming = @import("clap/streaming.zig");
16pub const ccw = @import("clap/codepoint_counting_writer.zig");
17
18test "clap" {
19 testing.refAllDecls(@This());
20}
21
22pub const default_assignment_separators = "="; 1pub const default_assignment_separators = "=";
23 2
24/// The names a `Param` can have. 3/// The names a `Param` can have.
@@ -97,14 +76,14 @@ pub fn Param(comptime Id: type) type {
97 76
98/// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice 77/// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice
99/// containing all the parsed params. The caller is responsible for freeing the slice. 78/// containing all the parsed params. The caller is responsible for freeing the slice.
100pub fn parseParams(allocator: mem.Allocator, str: []const u8) ![]Param(Help) { 79pub fn parseParams(allocator: std.mem.Allocator, str: []const u8) ![]Param(Help) {
101 var end: usize = undefined; 80 var end: usize = undefined;
102 return parseParamsEx(allocator, str, &end); 81 return parseParamsEx(allocator, str, &end);
103} 82}
104 83
105/// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice 84/// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice
106/// containing all the parsed params. The caller is responsible for freeing the slice. 85/// containing all the parsed params. The caller is responsible for freeing the slice.
107pub fn parseParamsEx(allocator: mem.Allocator, str: []const u8, end: *usize) ![]Param(Help) { 86pub fn parseParamsEx(allocator: std.mem.Allocator, str: []const u8, end: *usize) ![]Param(Help) {
108 var list = std.ArrayList(Param(Help)).init(allocator); 87 var list = std.ArrayList(Param(Help)).init(allocator);
109 errdefer list.deinit(); 88 errdefer list.deinit();
110 89
@@ -135,11 +114,11 @@ fn countParams(str: []const u8) usize {
135 @setEvalBranchQuota(std.math.maxInt(u32)); 114 @setEvalBranchQuota(std.math.maxInt(u32));
136 115
137 var res: usize = 0; 116 var res: usize = 0;
138 var it = mem.splitScalar(u8, str, '\n'); 117 var it = std.mem.splitScalar(u8, str, '\n');
139 while (it.next()) |line| { 118 while (it.next()) |line| {
140 const trimmed = mem.trimLeft(u8, line, " \t"); 119 const trimmed = std.mem.trimLeft(u8, line, " \t");
141 if (mem.startsWith(u8, trimmed, "-") or 120 if (std.mem.startsWith(u8, trimmed, "-") or
142 mem.startsWith(u8, trimmed, "<")) 121 std.mem.startsWith(u8, trimmed, "<"))
143 { 122 {
144 res += 1; 123 res += 1;
145 } 124 }
@@ -152,7 +131,7 @@ fn countParams(str: []const u8) usize {
152/// is returned, containing all the parameters parsed. This function will fail if the input slice 131/// is returned, containing all the parameters parsed. This function will fail if the input slice
153/// is to small. 132/// is to small.
154pub fn parseParamsIntoSlice(slice: []Param(Help), str: []const u8) ![]Param(Help) { 133pub fn parseParamsIntoSlice(slice: []Param(Help), str: []const u8) ![]Param(Help) {
155 var null_alloc = heap.FixedBufferAllocator.init(""); 134 var null_alloc = std.heap.FixedBufferAllocator.init("");
156 var list = std.ArrayList(Param(Help)){ 135 var list = std.ArrayList(Param(Help)){
157 .allocator = null_alloc.allocator(), 136 .allocator = null_alloc.allocator(),
158 .items = slice[0..0], 137 .items = slice[0..0],
@@ -167,7 +146,7 @@ pub fn parseParamsIntoSlice(slice: []Param(Help), str: []const u8) ![]Param(Help
167/// is returned, containing all the parameters parsed. This function will fail if the input slice 146/// is returned, containing all the parameters parsed. This function will fail if the input slice
168/// is to small. 147/// is to small.
169pub fn parseParamsIntoSliceEx(slice: []Param(Help), str: []const u8, end: *usize) ![]Param(Help) { 148pub fn parseParamsIntoSliceEx(slice: []Param(Help), str: []const u8, end: *usize) ![]Param(Help) {
170 var null_alloc = heap.FixedBufferAllocator.init(""); 149 var null_alloc = std.heap.FixedBufferAllocator.init("");
171 var list = std.ArrayList(Param(Help)){ 150 var list = std.ArrayList(Param(Help)){
172 .allocator = null_alloc.allocator(), 151 .allocator = null_alloc.allocator(),
173 .items = slice[0..0], 152 .items = slice[0..0],
@@ -410,7 +389,7 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) {
410 389
411fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void { 390fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void {
412 var end: usize = undefined; 391 var end: usize = undefined;
413 const actual_params = parseParamsEx(testing.allocator, str, &end) catch |err| { 392 const actual_params = parseParamsEx(std.testing.allocator, str, &end) catch |err| {
414 const loc = std.zig.findLineColumn(str, end); 393 const loc = std.zig.findLineColumn(str, end);
415 std.debug.print("error:{}:{}: Failed to parse parameter:\n{s}\n", .{ 394 std.debug.print("error:{}:{}: Failed to parse parameter:\n{s}\n", .{
416 loc.line + 1, 395 loc.line + 1,
@@ -419,22 +398,22 @@ fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void
419 }); 398 });
420 return err; 399 return err;
421 }; 400 };
422 defer testing.allocator.free(actual_params); 401 defer std.testing.allocator.free(actual_params);
423 402
424 try testing.expectEqual(expected_params.len, actual_params.len); 403 try std.testing.expectEqual(expected_params.len, actual_params.len);
425 for (expected_params, 0..) |_, i| 404 for (expected_params, 0..) |_, i|
426 try expectParam(expected_params[i], actual_params[i]); 405 try expectParam(expected_params[i], actual_params[i]);
427} 406}
428 407
429fn expectParam(expect: Param(Help), actual: Param(Help)) !void { 408fn expectParam(expect: Param(Help), actual: Param(Help)) !void {
430 try testing.expectEqualStrings(expect.id.desc, actual.id.desc); 409 try std.testing.expectEqualStrings(expect.id.desc, actual.id.desc);
431 try testing.expectEqualStrings(expect.id.val, actual.id.val); 410 try std.testing.expectEqualStrings(expect.id.val, actual.id.val);
432 try testing.expectEqual(expect.names.short, actual.names.short); 411 try std.testing.expectEqual(expect.names.short, actual.names.short);
433 try testing.expectEqual(expect.takes_value, actual.takes_value); 412 try std.testing.expectEqual(expect.takes_value, actual.takes_value);
434 if (expect.names.long) |long| { 413 if (expect.names.long) |long| {
435 try testing.expectEqualStrings(long, actual.names.long.?); 414 try std.testing.expectEqualStrings(long, actual.names.long.?);
436 } else { 415 } else {
437 try testing.expectEqual(@as(?[]const u8, null), actual.names.long); 416 try std.testing.expectEqual(@as(?[]const u8, null), actual.names.long);
438 } 417 }
439} 418}
440 419
@@ -549,11 +528,11 @@ test "parseParams" {
549 }, 528 },
550 }); 529 });
551 530
552 try testing.expectError(error.InvalidParameter, parseParam("--long, Help")); 531 try std.testing.expectError(error.InvalidParameter, parseParam("--long, Help"));
553 try testing.expectError(error.InvalidParameter, parseParam("-s, Help")); 532 try std.testing.expectError(error.InvalidParameter, parseParam("-s, Help"));
554 try testing.expectError(error.InvalidParameter, parseParam("-ss Help")); 533 try std.testing.expectError(error.InvalidParameter, parseParam("-ss Help"));
555 try testing.expectError(error.InvalidParameter, parseParam("-ss <val> Help")); 534 try std.testing.expectError(error.InvalidParameter, parseParam("-ss <val> Help"));
556 try testing.expectError(error.InvalidParameter, parseParam("- Help")); 535 try std.testing.expectError(error.InvalidParameter, parseParam("- Help"));
557} 536}
558 537
559/// Optional diagnostics used for reporting useful errors 538/// Optional diagnostics used for reporting useful errors
@@ -588,9 +567,9 @@ pub const Diagnostic = struct {
588 567
589fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) !void { 568fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) !void {
590 var buf: [1024]u8 = undefined; 569 var buf: [1024]u8 = undefined;
591 var slice_stream = io.fixedBufferStream(&buf); 570 var slice_stream = std.io.fixedBufferStream(&buf);
592 diag.report(slice_stream.writer(), err) catch unreachable; 571 diag.report(slice_stream.writer(), err) catch unreachable;
593 try testing.expectEqualStrings(expected, slice_stream.getWritten()); 572 try std.testing.expectEqualStrings(expected, slice_stream.getWritten());
594} 573}
595 574
596test "Diagnostic.report" { 575test "Diagnostic.report" {
@@ -644,7 +623,7 @@ test "Diagnostic.report" {
644 623
645/// Options that can be set to customize the behavior of parsing. 624/// Options that can be set to customize the behavior of parsing.
646pub const ParseOptions = struct { 625pub const ParseOptions = struct {
647 allocator: mem.Allocator, 626 allocator: std.mem.Allocator,
648 diagnostic: ?*Diagnostic = null, 627 diagnostic: ?*Diagnostic = null,
649 628
650 /// The assignment separators, which by default is `=`. This is the separator between the name 629 /// The assignment separators, which by default is `=`. This is the separator between the name
@@ -666,10 +645,10 @@ pub fn parse(
666 comptime value_parsers: anytype, 645 comptime value_parsers: anytype,
667 opt: ParseOptions, 646 opt: ParseOptions,
668) !Result(Id, params, value_parsers) { 647) !Result(Id, params, value_parsers) {
669 var arena = heap.ArenaAllocator.init(opt.allocator); 648 var arena = std.heap.ArenaAllocator.init(opt.allocator);
670 errdefer arena.deinit(); 649 errdefer arena.deinit();
671 650
672 var iter = try process.ArgIterator.initWithAllocator(arena.allocator()); 651 var iter = try std.process.ArgIterator.initWithAllocator(arena.allocator());
673 const exe_arg = iter.next(); 652 const exe_arg = iter.next();
674 653
675 const result = try parseEx(Id, params, value_parsers, &iter, .{ 654 const result = try parseEx(Id, params, value_parsers, &iter, .{
@@ -745,7 +724,7 @@ pub fn parseEx(
745 var arguments = Arguments(Id, params, value_parsers, .list){}; 724 var arguments = Arguments(Id, params, value_parsers, .list){};
746 errdefer deinitArgs(Id, params, allocator, &arguments); 725 errdefer deinitArgs(Id, params, allocator, &arguments);
747 726
748 var stream = streaming.Clap(Id, meta.Child(@TypeOf(iter))){ 727 var stream = streaming.Clap(Id, std.meta.Child(@TypeOf(iter))){
749 .params = params, 728 .params = params,
750 .iter = iter, 729 .iter = iter,
751 .diagnostic = opt.diagnostic, 730 .diagnostic = opt.diagnostic,
@@ -785,7 +764,7 @@ pub fn parseEx(
785 // We are done parsing, but our arguments are stored in lists, and not slices. Map the list 764 // We are done parsing, but our arguments are stored in lists, and not slices. Map the list
786 // fields to slices and return that. 765 // fields to slices and return that.
787 var result_args = Arguments(Id, params, value_parsers, .slice){}; 766 var result_args = Arguments(Id, params, value_parsers, .slice){};
788 inline for (meta.fields(@TypeOf(arguments))) |field| { 767 inline for (std.meta.fields(@TypeOf(arguments))) |field| {
789 if (@typeInfo(field.type) == .@"struct" and 768 if (@typeInfo(field.type) == .@"struct" and
790 @hasDecl(field.type, "toOwnedSlice")) 769 @hasDecl(field.type, "toOwnedSlice"))
791 { 770 {
@@ -812,7 +791,7 @@ pub fn ResultEx(
812 return struct { 791 return struct {
813 args: Arguments(Id, params, value_parsers, .slice), 792 args: Arguments(Id, params, value_parsers, .slice),
814 positionals: []const FindPositionalType(Id, params, value_parsers), 793 positionals: []const FindPositionalType(Id, params, value_parsers),
815 allocator: mem.Allocator, 794 allocator: std.mem.Allocator,
816 795
817 pub fn deinit(result: *@This()) void { 796 pub fn deinit(result: *@This()) void {
818 deinitArgs(Id, params, result.allocator, &result.args); 797 deinitArgs(Id, params, result.allocator, &result.args);
@@ -859,7 +838,7 @@ fn ParamType(
859fn deinitArgs( 838fn deinitArgs(
860 comptime Id: type, 839 comptime Id: type,
861 comptime params: []const Param(Id), 840 comptime params: []const Param(Id),
862 allocator: mem.Allocator, 841 allocator: std.mem.Allocator,
863 arguments: anytype, 842 arguments: anytype,
864) void { 843) void {
865 inline for (params) |param| { 844 inline for (params) |param| {
@@ -899,7 +878,7 @@ fn Arguments(
899 fields_len += 1; 878 fields_len += 1;
900 } 879 }
901 880
902 var fields: [fields_len]builtin.Type.StructField = undefined; 881 var fields: [fields_len]std.builtin.Type.StructField = undefined;
903 var i: usize = 0; 882 var i: usize = 0;
904 for (params) |param| { 883 for (params) |param| {
905 const longest = param.names.longest(); 884 const longest = param.names.longest();
@@ -946,7 +925,7 @@ test "str and u64" {
946 .args = &.{ "--num", "10", "--str", "cooley_rec_inp_ptr" }, 925 .args = &.{ "--num", "10", "--str", "cooley_rec_inp_ptr" },
947 }; 926 };
948 var res = try parseEx(Help, &params, parsers.default, &iter, .{ 927 var res = try parseEx(Help, &params, parsers.default, &iter, .{
949 .allocator = testing.allocator, 928 .allocator = std.testing.allocator,
950 }); 929 });
951 defer res.deinit(); 930 defer res.deinit();
952} 931}
@@ -961,12 +940,12 @@ test "different assignment separators" {
961 .args = &.{ "-a=0", "--aa=1", "-a:2", "--aa:3" }, 940 .args = &.{ "-a=0", "--aa=1", "-a:2", "--aa:3" },
962 }; 941 };
963 var res = try parseEx(Help, &params, parsers.default, &iter, .{ 942 var res = try parseEx(Help, &params, parsers.default, &iter, .{
964 .allocator = testing.allocator, 943 .allocator = std.testing.allocator,
965 .assignment_separators = "=:", 944 .assignment_separators = "=:",
966 }); 945 });
967 defer res.deinit(); 946 defer res.deinit();
968 947
969 try testing.expectEqualSlices(usize, &.{ 0, 1, 2, 3 }, res.args.aa); 948 try std.testing.expectEqualSlices(usize, &.{ 0, 1, 2, 3 }, res.args.aa);
970} 949}
971 950
972test "everything" { 951test "everything" {
@@ -984,18 +963,18 @@ test "everything" {
984 .args = &.{ "-a", "--aa", "-c", "0", "something", "-d", "1", "--dd", "2", "-h" }, 963 .args = &.{ "-a", "--aa", "-c", "0", "something", "-d", "1", "--dd", "2", "-h" },
985 }; 964 };
986 var res = try parseEx(Help, &params, parsers.default, &iter, .{ 965 var res = try parseEx(Help, &params, parsers.default, &iter, .{
987 .allocator = testing.allocator, 966 .allocator = std.testing.allocator,
988 }); 967 });
989 defer res.deinit(); 968 defer res.deinit();
990 969
991 try testing.expect(res.args.aa == 2); 970 try std.testing.expect(res.args.aa == 2);
992 try testing.expect(res.args.bb == 0); 971 try std.testing.expect(res.args.bb == 0);
993 try testing.expect(res.args.h == 1); 972 try std.testing.expect(res.args.h == 1);
994 try testing.expectEqualStrings("0", res.args.cc.?); 973 try std.testing.expectEqualStrings("0", res.args.cc.?);
995 try testing.expectEqual(@as(usize, 1), res.positionals.len); 974 try std.testing.expectEqual(@as(usize, 1), res.positionals.len);
996 try testing.expectEqualStrings("something", res.positionals[0]); 975 try std.testing.expectEqualStrings("something", res.positionals[0]);
997 try testing.expectEqualSlices(usize, &.{ 1, 2 }, res.args.dd); 976 try std.testing.expectEqualSlices(usize, &.{ 1, 2 }, res.args.dd);
998 try testing.expectEqual(@as(usize, 10), iter.index); 977 try std.testing.expectEqual(@as(usize, 10), iter.index);
999} 978}
1000 979
1001test "terminating positional" { 980test "terminating positional" {
@@ -1013,19 +992,19 @@ test "terminating positional" {
1013 .args = &.{ "-a", "--aa", "-c", "0", "something", "-d", "1", "--dd", "2", "-h" }, 992 .args = &.{ "-a", "--aa", "-c", "0", "something", "-d", "1", "--dd", "2", "-h" },
1014 }; 993 };
1015 var res = try parseEx(Help, &params, parsers.default, &iter, .{ 994 var res = try parseEx(Help, &params, parsers.default, &iter, .{
1016 .allocator = testing.allocator, 995 .allocator = std.testing.allocator,
1017 .terminating_positional = 0, 996 .terminating_positional = 0,
1018 }); 997 });
1019 defer res.deinit(); 998 defer res.deinit();
1020 999
1021 try testing.expect(res.args.aa == 2); 1000 try std.testing.expect(res.args.aa == 2);
1022 try testing.expect(res.args.bb == 0); 1001 try std.testing.expect(res.args.bb == 0);
1023 try testing.expect(res.args.h == 0); 1002 try std.testing.expect(res.args.h == 0);
1024 try testing.expectEqualStrings("0", res.args.cc.?); 1003 try std.testing.expectEqualStrings("0", res.args.cc.?);
1025 try testing.expectEqual(@as(usize, 1), res.positionals.len); 1004 try std.testing.expectEqual(@as(usize, 1), res.positionals.len);
1026 try testing.expectEqualStrings("something", res.positionals[0]); 1005 try std.testing.expectEqualStrings("something", res.positionals[0]);
1027 try testing.expectEqualSlices(usize, &.{}, res.args.dd); 1006 try std.testing.expectEqualSlices(usize, &.{}, res.args.dd);
1028 try testing.expectEqual(@as(usize, 5), iter.index); 1007 try std.testing.expectEqual(@as(usize, 5), iter.index);
1029} 1008}
1030 1009
1031test "overflow-safe" { 1010test "overflow-safe" {
@@ -1039,7 +1018,7 @@ test "overflow-safe" {
1039 1018
1040 // This just needs to not crash 1019 // This just needs to not crash
1041 var res = try parseEx(Help, &params, parsers.default, &iter, .{ 1020 var res = try parseEx(Help, &params, parsers.default, &iter, .{
1042 .allocator = testing.allocator, 1021 .allocator = std.testing.allocator,
1043 }); 1022 });
1044 defer res.deinit(); 1023 defer res.deinit();
1045} 1024}
@@ -1047,7 +1026,7 @@ test "overflow-safe" {
1047test "empty" { 1026test "empty" {
1048 var iter = args.SliceIterator{ .args = &.{} }; 1027 var iter = args.SliceIterator{ .args = &.{} };
1049 var res = try parseEx(u8, &[_]Param(u8){}, parsers.default, &iter, .{ 1028 var res = try parseEx(u8, &[_]Param(u8){}, parsers.default, &iter, .{
1050 .allocator = testing.allocator, 1029 .allocator = std.testing.allocator,
1051 }); 1030 });
1052 defer res.deinit(); 1031 defer res.deinit();
1053} 1032}
@@ -1060,17 +1039,17 @@ fn testErr(
1060 var diag = Diagnostic{}; 1039 var diag = Diagnostic{};
1061 var iter = args.SliceIterator{ .args = args_strings }; 1040 var iter = args.SliceIterator{ .args = args_strings };
1062 _ = parseEx(Help, params, parsers.default, &iter, .{ 1041 _ = parseEx(Help, params, parsers.default, &iter, .{
1063 .allocator = testing.allocator, 1042 .allocator = std.testing.allocator,
1064 .diagnostic = &diag, 1043 .diagnostic = &diag,
1065 }) catch |err| { 1044 }) catch |err| {
1066 var buf: [1024]u8 = undefined; 1045 var buf: [1024]u8 = undefined;
1067 var fbs = io.fixedBufferStream(&buf); 1046 var fbs = std.io.fixedBufferStream(&buf);
1068 diag.report(fbs.writer(), err) catch return error.TestFailed; 1047 diag.report(fbs.writer(), err) catch return error.TestFailed;
1069 try testing.expectEqualStrings(expected, fbs.getWritten()); 1048 try std.testing.expectEqualStrings(expected, fbs.getWritten());
1070 return; 1049 return;
1071 }; 1050 };
1072 1051
1073 try testing.expect(false); 1052 try std.testing.expect(false);
1074} 1053}
1075 1054
1076test "errors" { 1055test "errors" {
@@ -1175,7 +1154,7 @@ pub fn help(
1175 const max_spacing = blk: { 1154 const max_spacing = blk: {
1176 var res: usize = 0; 1155 var res: usize = 0;
1177 for (params) |param| { 1156 for (params) |param| {
1178 var cs = ccw.codepointCountingWriter(io.null_writer); 1157 var cs = ccw.codepointCountingWriter(std.io.null_writer);
1179 try printParam(cs.writer(), Id, param); 1158 try printParam(cs.writer(), Id, param);
1180 if (res < cs.codepoints_written) 1159 if (res < cs.codepoints_written)
1181 res = @intCast(cs.codepoints_written); 1160 res = @intCast(cs.codepoints_written);
@@ -1215,9 +1194,9 @@ pub fn help(
1215 1194
1216 var first_line = true; 1195 var first_line = true;
1217 var res: usize = std.math.maxInt(usize); 1196 var res: usize = std.math.maxInt(usize);
1218 var it = mem.tokenizeScalar(u8, description, '\n'); 1197 var it = std.mem.tokenizeScalar(u8, description, '\n');
1219 while (it.next()) |line| : (first_line = false) { 1198 while (it.next()) |line| : (first_line = false) {
1220 const trimmed = mem.trimLeft(u8, line, " "); 1199 const trimmed = std.mem.trimLeft(u8, line, " ");
1221 const indent = line.len - trimmed.len; 1200 const indent = line.len - trimmed.len;
1222 1201
1223 // If the first line has no indentation, then we ignore the indentation of the 1202 // If the first line has no indentation, then we ignore the indentation of the
@@ -1240,18 +1219,18 @@ pub fn help(
1240 }; 1219 };
1241 1220
1242 const description = param.id.description(); 1221 const description = param.id.description();
1243 var it = mem.splitScalar(u8, description, '\n'); 1222 var it = std.mem.splitScalar(u8, description, '\n');
1244 var first_line = true; 1223 var first_line = true;
1245 var non_emitted_newlines: usize = 0; 1224 var non_emitted_newlines: usize = 0;
1246 var last_line_indentation: usize = 0; 1225 var last_line_indentation: usize = 0;
1247 while (it.next()) |raw_line| : (first_line = false) { 1226 while (it.next()) |raw_line| : (first_line = false) {
1248 // First line might be special. See comment above. 1227 // First line might be special. See comment above.
1249 const indented_line = if (first_line and !mem.startsWith(u8, raw_line, " ")) 1228 const indented_line = if (first_line and !std.mem.startsWith(u8, raw_line, " "))
1250 raw_line 1229 raw_line
1251 else 1230 else
1252 raw_line[@min(min_description_indent, raw_line.len)..]; 1231 raw_line[@min(min_description_indent, raw_line.len)..];
1253 1232
1254 const line = mem.trimLeft(u8, indented_line, " "); 1233 const line = std.mem.trimLeft(u8, indented_line, " ");
1255 if (line.len == 0) { 1234 if (line.len == 0) {
1256 non_emitted_newlines += 1; 1235 non_emitted_newlines += 1;
1257 continue; 1236 continue;
@@ -1266,7 +1245,7 @@ pub fn help(
1266 const does_not_have_same_indent_as_last_line = 1245 const does_not_have_same_indent_as_last_line =
1267 line_indentation != last_line_indentation; 1246 line_indentation != last_line_indentation;
1268 1247
1269 const starts_with_control_char = mem.indexOfScalar(u8, "=*", line[0]) != null; 1248 const starts_with_control_char = std.mem.indexOfScalar(u8, "=*", line[0]) != null;
1270 1249
1271 // Either the input contains 2 or more newlines, in which case we should start 1250 // Either the input contains 2 or more newlines, in which case we should start
1272 // a new paragraph. 1251 // a new paragraph.
@@ -1286,7 +1265,7 @@ pub fn help(
1286 try description_writer.newline(); 1265 try description_writer.newline();
1287 } 1266 }
1288 1267
1289 var words = mem.tokenizeScalar(u8, line, ' '); 1268 var words = std.mem.tokenizeScalar(u8, line, ' ');
1290 while (words.next()) |word| 1269 while (words.next()) |word|
1291 try description_writer.writeWord(word); 1270 try description_writer.writeWord(word);
1292 1271
@@ -1310,7 +1289,7 @@ fn DescriptionWriter(comptime UnderlyingWriter: type) type {
1310 printed_chars: usize, 1289 printed_chars: usize,
1311 1290
1312 pub fn writeWord(writer: *@This(), word: []const u8) !void { 1291 pub fn writeWord(writer: *@This(), word: []const u8) !void {
1313 debug.assert(word.len != 0); 1292 std.debug.assert(word.len != 0);
1314 1293
1315 var first_word = writer.printed_chars <= writer.indentation; 1294 var first_word = writer.printed_chars <= writer.indentation;
1316 const chars_to_write = try std.unicode.utf8CountCodepoints(word) + @intFromBool(!first_word); 1295 const chars_to_write = try std.unicode.utf8CountCodepoints(word) + @intFromBool(!first_word);
@@ -1380,13 +1359,13 @@ fn printParam(
1380} 1359}
1381 1360
1382fn testHelp(opt: HelpOptions, str: []const u8) !void { 1361fn testHelp(opt: HelpOptions, str: []const u8) !void {
1383 const params = try parseParams(testing.allocator, str); 1362 const params = try parseParams(std.testing.allocator, str);
1384 defer testing.allocator.free(params); 1363 defer std.testing.allocator.free(params);
1385 1364
1386 var buf: [2048]u8 = undefined; 1365 var buf: [2048]u8 = undefined;
1387 var fbs = io.fixedBufferStream(&buf); 1366 var fbs = std.io.fixedBufferStream(&buf);
1388 try help(fbs.writer(), Help, params, opt); 1367 try help(fbs.writer(), Help, params, opt);
1389 try testing.expectEqualStrings(str, fbs.getWritten()); 1368 try std.testing.expectEqualStrings(str, fbs.getWritten());
1390} 1369}
1391 1370
1392test "clap.help" { 1371test "clap.help" {
@@ -1884,9 +1863,9 @@ pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !voi
1884 1863
1885fn testUsage(expected: []const u8, params: []const Param(Help)) !void { 1864fn testUsage(expected: []const u8, params: []const Param(Help)) !void {
1886 var buf: [1024]u8 = undefined; 1865 var buf: [1024]u8 = undefined;
1887 var fbs = io.fixedBufferStream(&buf); 1866 var fbs = std.io.fixedBufferStream(&buf);
1888 try usage(fbs.writer(), Help, params); 1867 try usage(fbs.writer(), Help, params);
1889 try testing.expectEqualStrings(expected, fbs.getWritten()); 1868 try std.testing.expectEqualStrings(expected, fbs.getWritten());
1890} 1869}
1891 1870
1892test "usage" { 1871test "usage" {
@@ -1948,3 +1927,17 @@ test "usage" {
1948 \\ 1927 \\
1949 )); 1928 ));
1950} 1929}
1930
1931test {
1932 _ = args;
1933 _ = parsers;
1934 _ = streaming;
1935 _ = ccw;
1936}
1937
1938pub const args = @import("clap/args.zig");
1939pub const parsers = @import("clap/parsers.zig");
1940pub const streaming = @import("clap/streaming.zig");
1941pub const ccw = @import("clap/codepoint_counting_writer.zig");
1942
1943const std = @import("std");