Improve file/directory utils (#84)

This commit is contained in:
Josh Goebel 2021-05-01 18:58:17 -04:00 committed by GitHub
parent 4acf7b395d
commit 8227330eb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 125 additions and 56 deletions

View File

@ -10,6 +10,8 @@
#include "timer.wren.inc"
extern void directoryList(WrenVM* vm);
extern void directoryCreate(WrenVM* vm);
extern void directoryDelete(WrenVM* vm);
extern void fileAllocate(WrenVM* vm);
extern void fileFinalize(void* data);
extern void fileDelete(WrenVM* vm);
@ -121,6 +123,8 @@ static ModuleRegistry modules[] =
{
MODULE(io)
CLASS(Directory)
STATIC_METHOD("create_(_,_)", directoryCreate)
STATIC_METHOD("delete_(_,_)", directoryDelete)
STATIC_METHOD("list_(_,_)", directoryList)
END_CLASS
CLASS(File)

View File

@ -139,10 +139,31 @@ static void directoryListCallback(uv_fs_t* request)
void directoryList(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
uv_fs_t* request = createRequest(fiber);
// TODO: Check return.
uv_fs_scandir(getLoop(), request, path, 0, directoryListCallback);
int error = uv_fs_scandir(getLoop(), request, path, 0, directoryListCallback);
if (error != 0 ) schedulerResumeError(fiber, uv_strerror(error));
}
void fileDirectoryCallback(uv_fs_t* request) {
if (handleRequestError(request)) return;
schedulerResume(freeRequest(request), false);
}
void directoryCreate(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
uv_fs_mkdir(getLoop(), request, path, 0, fileDirectoryCallback);
}
void directoryDelete(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
uv_fs_rmdir(getLoop(), request, path, fileDirectoryCallback);
}
void fileAllocate(WrenVM* vm)
@ -174,10 +195,11 @@ static void fileDeleteCallback(uv_fs_t* request)
void fileDelete(WrenVM* vm)
{
const char* path = wrenGetSlotString(vm, 1);
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
uv_fs_t* request = createRequest(fiber);
// TODO: Check return.
uv_fs_unlink(getLoop(), request, path, fileDeleteCallback);
int error = uv_fs_unlink(getLoop(), request, path, fileDeleteCallback);
if (error != 0 ) schedulerResumeError(fiber, uv_strerror(error));
}
static void fileOpenCallback(uv_fs_t* request)

54
src/module/io.wren vendored
View File

@ -2,12 +2,22 @@ import "scheduler" for Scheduler
class Directory {
// TODO: Copied from File. Figure out good way to share this.
static ensurePath_(path) {
static ensureString_(path) {
if (!(path is String)) Fiber.abort("Path must be a string.")
}
static create(path) {
ensureString_(path)
return Scheduler.await_ { create_(path, Fiber.current) }
}
static delete(path) {
ensureString_(path)
return Scheduler.await_ { delete_(path, Fiber.current) }
}
static exists(path) {
ensurePath_(path)
ensureString_(path)
var stat
Fiber.new {
stat = Stat.path(path)
@ -19,11 +29,12 @@ class Directory {
}
static list(path) {
ensurePath_(path)
list_(path, Fiber.current)
return Scheduler.runNextScheduled_()
ensureString_(path)
return Scheduler.await_ { list_(path, Fiber.current) }
}
foreign static create_(path, fiber)
foreign static delete_(path, fiber)
foreign static list_(path, fiber)
}
@ -43,13 +54,12 @@ foreign class File {
}
static delete(path) {
ensurePath_(path)
delete_(path, Fiber.current)
return Scheduler.runNextScheduled_()
ensureString_(path)
Scheduler.await_ { delete_(path, Fiber.current) }
}
static exists(path) {
ensurePath_(path)
ensureString_(path)
var stat
Fiber.new {
stat = Stat.path(path)
@ -67,7 +77,7 @@ foreign class File {
// TODO: Add named parameters and then call this "open(_,flags:_)"?
// TODO: Test.
static openWithFlags(path, flags) {
ensurePath_(path)
ensureString_(path)
ensureInt_(flags, "Flags")
open_(path, flags, Fiber.current)
var fd = Scheduler.runNextScheduled_()
@ -94,15 +104,13 @@ foreign class File {
// TODO: This works for directories too, so putting it on File is kind of
// lame. Consider reorganizing these classes some.
static realPath(path) {
ensurePath_(path)
realPath_(path, Fiber.current)
return Scheduler.runNextScheduled_()
ensureString_(path)
return Scheduler.await_ { realPath_(path, Fiber.current) }
}
static size(path) {
ensurePath_(path)
sizePath_(path, Fiber.current)
return Scheduler.runNextScheduled_()
ensureString_(path)
return Scheduler.await_ { sizePath_(path, Fiber.current) }
}
construct new_(fd) {}
@ -118,14 +126,12 @@ foreign class File {
size {
ensureOpen_()
size_(Fiber.current)
return Scheduler.runNextScheduled_()
return Scheduler.await_ { size_(Fiber.current) }
}
stat {
ensureOpen_()
stat_(Fiber.current)
return Scheduler.runNextScheduled_()
return Scheduler.await_ { stat_(Fiber.current) }
}
readBytes(count) { readBytes(count, 0) }
@ -135,8 +141,7 @@ foreign class File {
File.ensureInt_(count, "Count")
File.ensureInt_(offset, "Offset")
readBytes_(count, offset, Fiber.current)
return Scheduler.runNextScheduled_()
return Scheduler.await_ { readBytes_(count, offset, Fiber.current) }
}
writeBytes(bytes) { writeBytes(bytes, size) }
@ -146,15 +151,14 @@ foreign class File {
if (!(bytes is String)) Fiber.abort("Bytes must be a string.")
File.ensureInt_(offset, "Offset")
writeBytes_(bytes, offset, Fiber.current)
return Scheduler.runNextScheduled_()
return Scheduler.await_ { writeBytes_(bytes, offset, Fiber.current) }
}
ensureOpen_() {
if (!isOpen) Fiber.abort("File is not open.")
}
static ensurePath_(path) {
static ensureString_(path) {
if (!(path is String)) Fiber.abort("Path must be a string.")
}

View File

@ -6,12 +6,22 @@ static const char* ioModuleSource =
"\n"
"class Directory {\n"
" // TODO: Copied from File. Figure out good way to share this.\n"
" static ensurePath_(path) {\n"
" static ensureString_(path) {\n"
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
" }\n"
"\n"
" static create(path) {\n"
" ensureString_(path)\n"
" return Scheduler.await_ { create_(path, Fiber.current) }\n"
" }\n"
"\n"
" static delete(path) {\n"
" ensureString_(path)\n"
" return Scheduler.await_ { delete_(path, Fiber.current) }\n"
" }\n"
"\n"
" static exists(path) {\n"
" ensurePath_(path)\n"
" ensureString_(path)\n"
" var stat\n"
" Fiber.new {\n"
" stat = Stat.path(path)\n"
@ -23,11 +33,12 @@ static const char* ioModuleSource =
" }\n"
"\n"
" static list(path) {\n"
" ensurePath_(path)\n"
" list_(path, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" ensureString_(path)\n"
" return Scheduler.await_ { list_(path, Fiber.current) }\n"
" }\n"
"\n"
" foreign static create_(path, fiber)\n"
" foreign static delete_(path, fiber)\n"
" foreign static list_(path, fiber)\n"
"}\n"
"\n"
@ -47,13 +58,12 @@ static const char* ioModuleSource =
" }\n"
"\n"
" static delete(path) {\n"
" ensurePath_(path)\n"
" delete_(path, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" ensureString_(path)\n"
" Scheduler.await_ { delete_(path, Fiber.current) }\n"
" }\n"
"\n"
" static exists(path) {\n"
" ensurePath_(path)\n"
" ensureString_(path)\n"
" var stat\n"
" Fiber.new {\n"
" stat = Stat.path(path)\n"
@ -71,7 +81,7 @@ static const char* ioModuleSource =
" // TODO: Add named parameters and then call this \"open(_,flags:_)\"?\n"
" // TODO: Test.\n"
" static openWithFlags(path, flags) {\n"
" ensurePath_(path)\n"
" ensureString_(path)\n"
" ensureInt_(flags, \"Flags\")\n"
" open_(path, flags, Fiber.current)\n"
" var fd = Scheduler.runNextScheduled_()\n"
@ -98,15 +108,13 @@ static const char* ioModuleSource =
" // TODO: This works for directories too, so putting it on File is kind of\n"
" // lame. Consider reorganizing these classes some.\n"
" static realPath(path) {\n"
" ensurePath_(path)\n"
" realPath_(path, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" ensureString_(path)\n"
" return Scheduler.await_ { realPath_(path, Fiber.current) }\n"
" }\n"
"\n"
" static size(path) {\n"
" ensurePath_(path)\n"
" sizePath_(path, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" ensureString_(path)\n"
" return Scheduler.await_ { sizePath_(path, Fiber.current) }\n"
" }\n"
"\n"
" construct new_(fd) {}\n"
@ -122,14 +130,12 @@ static const char* ioModuleSource =
"\n"
" size {\n"
" ensureOpen_()\n"
" size_(Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" return Scheduler.await_ { size_(Fiber.current) }\n"
" }\n"
"\n"
" stat {\n"
" ensureOpen_()\n"
" stat_(Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" return Scheduler.await_ { stat_(Fiber.current) }\n"
" }\n"
"\n"
" readBytes(count) { readBytes(count, 0) }\n"
@ -139,8 +145,7 @@ static const char* ioModuleSource =
" File.ensureInt_(count, \"Count\")\n"
" File.ensureInt_(offset, \"Offset\")\n"
"\n"
" readBytes_(count, offset, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" return Scheduler.await_ { readBytes_(count, offset, Fiber.current) }\n"
" }\n"
"\n"
" writeBytes(bytes) { writeBytes(bytes, size) }\n"
@ -150,15 +155,14 @@ static const char* ioModuleSource =
" if (!(bytes is String)) Fiber.abort(\"Bytes must be a string.\")\n"
" File.ensureInt_(offset, \"Offset\")\n"
"\n"
" writeBytes_(bytes, offset, Fiber.current)\n"
" return Scheduler.runNextScheduled_()\n"
" return Scheduler.await_ { writeBytes_(bytes, offset, Fiber.current) }\n"
" }\n"
"\n"
" ensureOpen_() {\n"
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
" }\n"
"\n"
" static ensurePath_(path) {\n"
" static ensureString_(path) {\n"
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
" }\n"
"\n"

View File

@ -13,6 +13,12 @@ class Scheduler {
static resume_(fiber, arg) { fiber.transfer(arg) }
static resumeError_(fiber, error) { fiber.transferError(error) }
// wait for a method to finish that has a callback on the C side
static await_(fn) {
fn.call()
return Scheduler.runNextScheduled_()
}
static runNextScheduled_() {
if (__scheduled == null || __scheduled.isEmpty) {
return Fiber.suspend()

View File

@ -17,6 +17,12 @@ static const char* schedulerModuleSource =
" static resume_(fiber, arg) { fiber.transfer(arg) }\n"
" static resumeError_(fiber, error) { fiber.transferError(error) }\n"
"\n"
" // wait for a method to finish that has a callback on the C side\n"
" static await_(fn) {\n"
" fn.call()\n"
" return Scheduler.runNextScheduled_()\n"
" }\n"
"\n"
" static runNextScheduled_() {\n"
" if (__scheduled == null || __scheduled.isEmpty) {\n"
" return Fiber.suspend()\n"

23
test/io/directory/create_delete.wren vendored Normal file
View File

@ -0,0 +1,23 @@
import "io" for Directory, Stat
import "os" for Platform
if (Directory.exists("tmp")) {
Directory.delete("tmp")
}
System.print(Directory.exists("tmp")) // expect: false
Directory.create("tmp")
System.print(Directory.exists("tmp")) // expect: true
// Windows does not support mode
if (Platform.isPosix) {
var stat = Stat.path("tmp")
// 511 is 0755
System.print(stat.mode && 0x1ff) // expect: 511
}
Directory.delete("tmp")
System.print(Directory.exists("tmp")) // expect: false