shell: implement exit builtin

This commit is contained in:
Meghan Denny
2024-03-20 23:21:41 -07:00
parent 49ee19cb41
commit 1fc547fdd9
2 changed files with 105 additions and 52 deletions

View File

@@ -3788,7 +3788,9 @@ pub const Interpreter = struct {
args_slice: ?[]const [:0]const u8 = null,
cwd: bun.FileDescriptor,
impl: union(Kind) {
impl: RealImpl,
const RealImpl = union(Kind) {
cat: Cat,
touch: Touch,
mkdir: Mkdir,
@@ -3800,7 +3802,8 @@ pub const Interpreter = struct {
rm: Rm,
mv: Mv,
ls: Ls,
},
exit: Exit,
};
const Result = @import("../result.zig").Result;
@@ -3816,6 +3819,7 @@ pub const Interpreter = struct {
rm,
mv,
ls,
exit,
pub fn parentType(this: Kind) type {
_ = this;
@@ -3834,6 +3838,7 @@ pub const Interpreter = struct {
.rm => "usage: rm [-f | -i] [-dIPRrvWx] file ...\n unlink [--] file\n",
.mv => "usage: mv [-f | -i | -n] [-hv] source target\n mv [-f | -i | -n] [-v] source ... directory\n",
.ls => "usage: ls [-@ABCFGHILOPRSTUWabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]\n",
.exit => "usage: exit [n]\n",
};
}
@@ -3850,6 +3855,7 @@ pub const Interpreter = struct {
.rm => "rm",
.mv => "mv",
.ls => "ls",
.exit => "exit",
};
}
@@ -4011,6 +4017,7 @@ pub const Interpreter = struct {
.pwd => this.callImplWithType(Pwd, Ret, "pwd", field, args_),
.mv => this.callImplWithType(Mv, Ret, "mv", field, args_),
.ls => this.callImplWithType(Ls, Ret, "ls", field, args_),
.exit => this.callImplWithType(Exit, Ret, "exit", field, args_),
};
}
@@ -4080,26 +4087,6 @@ pub const Interpreter = struct {
};
switch (kind) {
.cat => {
cmd.exec.bltn.impl = .{
.cat = Cat{ .bltn = &cmd.exec.bltn },
};
},
.touch => {
cmd.exec.bltn.impl = .{
.touch = Touch{ .bltn = &cmd.exec.bltn },
};
},
.mkdir => {
cmd.exec.bltn.impl = .{
.mkdir = Mkdir{ .bltn = &cmd.exec.bltn },
};
},
.@"export" => {
cmd.exec.bltn.impl = .{
.@"export" = Export{ .bltn = &cmd.exec.bltn },
};
},
.rm => {
cmd.exec.bltn.impl = .{
.rm = Rm{
@@ -4116,36 +4103,10 @@ pub const Interpreter = struct {
},
};
},
.cd => {
cmd.exec.bltn.impl = .{
.cd = Cd{
.bltn = &cmd.exec.bltn,
},
};
},
.which => {
cmd.exec.bltn.impl = .{
.which = Which{
.bltn = &cmd.exec.bltn,
},
};
},
.pwd => {
cmd.exec.bltn.impl = .{
.pwd = Pwd{ .bltn = &cmd.exec.bltn },
};
},
.mv => {
cmd.exec.bltn.impl = .{
.mv = Mv{ .bltn = &cmd.exec.bltn },
};
},
.ls => {
cmd.exec.bltn.impl = .{
.ls = Ls{
.bltn = &cmd.exec.bltn,
},
};
inline else => |tag| {
cmd.exec.bltn.impl = @unionInit(RealImpl, @tagName(tag), .{
.bltn = &cmd.exec.bltn,
});
},
}
@@ -8525,6 +8486,80 @@ pub const Interpreter = struct {
}
};
};
pub const Exit = struct {
bltn: *Builtin,
state: enum {
idle,
waiting_io,
err,
done,
} = .idle,
pub fn start(this: *Exit) Maybe(void) {
const args = this.bltn.argsSlice();
switch (args.len) {
0 => {
this.bltn.done(0);
return Maybe(void).success;
},
1 => {
const first_arg = args[0][0..std.mem.len(args[0]) :0];
const exit_code = std.fmt.parseInt(ExitCode, first_arg, 10) catch {
@panic("TODO");
};
this.bltn.done(exit_code);
return Maybe(void).success;
},
else => {
const msg = "exit: too many arguments";
if (this.bltn.stderr.needsIO()) {
this.state = .waiting_io;
this.bltn.stderr.enqueue(this, msg);
return Maybe(void).success;
}
_ = this.bltn.writeNoIO(.stderr, msg);
this.bltn.done(1);
return Maybe(void).success;
},
}
}
pub fn next(this: *Exit) void {
while (!(this.state == .err or this.state == .done)) {
switch (this.state) {
.waiting_io => return,
.idle, .done, .err => unreachable,
}
}
if (this.state == .done) {
this.bltn.done(1);
return;
}
if (this.state == .err) {
this.bltn.done(1);
return;
}
}
pub fn onIOWriterChunk(this: *Exit, _: usize, maybe_e: ?JSC.SystemError) void {
if (comptime bun.Environment.allow_assert) {
std.debug.assert(this.state == .waiting_io);
}
if (maybe_e) |e| {
defer e.deref();
this.state = .err;
this.next();
return;
}
this.state = .done;
this.next();
}
pub fn deinit(this: *Exit) void {
_ = this;
}
};
};
/// This type is reference counted, but deinitialization is queued onto the event loop
@@ -9519,6 +9554,7 @@ pub const IOWriterChildPtr = struct {
Interpreter.Builtin.Touch,
Interpreter.Builtin.Touch.ShellTouchOutputTask,
Interpreter.Builtin.Cat,
Interpreter.Builtin.Exit,
shell.subproc.PipeReader.CapturedWriter,
});

View File

@@ -0,0 +1,17 @@
import { $ } from "bun";
import { describe, test, expect } from "bun:test";
import { TestBuilder } from "../test_builder";
import { sortedShellOutput } from "../util";
import { join } from "path";
describe("exit", async () => {
TestBuilder.command`exit`.exitCode(0).runAsTest("works");
describe("argument sets exit code", async () => {
for (const arg of [0, 2, 11]) {
TestBuilder.command`exit ${arg}`.exitCode(arg).runAsTest(`${arg}`);
}
});
TestBuilder.command`exit 3 5`.exitCode(1).stderr("exit: too many arguments").runAsTest("too many arguments");
});