Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ Safe release: for instances where no memory allocation occurred during parsing,

Provides `String`, `LiteralString` and `checker`.

Provides the wrappers of some struct in `std`:
- `Open(...)`: `std.fs.File/Dir`
- See [ex-06](examples/ex-06.copy.zig) for usage
- `...`

## Features

### Options, Arguments, Subcommands
Expand Down Expand Up @@ -205,7 +210,6 @@ For arguments, T must be the smallest parsable unit: `[]const u8` -> T
- Only supports base types of `.int`, `.float`, and `.bool`
- `@Vector{1,1}`: `[\(\[\{][ ]*1[ ]*[;:,][ ]*1[ ]*[\)\]\}]`
- `@Vector{true,false}`: `[\(\[\{][ ]*y[ ]*[;:,][ ]*no[ ]*[\)\]\}]`
- `std.fs.File/Dir`

If type T has no associated default parser or `parse` method, you can specify a custom parser (`.parseFn`) for the parameter. Obviously, single-option parameters cannot have parsers as it would be meaningless.

Expand All @@ -218,7 +222,7 @@ Options and arguments can be configured with default values (`.default`). Once c

> Single options, options with a single argument of optional type, or single positional arguments of optional type are always optional.

Default values must be determined at comptime. For `argOpt`, if the value cannot be determined at comptime (e.g., `std.fs.cwd()` at `Windows`), you can configure the default input (`.rawDefault`), which will determine the default value in the perser.
Default values must be determined at comptime. For `argOpt` and `posArg`, if the value cannot be determined at comptime (e.g., `std.fs.cwd()` at `Windows`), you can configure the default input (`.rawDefault`), which will determine the default value in the perser.

#### Value Ranges

