diff --git a/src/playdate/file.nim b/src/playdate/file.nim index 5c075a8..4d0cf74 100644 --- a/src/playdate/file.nim +++ b/src/playdate/file.nim @@ -15,7 +15,19 @@ type SDFileObj {.requiresinit.} = object resource: SDFilePtr path: string - SDFile* = ref SDFileObj + SDFile* = ref SDFileObj + +proc requireValidStatus(res: SomeInteger): int {.raises: [IOError], discardable.} = + privateAccess(PlaydateFile) + if res < 0: + raise newException(IOError, $playdate.file.geterr()) + return res.int + +proc requireNotNil[T: pointer](res: T): T {.raises: [IOError].} = + privateAccess(PlaydateFile) + if res == nil: + raise newException(IOError, $playdate.file.geterr()) + return res proc `=destroy`(this: var SDFileObj) = privateAccess(PlaydateFile) @@ -29,105 +41,78 @@ proc fileCallback(filename: ConstChar, userdata: pointer) {.cdecl.} = proc listFiles*(this: ptr PlaydateFile, path: string, showHidden: bool = false): seq[string] {.raises: [IOError]} = privateAccess(PlaydateFile) var files = newSeq[string]() - var res = this.listfiles(toC(path.cstring), fileCallback, addr(files), if showHidden: 1 else: 0) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + this.listfiles(toC(path.cstring), fileCallback, addr(files), if showHidden: 1 else: 0).requireValidStatus return files proc stat*(this: ptr PlaydateFile, path: string): FileStat {.raises: [IOError]} = privateAccess(PlaydateFile) var info: FileStat = FileStat() - let res = this.stat(path.cstring, addr(info[])) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + this.stat(path.cstring, addr(info[])).requireValidStatus return info proc exists*(this: ptr PlaydateFile, path: string): bool = privateAccess(PlaydateFile) - var info: FileStat = FileStat() - let res = this.stat(path.cstring, addr(info[])) - return res != 0 + var info: FileStatRaw + return this.stat(path.cstring, addr(info)) == 0 proc unlink*(this: ptr PlaydateFile, path: string, recursive: bool) {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = this.unlink(path.cstring, if recursive: 1 else: 0) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + this.unlink(path.cstring, if recursive: 1 else: 0).requireValidStatus proc mkdir*(this: ptr PlaydateFile, path: string) {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = this.mkdir(path.cstring) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + this.mkdir(path.cstring).requireValidStatus proc rename*(this: ptr PlaydateFile, fromName: string, to: string) {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = this.rename(fromName.cstring, to.cstring) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + this.rename(fromName.cstring, to.cstring).requireValidStatus proc close*(this: SDFile) {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = playdate.file.close(this.resource) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + discard playdate.file.close(this.resource).requireValidStatus -proc flush*(this: SDFile): int {.raises: [IOError]} = +proc flush*(this: SDFile): int {.raises: [IOError], discardable} = privateAccess(PlaydateFile) - let res = playdate.file.flush(this.resource) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) - return res + return playdate.file.flush(this.resource).requireValidStatus proc open*(this: ptr PlaydateFile, path: string, mode: FileOptions): SDFile {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = this.open(path.cstring, mode) - if res == nil: - raise newException(IOError, $playdate.file.geterr()) - return SDFile(resource: res, path: path) + return SDFile(resource: this.open(path.cstring, mode).requireNotNil, path: path) proc read*(this: SDFile, length: uint): tuple[bytes: seq[byte], length: int] {.raises: [IOError]} = privateAccess(PlaydateFile) var buffer = newSeq[byte](length) - let res = playdate.file.read(this.resource, addr(buffer[0]), length.cuint) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) + let res = playdate.file.read(this.resource, addr(buffer[0]), length.cuint).requireValidStatus return (bytes: buffer, length: res.int) proc read*(this: SDFile): seq[byte] {.raises: [IOError]} = let size = playdate.file.stat(this.path).size privateAccess(PlaydateFile) var buffer = newSeq[byte](size) - let res = playdate.file.read(this.resource, addr(buffer[0]), size.cuint) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) + playdate.file.read(this.resource, addr(buffer[0]), size.cuint).requireValidStatus return buffer proc readString*(this: SDFile): string {.raises: [IOError].} = let size = playdate.file.stat(this.path).size privateAccess(PlaydateFile) var str = newString(size) - let res = playdate.file.read(this.resource, addr(str[0]), size.cuint) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) + playdate.file.read(this.resource, addr(str[0]), size.cuint).requireValidStatus return str proc seek*(this: SDFile, pos: int, whence: int) {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = playdate.file.seek(this.resource, pos.cint, whence.cint) - if res != 0: - raise newException(IOError, $playdate.file.geterr()) + playdate.file.seek(this.resource, pos.cint, whence.cint).requireValidStatus proc tell*(this: SDFile): int {.raises: [IOError]} = privateAccess(PlaydateFile) - let res = playdate.file.tell(this.resource) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) - return res + return playdate.file.tell(this.resource).requireValidStatus -proc write*(this: SDFile, buffer: seq[byte], length: uint): int {.raises: [IOError]} = +proc write*(this: SDFile, buffer: seq[byte], length: uint): int {.raises: [IOError], discardable} = privateAccess(PlaydateFile) - let res = playdate.file.write(this.resource, unsafeAddr(buffer[0]), length.cuint) - if res < 0: - raise newException(IOError, $playdate.file.geterr()) - return res \ No newline at end of file + if length > 0: + return playdate.file.write(this.resource, unsafeAddr(buffer[0]), length.cuint).requireValidStatus + +proc write*(this: SDFile, content: string): int {.raises: [IOError], discardable} = + privateAccess(PlaydateFile) + if content.len > 0: + return playdate.file.write(this.resource, unsafeAddr(content[0]), content.len.cuint).requireValidStatus diff --git a/tests/src/playdate_tests.nim b/tests/src/playdate_tests.nim index 107cc07..1a7ecfa 100644 --- a/tests/src/playdate_tests.nim +++ b/tests/src/playdate_tests.nim @@ -5,13 +5,14 @@ ## import playdate/api -import ../[t_buttons, t_graphics, t_nineslice] +import ../[t_buttons, t_graphics, t_nineslice, t_files] proc runTests() {.raises: [].} = try: execButtonsTests() execGraphicsTests(true) execNineSliceTests(true) + execFilesTest() except Exception as e: quit(e.msg & "\n" & e.getStackTrace) diff --git a/tests/t_files.nim b/tests/t_files.nim new file mode 100644 index 0000000..3d4a410 --- /dev/null +++ b/tests/t_files.nim @@ -0,0 +1,51 @@ +import unittest, playdate/api + +proc createFile(name: string, body: string = "") = + var handle = playdate.file.open(name, kFileWrite) + check(handle.write(cast[seq[byte]](body), body.len.uint) >= 0) + +proc execFilesTest*() = + suite "File loading": + test "Writing and reading files": + createFile("test_data.txt", "foo") + var handle = playdate.file.open("test_data.txt", kFileReadData) + check(handle.readString() == "foo") + + test "Writing strings to files": + block: + playdate.file.open("test_data.txt", kFileWrite).write("file content") + check(playdate.file.open("test_data.txt", kFileReadData).readString() == "file content") + + test "Listing files": + createFile("list_files.txt") + check("list_files.txt" in playdate.file.listFiles("/")) + + test "Stating missing file": + expect IOError: + discard playdate.file.stat("not_real.txt") + + test "Stating existing file": + createFile("stat_file.txt", "some content") + let stat = playdate.file.stat("stat_file.txt") + check(stat.isdir == 0) + check(stat.size == 12) + + test "Checking if files exists": + check(playdate.file.exists("not_a_file.txt") == false) + + createFile("real_file.txt") + check(playdate.file.exists("real_file.txt")) + + test "Unlinking files": + createFile("delete_me.txt") + playdate.file.unlink("delete_me.txt", false) + check(playdate.file.exists("delete_me.txt") == false) + + test "mkdir": + playdate.file.mkdir("my_dir") + check(playdate.file.stat("my_dir").isdir == 1) + + test "Renaming file": + createFile("original_file.txt") + playdate.file.rename("original_file.txt", "renamed_file.txt") + check(playdate.file.exists("renamed_file.txt")) \ No newline at end of file