diff --git a/README.mbt.md b/README.mbt.md index dd25286..dd7b9c3 100644 --- a/README.mbt.md +++ b/README.mbt.md @@ -74,7 +74,7 @@ test "copy text between files" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) @miniio.write_text_file(dir + "/input.txt", "hello\n") @miniio.copy_file(dir + "/input.txt", dir + "/output.txt") @@ -92,7 +92,7 @@ test "append log file" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/tool.log" @miniio.write_text_file(path, "start\n") @@ -111,7 +111,7 @@ test "read and write json" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/manifest.json" @miniio.write_json_file(path, { "name": "miniio", "portable": true }) diff --git a/example/cli_tools/moon.mod b/example/cli_tools/moon.mod new file mode 100644 index 0000000..4020363 --- /dev/null +++ b/example/cli_tools/moon.mod @@ -0,0 +1,9 @@ +name = "moonbit-community/miniio-example-cli-tools" + +version = "0.1.0" + +import { + "moonbit-community/miniio@0.2.0", +} + +preferred_target = "wasm" diff --git a/example/cli_tools/moon.mod.json b/example/cli_tools/moon.mod.json deleted file mode 100644 index 6a79c15..0000000 --- a/example/cli_tools/moon.mod.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "moonbit-community/miniio-example-cli-tools", - "version": "0.1.0", - "deps": { - "moonbit-community/miniio": "0.1.0" - }, - "preferred-target": "wasm" -} diff --git a/example/lottie_manifest/moon.mod b/example/lottie_manifest/moon.mod new file mode 100644 index 0000000..ebf3837 --- /dev/null +++ b/example/lottie_manifest/moon.mod @@ -0,0 +1,10 @@ +name = "moonbit-community/miniio-example-lottie-manifest" + +version = "0.1.0" + +import { + "moonbit-community/miniio@0.2.0", + "cg-zhou/moon_lottie@0.3.0", +} + +preferred_target = "wasm" diff --git a/example/lottie_manifest/moon.mod.json b/example/lottie_manifest/moon.mod.json deleted file mode 100644 index f44529d..0000000 --- a/example/lottie_manifest/moon.mod.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "moonbit-community/miniio-example-lottie-manifest", - "version": "0.1.0", - "deps": { - "moonbit-community/miniio": "0.1.0", - "cg-zhou/moon_lottie": "0.3.0" - }, - "preferred-target": "wasm" -} diff --git a/example/lottie_manifest/moon.pkg b/example/lottie_manifest/moon.pkg index 99dd1c4..3833934 100644 --- a/example/lottie_manifest/moon.pkg +++ b/example/lottie_manifest/moon.pkg @@ -1,6 +1,6 @@ import { "moonbit-community/miniio", - "cg-zhou/moon_lottie/lib/parser" @parser, + "cg-zhou/moon_lottie/lib/parser", "moonbitlang/core/json", } diff --git a/example/morm_query/moon.mod b/example/morm_query/moon.mod new file mode 100644 index 0000000..48cb209 --- /dev/null +++ b/example/morm_query/moon.mod @@ -0,0 +1,10 @@ +name = "moonbit-community/miniio-example-morm-query" + +version = "0.1.0" + +import { + "moonbit-community/miniio@0.2.0", + "oboard/morm@0.3.15", +} + +preferred_target = "wasm" diff --git a/example/morm_query/moon.mod.json b/example/morm_query/moon.mod.json deleted file mode 100644 index 8da2f1d..0000000 --- a/example/morm_query/moon.mod.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "moonbit-community/miniio-example-morm-query", - "version": "0.1.0", - "deps": { - "moonbit-community/miniio": "0.1.0", - "oboard/morm": "0.3.12" - }, - "preferred-target": "wasm" -} diff --git a/example/morm_query/moon.pkg b/example/morm_query/moon.pkg index b77b6b5..8d0b3c3 100644 --- a/example/morm_query/moon.pkg +++ b/example/morm_query/moon.pkg @@ -1,7 +1,7 @@ import { "moonbit-community/miniio", "oboard/morm", - "oboard/morm/engine" @engine, + "oboard/morm/engine", } supported_targets = "wasm" diff --git a/fd.mbt b/fd.mbt index 2a7ee29..900d0e5 100644 --- a/fd.mbt +++ b/fd.mbt @@ -42,16 +42,16 @@ pub fn File::tell(self : File) -> UInt64 raise Errno { } ///| -pub impl @io.Reader for File with _get_internal_buffer(self) { +pub impl @io.Reader for File with fn _get_internal_buffer(self) { self.read_buf } ///| -pub impl @io.Reader for File with _direct_read(self, buf, offset~, max_len~) { +pub impl @io.Reader for File with fn _direct_read(self, buf, offset~, max_len~) { wrap(() => self.raw.read(buf, offset~, max_len~)) } ///| -pub impl @io.Writer for File with write_once(self, data) { +pub impl @io.Writer for File with fn write_once(self, data) { wrap(() => self.raw.write_once(data)) } diff --git a/fs.mbt b/fs.mbt index 3b4189b..c9541ff 100644 --- a/fs.mbt +++ b/fs.mbt @@ -38,14 +38,21 @@ pub fn open( mode? : Mode = ReadOnly, append? : Bool = false, create_mode? : CreateMode = OpenExisting, + follow_symlink? : Bool = true, ) -> File raise Errno { { raw: wrap(() => { + let lookup_flags = if follow_symlink { + @raw.LookupFlags::new().symlink_follow() + } else { + @raw.LookupFlags::new() + } @raw.open( path, mode=mode.to_raw(), append~, create_mode=create_mode.to_raw(), + lookup_flags~, ) }), read_buf: @io.ReaderBuffer::new(), @@ -54,7 +61,7 @@ pub fn open( ///| pub fn create(path : StringView) -> File raise Errno { - { raw: wrap(() => @raw.create(path)), read_buf: @io.ReaderBuffer::new() } + open(path, mode=WriteOnly, create_mode=CreateOrTruncate) } ///| @@ -85,8 +92,14 @@ pub fn readdir( path : StringView, include_hidden? : Bool = true, sort? : Bool = false, + follow_symlink? : Bool = true, ) -> Array[String] raise Errno { - let result = wrap(() => @raw.readdir_names(path)) + let lookup_flags = if follow_symlink { + @raw.LookupFlags::new().symlink_follow() + } else { + @raw.LookupFlags::new() + } + let result = wrap(() => @raw.readdir_names(path, lookup_flags~)) let result = if include_hidden { result } else { @@ -147,7 +160,13 @@ pub fn is_file(path : StringView) -> Bool raise Errno { ///| pub fn rmdir(path : StringView, recursive? : Bool = false) -> Unit raise Errno { if recursive { - for entry in readdir(path, include_hidden=true, sort=false) { + for + entry in readdir( + path, + include_hidden=true, + sort=false, + follow_symlink=false, + ) { let entry_path = path.to_owned() + "/" + entry remove(entry_path) catch { Notempty => rmdir(entry_path, recursive=true) diff --git a/fs_test.mbt b/fs_test.mbt index 205fbeb..33c8c47 100644 --- a/fs_test.mbt +++ b/fs_test.mbt @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +///| +fn host_symlink_tests_enabled() -> Bool raise Errno { + @miniio.get_env_var("RUNNER_OS") != Some("Windows") +} + ///| test "write_file supports create append and truncate" { let dir = "miniio_test_dir_write_file" @@ -21,7 +26,7 @@ test "write_file supports create append and truncate" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/sample.txt" @miniio.write_file(path, b"hello") @@ -43,16 +48,17 @@ test "create modes distinguish existing files" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/sample.txt" @miniio.write_text_file(path, "first", create_mode=CreateNew) inspect(@miniio.read_text_file(path), content="first") - debug_inspect( - try? @miniio.write_text_file(path, "second", create_mode=CreateNew), - content="Err(Exist)", - ) + try @miniio.write_text_file(path, "second", create_mode=CreateNew) catch { + e => debug_inspect(e, content="Exist") + } noraise { + _ => abort("expected CreateNew to reject an existing file") + } @miniio.write_text_file(path, "second", create_mode=CreateOrTruncate) inspect(@miniio.read_text_file(path), content="second") @@ -67,7 +73,7 @@ test "readdir sorts and filters hidden entries" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) @miniio.write_file(dir + "/b.txt", b"b") @miniio.write_file(dir + "/a.txt", b"a") @@ -83,6 +89,80 @@ test "readdir sorts and filters hidden entries" { ) } +///| +test "open follows symlink by default and can reject symlink" { + if host_symlink_tests_enabled() { + let target = "miniio_test_symlink_target.txt" + let link = "testdata/symlinks/file-link" + @miniio.remove(target) catch { + Noent => () + e => raise e + } + defer (@miniio.remove(target) catch { _ => () }) + + @miniio.write_text_file(target, "target") + try @miniio.read_text_file(link) catch { + Noent => () + e => raise e + } noraise { + content => + // Windows checkouts may materialize symlink fixtures as plain text files. + if content == "target" { + let file = @miniio.create(link) + file.write_text("created through link") + file.close() + inspect( + @miniio.read_text_file(target), + content="created through link", + ) + + try { + let file = @miniio.open(link, follow_symlink=false) + file.close() + abort( + "expected opening a symlink with follow_symlink=false to fail", + ) + } catch { + _ => () + } + } + } + } +} + +///| +test "readdir follows directory symlink by default and can reject symlink" { + if host_symlink_tests_enabled() { + let dir = "miniio_test_symlink_target_dir" + let link = "testdata/symlinks/dir-link" + @miniio.remove(dir) catch { + Noent => () + Notempty => @miniio.rmdir(dir, recursive=true) + e => raise e + } + @miniio.mkdir(dir) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) + + @miniio.write_text_file(dir + "/visible.txt", "visible") + @miniio.write_text_file(dir + "/.hidden", "hidden") + + try @miniio.readdir(link, include_hidden=false, sort=true) catch { + Noent | Notdir => () + e => raise e + } noraise { + entries => { + debug_inspect(entries, content="[\"visible.txt\"]") + try { + ignore(@miniio.readdir(link, follow_symlink=false)) + abort("expected reading a symlink with follow_symlink=false to fail") + } catch { + _ => () + } + } + } + } +} + ///| test "exists and kind classify file and directory" { let dir = "miniio_test_dir_kind" @@ -92,7 +172,7 @@ test "exists and kind classify file and directory" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/sample.txt" @miniio.write_file(path, b"sample") @@ -119,7 +199,7 @@ test "read_file returns io data" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/sample.txt" @miniio.write_text_file(path, "hello") @@ -138,7 +218,7 @@ test "copy_file copies through reader and writer" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let src = dir + "/src.txt" let dst = dir + "/dst.txt" @@ -156,7 +236,7 @@ test "json file helpers read and write json" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/config.json" @miniio.write_json_file(path, { "name": "miniio", "ok": true }) @@ -175,7 +255,7 @@ test "remove and recursive rmdir delete created paths" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let file_path = dir + "/sample.txt" let nested_dir = dir + "/nested" @@ -185,8 +265,46 @@ test "remove and recursive rmdir delete created paths" { @miniio.write_file(nested_dir + "/.hidden", b"hidden") @miniio.remove_file(file_path) - debug_inspect(try? @miniio.remove(file_path), content="Err(Noent)") + try @miniio.remove(file_path) catch { + e => debug_inspect(e, content="Noent") + } noraise { + _ => abort("expected removing a missing file to fail") + } @miniio.rmdir(dir, recursive=true) - debug_inspect(try? @miniio.rmdir(dir), content="Err(Noent)") + try @miniio.rmdir(dir) catch { + e => debug_inspect(e, content="Noent") + } noraise { + _ => abort("expected removing a missing directory to fail") + } +} + +///| +test "recursive rmdir does not follow symlink root" { + if host_symlink_tests_enabled() { + let dir = "miniio_test_symlink_rmdir_target_dir" + let link = "testdata/symlinks/rmdir-dir-link" + @miniio.remove(dir) catch { + Noent => () + Notempty => @miniio.rmdir(dir, recursive=true) + e => raise e + } + @miniio.mkdir(dir) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) + + @miniio.write_text_file(dir + "/target.txt", "keep") + try @miniio.readdir(link, include_hidden=false, sort=true) catch { + Noent | Notdir => () + e => raise e + } noraise { + _ => { + try @miniio.rmdir(link, recursive=true) catch { + _ => () + } noraise { + _ => abort("expected recursive rmdir on a symlink root to fail") + } + inspect(@miniio.read_text_file(dir + "/target.txt"), content="keep") + } + } + } } diff --git a/internal/raw/fs.mbt b/internal/raw/fs.mbt index 583c5ab..fe10a8c 100644 --- a/internal/raw/fs.mbt +++ b/internal/raw/fs.mbt @@ -225,6 +225,7 @@ pub fn open( mode? : Mode = ReadOnly, append? : Bool = false, create_mode? : CreateMode = OpenExisting, + lookup_flags? : LookupFlags = LookupFlags::new(), ) -> File raise Errno { let (base_fd, relative_path) = resolve_path(path) let oflags = create_mode.to_open_flags() @@ -235,7 +236,7 @@ pub fn open( let needs_resize = mode is WriteOnly || mode is ReadWrite { fd: base_fd.path_open( - LookupFlags::new(), + lookup_flags, relative_path, oflags, file_rights(mode, needs_resize), @@ -265,12 +266,15 @@ pub fn create(path : StringView) -> File raise Errno { } ///| -fn open_dir(path : StringView) -> Fd raise Errno { +fn open_dir( + path : StringView, + lookup_flags? : LookupFlags = LookupFlags::new(), +) -> Fd raise Errno { let (base_fd, relative_path) = resolve_path(path) let rights = Rights::new() |> Rights::set(Fd_read) |> Rights::set(Fd_readdir) let oflags = OpenFlags::new() |> OpenFlags::set(Directory) base_fd.path_open( - LookupFlags::new(), + lookup_flags, relative_path, oflags, rights, @@ -301,11 +305,12 @@ pub fn remove_dir(path : StringView) -> Unit raise Errno { pub fn readdir( path : StringView, buffer_size? : Int = 4096, + lookup_flags? : LookupFlags = LookupFlags::new(), ) -> Array[DirEntry] raise Errno { if buffer_size <= 0 { raise Inval } - let fd = open_dir(path) + let fd = open_dir(path, lookup_flags~) with_closed_fd(fd, fd => { let result : Array[DirEntry] = [] let mut cookie : DirCookie = DirCookie(0) @@ -345,9 +350,10 @@ pub fn readdir( pub fn readdir_names( path : StringView, buffer_size? : Int = 4096, + lookup_flags? : LookupFlags = LookupFlags::new(), ) -> Array[String] raise Errno { let names = [] - for entry in readdir(path, buffer_size~) { + for entry in readdir(path, buffer_size~, lookup_flags~) { names.push(entry.name()) } names diff --git a/internal/raw/pkg.generated.mbti b/internal/raw/pkg.generated.mbti index cd8ddd6..86db317 100644 --- a/internal/raw/pkg.generated.mbti +++ b/internal/raw/pkg.generated.mbti @@ -14,7 +14,7 @@ pub fn envs() -> Array[(String, String)] raise Errno pub fn mkdir(StringView) -> Unit raise Errno -pub fn open(StringView, mode? : Mode, append? : Bool, create_mode? : CreateMode) -> File raise Errno +pub fn open(StringView, mode? : Mode, append? : Bool, create_mode? : CreateMode, lookup_flags? : LookupFlags) -> File raise Errno pub fn open_read(StringView) -> File raise Errno @@ -24,9 +24,9 @@ pub fn preopens() -> Array[String] raise Errno pub fn read_file(StringView) -> Bytes raise Errno -pub fn readdir(StringView, buffer_size? : Int) -> Array[DirEntry] raise Errno +pub fn readdir(StringView, buffer_size? : Int, lookup_flags? : LookupFlags) -> Array[DirEntry] raise Errno -pub fn readdir_names(StringView, buffer_size? : Int) -> Array[String] raise Errno +pub fn readdir_names(StringView, buffer_size? : Int, lookup_flags? : LookupFlags) -> Array[String] raise Errno pub fn remove_dir(StringView) -> Unit raise Errno @@ -177,6 +177,8 @@ type FileType derive(Eq, @debug.Debug) type Inode derive(Eq, @debug.Debug) type LookupFlags derive(Eq, @debug.Debug) +pub fn LookupFlags::new() -> Self +pub fn LookupFlags::symlink_follow(Self) -> Self pub(all) enum Mode { ReadOnly diff --git a/internal/raw/types.mbt b/internal/raw/types.mbt index 440858b..4bfaebf 100644 --- a/internal/raw/types.mbt +++ b/internal/raw/types.mbt @@ -391,10 +391,15 @@ fn FdFlags::set(self : FdFlags, flag : FdFlag) -> FdFlags { struct LookupFlags(UInt) derive(Eq, Debug) ///| -fn LookupFlags::new() -> LookupFlags { +pub fn LookupFlags::new() -> LookupFlags { 0 } +///| +pub fn LookupFlags::symlink_follow(self : LookupFlags) -> LookupFlags { + self.0 | 1 +} + ///| struct OpenFlags(UInt) derive(Eq, Debug) diff --git a/io/data.mbt b/io/data.mbt index b2b1483..ef8a56e 100644 --- a/io/data.mbt +++ b/io/data.mbt @@ -14,42 +14,42 @@ ///| trait Data { - to_bytesview(Self) -> BytesView = _ - to_bytes(Self) -> Bytes + fn to_bytesview(Self) -> BytesView = _ + fn to_bytes(Self) -> Bytes } ///| -impl Data with to_bytesview(self) { +impl Data with fn to_bytesview(self) { self.to_bytes()[:] } ///| -pub impl Data for Bytes with to_bytes(self) { +pub impl Data for Bytes with fn to_bytes(self) { self } ///| -pub impl Data for BytesView with to_bytes(self) { +pub impl Data for BytesView with fn to_bytes(self) { self.to_owned() } ///| -pub impl Data for BytesView with to_bytesview(self) { +pub impl Data for BytesView with fn to_bytesview(self) { self } ///| -pub impl Data for String with to_bytes(self) { +pub impl Data for String with fn to_bytes(self) { @utf8.encode(self) } ///| -pub impl Data for StringView with to_bytes(self) { +pub impl Data for StringView with fn to_bytes(self) { @utf8.encode(self) } ///| -pub impl Data for Json with to_bytes(self) { +pub impl Data for Json with fn to_bytes(self) { @utf8.encode(self.stringify()) } diff --git a/io/moon.pkg b/io/moon.pkg index 85c172f..b76f51e 100644 --- a/io/moon.pkg +++ b/io/moon.pkg @@ -1,7 +1,7 @@ import { - "moonbit-community/miniio/internal/io_buffer" @io_buffer, + "moonbit-community/miniio/internal/io_buffer", "moonbitlang/core/cmp", - "moonbitlang/core/encoding/utf8" @utf8, + "moonbitlang/core/encoding/utf8", "moonbitlang/core/json", } diff --git a/io/pkg.generated.mbti b/io/pkg.generated.mbti index dd278c9..f343430 100644 --- a/io/pkg.generated.mbti +++ b/io/pkg.generated.mbti @@ -28,19 +28,19 @@ pub fn &Data::json(Self) -> Json raise pub fn &Data::text(Self) -> String raise pub(open) trait Reader { - _get_internal_buffer(Self) -> ReaderBuffer - _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int raise - read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int raise = _ - drop(Self, Int) -> Int raise = _ - read_exactly(Self, Int) -> Bytes raise = _ - read_some(Self, max_len? : Int) -> Bytes? raise = _ - read_all(Self) -> &Data raise = _ - read_until(Self, StringView) -> String? raise = _ + fn _get_internal_buffer(Self) -> ReaderBuffer + fn _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int raise + fn read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int raise = _ + fn drop(Self, Int) -> Int raise = _ + fn read_exactly(Self, Int) -> Bytes raise = _ + fn read_some(Self, max_len? : Int) -> Bytes? raise = _ + fn read_all(Self) -> &Data raise = _ + fn read_until(Self, StringView) -> String? raise = _ } pub(open) trait Writer { - write_once(Self, BytesView) -> Int raise - write(Self, &Data) -> Unit raise = _ - write_reader(Self, &Reader) -> Unit raise = _ + fn write_once(Self, BytesView) -> Int raise + fn write(Self, &Data) -> Unit raise = _ + fn write_reader(Self, &Reader) -> Unit raise = _ } diff --git a/io/reader.mbt b/io/reader.mbt index 0e203a0..a1d50f9 100644 --- a/io/reader.mbt +++ b/io/reader.mbt @@ -14,21 +14,26 @@ ///| pub(open) trait Reader { - _get_internal_buffer(Self) -> ReaderBuffer - _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int raise - read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int raise = _ - drop(Self, Int) -> Int raise = _ - read_exactly(Self, len : Int) -> Bytes raise = _ - read_some(Self, max_len? : Int) -> Bytes? raise = _ - read_all(Self) -> &Data raise = _ - read_until(Self, StringView) -> String? raise = _ + fn _get_internal_buffer(Self) -> ReaderBuffer + fn _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int raise + fn read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int raise = _ + fn drop(Self, Int) -> Int raise = _ + fn read_exactly(Self, len : Int) -> Bytes raise = _ + fn read_some(Self, max_len? : Int) -> Bytes? raise = _ + fn read_all(Self) -> &Data raise = _ + fn read_until(Self, StringView) -> String? raise = _ } ///| pub(all) suberror ReaderClosed derive(Debug, ToJson) ///| -impl Reader with read(self, dst, offset? = 0, max_len? = dst.length() - offset) { +impl Reader with fn read( + self, + dst, + offset? = 0, + max_len? = dst.length() - offset, +) { let buf = self._get_internal_buffer() if buf.len() > 0 { let n = @cmp.minimum(max_len, buf.len()) @@ -41,7 +46,7 @@ impl Reader with read(self, dst, offset? = 0, max_len? = dst.length() - offset) } ///| -impl Reader with drop(self, len) { +impl Reader with fn drop(self, len) { let buf = self._get_internal_buffer() if buf.len() >= len { buf.drop(len) @@ -70,7 +75,7 @@ impl Reader with drop(self, len) { } ///| -impl Reader with read_exactly(self, len) { +impl Reader with fn read_exactly(self, len) { let buf = FixedArray::make(len, b'\x00') for received = 0; received < len; { let new_received = self.read(buf, offset=received, max_len=len - received) @@ -83,7 +88,7 @@ impl Reader with read_exactly(self, len) { } ///| -impl Reader with read_some(self, max_len?) { +impl Reader with fn read_some(self, max_len?) { let buf = self._get_internal_buffer() if buf.len() > 0 { let buf_bytes = buf.buf().unsafe_reinterpret_as_bytes() @@ -115,7 +120,7 @@ impl Reader with read_some(self, max_len?) { } ///| -impl Reader with read_all(self) { +impl Reader with fn read_all(self) { let buffer_list = [] let mut buffer = FixedArray::make(1024, b'\x00') let mut offset = 0 @@ -138,7 +143,7 @@ impl Reader with read_all(self) { } ///| -impl Reader with read_until(self, sep) { +impl Reader with fn read_until(self, sep) { let sep = @utf8.encode(sep) let buf = self._get_internal_buffer() match buf.find_opt(sep, reader=self) { diff --git a/io/writer.mbt b/io/writer.mbt index 20b9b8b..6bf2646 100644 --- a/io/writer.mbt +++ b/io/writer.mbt @@ -14,13 +14,13 @@ ///| pub(open) trait Writer { - write_once(Self, BytesView) -> Int raise - write(Self, &Data) -> Unit raise = _ - write_reader(Self, &Reader) -> Unit raise = _ + fn write_once(Self, BytesView) -> Int raise + fn write(Self, &Data) -> Unit raise = _ + fn write_reader(Self, &Reader) -> Unit raise = _ } ///| -impl Writer with write(self, data) { +impl Writer with fn write(self, data) { let mut view = data.to_bytesview() while view.length() > 0 { let written = self.write_once(view) @@ -29,7 +29,7 @@ impl Writer with write(self, data) { } ///| -impl Writer with write_reader(self, reader) { +impl Writer with fn write_reader(self, reader) { let buf = reader._get_internal_buffer() if buf.len() > 0 { let buf_bytes = buf.buf().unsafe_reinterpret_as_bytes() diff --git a/io_test.mbt b/io_test.mbt index 8d723a1..cf7336d 100644 --- a/io_test.mbt +++ b/io_test.mbt @@ -21,7 +21,7 @@ test "file implements io reader and writer" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let path = dir + "/sample.txt" let writer = @miniio.create(path) @@ -47,7 +47,7 @@ test "write_reader copies file content" { e => raise e } @miniio.mkdir(dir) - defer ignore(try? @miniio.rmdir(dir, recursive=true)) + defer (@miniio.rmdir(dir, recursive=true) catch { _ => () }) let src_path = dir + "/src.txt" let dst_path = dir + "/dst.txt" diff --git a/moon.mod b/moon.mod new file mode 100644 index 0000000..2fde47d --- /dev/null +++ b/moon.mod @@ -0,0 +1,15 @@ +name = "moonbit-community/miniio" + +version = "0.2.1" + +description = "Small, portable WASIp1 I/O SDK for building MoonBit CLI tools and agent skills." + +keywords = [ "wasi", "wasip1", "io", "cli", "agent" ] + +readme = "README.md" + +repository = "https://github.com/moonbit-community/miniio" + +license = "Apache-2.0" + +preferred_target = "wasm" diff --git a/moon.mod.json b/moon.mod.json deleted file mode 100644 index bc8d357..0000000 --- a/moon.mod.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "moonbit-community/miniio", - "version": "0.2.0", - "description": "Small, portable WASIp1 I/O SDK for building MoonBit CLI tools and agent skills.", - "keywords": ["wasi", "wasip1", "io", "cli", "agent"], - "readme": "README.md", - "repository": "https://github.com/moonbit-community/miniio", - "license": "Apache-2.0", - "preferred-target": "wasm" -} diff --git a/moon.pkg b/moon.pkg index a3205e4..4566bc6 100644 --- a/moon.pkg +++ b/moon.pkg @@ -1,6 +1,6 @@ import { - "moonbit-community/miniio/io" @io, - "moonbit-community/miniio/internal/raw" @raw, + "moonbit-community/miniio/io", + "moonbit-community/miniio/internal/raw", } supported_targets = "wasm" diff --git a/pkg.generated.mbti b/pkg.generated.mbti index 00f2b1c..61d6c84 100644 --- a/pkg.generated.mbti +++ b/pkg.generated.mbti @@ -31,7 +31,7 @@ pub fn kind(StringView) -> FileKind raise Errno pub fn mkdir(StringView) -> Unit raise Errno -pub fn open(StringView, mode? : Mode, append? : Bool, create_mode? : CreateMode) -> File raise Errno +pub fn open(StringView, mode? : Mode, append? : Bool, create_mode? : CreateMode, follow_symlink? : Bool) -> File raise Errno pub fn read_file(StringView) -> &@io.Data raise Errno @@ -39,7 +39,7 @@ pub fn read_json_file(StringView) -> Json raise Errno pub fn read_text_file(StringView) -> String raise Errno -pub fn readdir(StringView, include_hidden? : Bool, sort? : Bool) -> Array[String] raise Errno +pub fn readdir(StringView, include_hidden? : Bool, sort? : Bool, follow_symlink? : Bool) -> Array[String] raise Errno pub fn remove(StringView) -> Unit raise Errno diff --git a/testdata/symlinks/dir-link b/testdata/symlinks/dir-link new file mode 120000 index 0000000..b0ae6ca --- /dev/null +++ b/testdata/symlinks/dir-link @@ -0,0 +1 @@ +../../miniio_test_symlink_target_dir \ No newline at end of file diff --git a/testdata/symlinks/file-link b/testdata/symlinks/file-link new file mode 120000 index 0000000..c9b2edf --- /dev/null +++ b/testdata/symlinks/file-link @@ -0,0 +1 @@ +../../miniio_test_symlink_target.txt \ No newline at end of file diff --git a/testdata/symlinks/rmdir-dir-link b/testdata/symlinks/rmdir-dir-link new file mode 120000 index 0000000..45bffdf --- /dev/null +++ b/testdata/symlinks/rmdir-dir-link @@ -0,0 +1 @@ +../../miniio_test_symlink_rmdir_target_dir \ No newline at end of file diff --git a/types.mbt b/types.mbt index b5b66df..c3e2f56 100644 --- a/types.mbt +++ b/types.mbt @@ -157,7 +157,7 @@ pub(all) suberror Errno { } derive(Eq, Debug) ///| -pub impl Show for Errno with to_string(self) { +pub impl Show for Errno with fn to_string(self) { match self { Success => "Success" TooBig => "TooBig" @@ -240,7 +240,7 @@ pub impl Show for Errno with to_string(self) { } ///| -pub impl Show for Errno with output(self, logger) { +pub impl Show for Errno with fn output(self, logger) { logger.write_string(self.to_string()) }