Expand Down
8 changes: 6 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ pub fn getString(self: Self) *const [stringify(self, "fname").count():0]u8 {

提供 `String`, `LiteralString` 和 `checker`。

为来自`std`的一些结构体提供封装:
- `Open(...)`:`std.fs.File/Dir`
- 用法详见[ex-06](examples/ex-06.copy.zig)
- `...`

## 特性

### 选项、参数、子命令
Expand Down Expand Up @@ -204,7 +209,6 @@ pub fn getString(self: Self) *const [stringify(self, "fname").count():0]u8 {
- 仅支持基类型为 `.int`, `.float` 和 `.bool` 的
- `@Vector{1,1}`:`[\(\[\{][ ]*1[ ]*[;:,][ ]*1[ ]*[\)\]\}]`
- `@Vector{true,false}`:`[\(\[\{][ ]*y[ ]*[;:,][ ]*no[ ]*[\)\]\}]`
- `std.fs.File/Dir`

如果 T 不存在相关联的默认解析器或`parse`方法,可以为参数自定义解析器(`.parseFn`)。显然,无法为单选项配置解析器,因为这是无意义的。

Expand All @@ -217,7 +221,7 @@ pub fn getString(self: Self) *const [stringify(self, "fname").count():0]u8 {

> 单选项、具有可选类型的带单参数选项或具有可选类型的单位置参数,总是可选的。

默认值需要在编译期确定。对于带参数选项(`argOpt`),如果无法在编译期确定值(比如在`Windows`上的`std.fs.cwd()`),那么可以配置默认输入(`.rawDefault`),这将在解析器中完成默认值的确定。
默认值需要在编译期确定。对于带参数选项(`argOpt`)和位置参数(`posArg`),如果无法在编译期确定值(比如在`Windows`上的`std.fs.cwd()`),那么可以配置默认输入(`.rawDefault`),这将在解析器中完成默认值的确定。

#### 取值范围

Expand Down
50 changes: 50 additions & 0 deletions examples/ex-06.copy.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const std = @import("std");
const zargs = @import("zargs");
const Command = zargs.Command;
const Arg = zargs.Arg;
const TokenIter = zargs.TokenIter;
const ztype = @import("ztype");
const String = ztype.String;
const Open = ztype.Open;

pub fn main() !void {
const cmd = Command.new("copy").alias("cp")
.about("Copy a file to another file")
.author("KiozWang")
.homepage("https://github.com/kioz-wang/zargs")
.arg(Arg.opt("verbose", bool).short('v').long("verbose").help("Show detail"))
.arg(Arg.optArg("max", usize).long("max").help("Max byte to copy").default(32 << 10))
.arg(Arg.posArg("source", Open(.file, .{})))
.arg(Arg.posArg("target", Open(.fileCreate, .{})))
.config(.{ .style = .classic });

var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const allocator = gpa.allocator();

var args = cmd.parse(allocator) catch |e|
zargs.exitf(e, 1, "\n{s}\n", .{cmd.usageString()});
defer cmd.destroy(&args, allocator);

if (args.verbose) {
std.debug.print("Try copy {s} to {s} with max 0x{x} bytes\n", .{ args.source.s, args.target.s, args.max });
}
const content = try args.source.v.reader().any().readAllAlloc(allocator, args.max);
defer allocator.free(content);
try args.target.v.writer().any().writeAll(content);
if (args.verbose) {
std.debug.print("Done with 0x{x} bytes\n", .{content.len});
}
}

// [kioz@matexpro zargs]$ echo "hello world" | ./zig-out/bin/06.copy -v -- - /tmp/t
// Try copy - to /tmp/t with max 0x8000 bytes
// Done with 0xc bytes
// [kioz@matexpro zargs]$ ./zig-out/bin/06.copy -v /tmp/t -
// Try copy /tmp/t to - with max 0x8000 bytes
// hello world
// Done with 0xc bytes
// [kioz@matexpro zargs]$ echo "bye" | ./zig-out/bin/06.copy -v -- - - | hexdump -C
// Try copy - to - with max 0x8000 bytes
// Done with 0x4 bytes
// 00000000 62 79 65 0a |bye.|
// 00000004
44 changes: 31 additions & 13 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -192,23 +192,41 @@ pub fn Open(openType: OpenType, flags: OpenFlags(openType)) type {
const Self = @This();
v: OpenValue(openType),
s: String,
@".is_stdio": bool = false,
pub fn parse(s: String, a_maybe: ?std.mem.Allocator) ?Self {
return Self{
.s = if (a_maybe) |a| blk: {
const s_alloc = a.alloc(u8, s.len) catch return null;
@memcpy(s_alloc, s);
break :blk s_alloc;
} else s,
.v = switch (openType) {
.file => std.fs.cwd().openFile(s, flags),
.dir => std.fs.cwd().openDir(s, flags),
.fileCreate => std.fs.cwd().createFile(s, flags),
} catch return null,
};
var self: Self = undefined;

switch (openType) {
.file => {
if ((comptime flags.mode != .read_write) and std.mem.eql(u8, s, "-")) {
self.@".is_stdio" = true;
self.v = switch (flags.mode) {
.read_only => std.io.getStdIn(),
.write_only => std.io.getStdOut(),
else => unreachable,
};
} else self.v = std.fs.cwd().openFile(s, flags) catch return null;
},
.dir => self.v = std.fs.cwd().openDir(s, flags) catch return null,
.fileCreate => {
if ((comptime !flags.read) and std.mem.eql(u8, s, "-")) {
self.@".is_stdio" = true;
self.v = std.io.getStdOut();
} else self.v = std.fs.cwd().createFile(s, flags) catch return null;
},
}

self.s = if (a_maybe) |a| blk: {
const s_alloc = a.alloc(u8, s.len) catch return null;
@memcpy(s_alloc, s);
break :blk s_alloc;
} else s;

return self;
}
pub fn destroy(self: *Self, a_maybe: ?std.mem.Allocator) void {
if (!self.@".is_stdio") self.v.close();
if (a_maybe) |a| a.free(self.s);
self.v.close();
}
pub fn format(self: Self, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
try writer.print("{s}({s},{?})", .{ @typeName(@TypeOf(self.v)), self.s, flags });
Expand Down
Loading