From 766810182d02156b3b6ec8119f33d7d773c4ddf8 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:36:42 +0800 Subject: [PATCH 1/7] feat: add fs_sync package --- fs_sync/blackbox_test.mbt | 27 +++++++++++ fs_sync/fs_sync.mbt | 66 ++++++++++++++++++++++++++ fs_sync/fs_sync.mbti | 19 ++++++++ fs_sync/moon.pkg.json | 18 ++++++++ fs_sync/test_1.txt | Bin 0 -> 24 bytes fs_sync/test_2.txt | 1 + fs_sync/util.mbt | 94 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 225 insertions(+) create mode 100644 fs_sync/blackbox_test.mbt create mode 100644 fs_sync/fs_sync.mbt create mode 100644 fs_sync/fs_sync.mbti create mode 100644 fs_sync/moon.pkg.json create mode 100644 fs_sync/test_1.txt create mode 100644 fs_sync/test_2.txt create mode 100644 fs_sync/util.mbt diff --git a/fs_sync/blackbox_test.mbt b/fs_sync/blackbox_test.mbt new file mode 100644 index 000000000..341a2a97d --- /dev/null +++ b/fs_sync/blackbox_test.mbt @@ -0,0 +1,27 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +test "write_and_read" { + let path_1 = "./fs_sync/test_1.txt" + let path_2 = "./fs_sync/test_2.txt" + let content_1 = "Hello, World" + let content_2 = "Hello, MoonBit" + @fs_sync.write_bytes_to_file!(path_1, content_1.to_bytes()) + let res_1 = @fs_sync.read_file_to_bytes!(path_1) + inspect!(res_1.to_unchecked_string(), content=content_1) + + @fs_sync.write_string_to_file!(path_2, content_2) + let res_2 = @fs_sync.read_file_to_string!(path_2) + inspect!(res_2, content=content_2) +} diff --git a/fs_sync/fs_sync.mbt b/fs_sync/fs_sync.mbt new file mode 100644 index 000000000..4a7feb44e --- /dev/null +++ b/fs_sync/fs_sync.mbt @@ -0,0 +1,66 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +pub type! IOError String derive(Show) + +///| Reads the content of a file specified by the given path and returns its +/// content as `Bytes` +/// +/// # Parameters +/// +/// - `path` : The path to the file to be read. +/// +/// # Returns +/// +/// - A `Bytes` representing the content of the file. +pub fn read_file_to_bytes(path: String) -> Bytes! { + read_file_to_bytes_internal!(path) +} + +///| Reads the content of a file specified by the given path and returns its +/// content as `String`. +/// +/// # Parameters +/// +/// - `path` : The path to the file to be read. +/// - `encoding~` : The encoding of the file. Only support `utf8` for now. +/// +/// # Returns +/// +/// - A `String` representing the content of the file. +pub fn read_file_to_string(path: String, encoding~ : String = "utf8") -> String! { + read_file_to_string_internal!(path, encoding~) +} + +///| Writes a `Bytes` to a file at the specified path. +/// +/// # Parameters +/// +/// - `path` : The path to the file where the bytes will be written. +/// - `content` : A `Bytes` to be written to the file. +pub fn write_bytes_to_file(path: String, content: Bytes) -> Unit! { + write_bytes_to_file_internal!(path, content) +} + +///| Writes a `String` to a file at the specified path. +/// +/// # Parameters +/// +/// - `path` : The path to the file where the string will be written. +/// - `content` : A `String` to be written to the file. +/// - `encoding~` : The encoding of the file. Only support `utf8` for now. +pub fn write_string_to_file(path: String, content: String, encoding~ : String = "utf8") -> Unit! { + write_string_to_file_internal!(path, content, encoding~) +} diff --git a/fs_sync/fs_sync.mbti b/fs_sync/fs_sync.mbti new file mode 100644 index 000000000..0d95b4827 --- /dev/null +++ b/fs_sync/fs_sync.mbti @@ -0,0 +1,19 @@ +package moonbitlang/core/fs_sync + +// Values +fn read_file_to_bytes(String) -> Bytes! + +fn read_file_to_string(String, encoding~ : String = ..) -> String! + +fn write_bytes_to_file(String, Bytes) -> Unit! + +fn write_string_to_file(String, String, encoding~ : String = ..) -> Unit! + +// Types and methods +pub type! IOError String +impl Show for IOError + +// Type aliases + +// Traits + diff --git a/fs_sync/moon.pkg.json b/fs_sync/moon.pkg.json new file mode 100644 index 000000000..74ac21b96 --- /dev/null +++ b/fs_sync/moon.pkg.json @@ -0,0 +1,18 @@ +{ + "import": [ + "moonbitlang/core/string", + "moonbitlang/core/builtin", + "moonbitlang/core/array", + "moonbitlang/core/bytes" + ], + "targets": { + "fs_sync_wasm.mbt": ["wasm", "wasm-gc"], + "fs_sync_js.mbt": ["js"], + "fs_sync_native.mbt": ["native"] + }, + "link": { + "native": { + "cc-flags": "-O2" + } + } +} diff --git a/fs_sync/test_1.txt b/fs_sync/test_1.txt new file mode 100644 index 0000000000000000000000000000000000000000..d96d5ec320b141aa3ee39cb5310887e16a1e6d4d GIT binary patch literal 24 bcmeZZNM*hHfRJ% literal 0 HcmV?d00001 diff --git a/fs_sync/test_2.txt b/fs_sync/test_2.txt new file mode 100644 index 000000000..064fd611c --- /dev/null +++ b/fs_sync/test_2.txt @@ -0,0 +1 @@ +Hello, MoonBit \ No newline at end of file diff --git a/fs_sync/util.mbt b/fs_sync/util.mbt new file mode 100644 index 000000000..5a8782021 --- /dev/null +++ b/fs_sync/util.mbt @@ -0,0 +1,94 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +///| +fn mbt_string_to_utf8_bytes(str : String, is_c_str : Bool) -> Bytes { + let res : Array[Byte] = [] + let len = str.length() + let mut i = 0 + while i < len { + let mut c = str[i].to_int() + if 0xD800 <= c && c <= 0xDBFF { + c -= 0xD800 + i = i + 1 + let l = str[i].to_int() - 0xDC00 + c = (c << 10) + l + 0x10000 + } + + // stdout accepts UTF-8, so convert the stream to UTF-8 first + if c < 0x80 { + res.push(c.to_byte()) + } else if c < 0x800 { + res.push((0xc0 + (c >> 6)).to_byte()) + res.push((0x80 + (c & 0x3f)).to_byte()) + } else if c < 0x10000 { + res.push((0xe0 + (c >> 12)).to_byte()) + res.push((0x80 + ((c >> 6) & 0x3f)).to_byte()) + res.push((0x80 + (c & 0x3f)).to_byte()) + } else { + res.push((0xf0 + (c >> 18)).to_byte()) + res.push((0x80 + ((c >> 12) & 0x3f)).to_byte()) + res.push((0x80 + ((c >> 6) & 0x3f)).to_byte()) + res.push((0x80 + (c & 0x3f)).to_byte()) + } + i = i + 1 + } + if is_c_str { + res.push((0).to_byte()) + } + Bytes::from_array(res) +} + +fn utf8_bytes_to_mbt_string(bytes : Bytes) -> String { + let res : Array[Char] = [] + let len = bytes.length() + let mut i = 0 + while i < len { + let mut c = bytes[i].to_int() + if c < 0x80 { + res.push(Char::from_int(c)) + i += 1 + } else if c < 0xE0 { + if i + 1 >= len { + break + } + c = ((c & 0x1F) << 6) | (bytes[i + 1].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 2 + } else if c < 0xF0 { + if i + 2 >= len { + break + } + c = ((c & 0x0F) << 12) | + ((bytes[i + 1].to_int() & 0x3F) << 6) | + (bytes[i + 2].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 3 + } else { + if i + 3 >= len { + break + } + c = ((c & 0x07) << 18) | + ((bytes[i + 1].to_int() & 0x3F) << 12) | + ((bytes[i + 2].to_int() & 0x3F) << 6) | + (bytes[i + 3].to_int() & 0x3F) + c -= 0x10000 + res.push(Char::from_int((c >> 10) + 0xD800)) + res.push(Char::from_int((c & 0x3FF) + 0xDC00)) + i += 4 + } + } + String::from_array(res) +} From 6a8a49aab75ee855372238b772be183ae1abb808 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:37:15 +0800 Subject: [PATCH 2/7] internal(fs): add wasm & wasm-gc support --- fs_sync/fs_sync_wasm.mbt | 151 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 fs_sync/fs_sync_wasm.mbt diff --git a/fs_sync/fs_sync_wasm.mbt b/fs_sync/fs_sync_wasm.mbt new file mode 100644 index 000000000..3067e0828 --- /dev/null +++ b/fs_sync/fs_sync_wasm.mbt @@ -0,0 +1,151 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn get_error_message_ffi() -> XExternString = "__moonbit_fs_unstable" "get_error_message" + +fn get_error_message() -> String { + string_from_extern(get_error_message_ffi()) +} + +fn get_file_content_ffi() -> XExternByteArray = "__moonbit_fs_unstable" "get_file_content" + +fn read_file_to_bytes_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "read_file_to_bytes_new" + +fn write_bytes_to_file_ffi(path : XExternString, content : XExternByteArray) -> Int = "__moonbit_fs_unstable" "write_bytes_to_file_new" + +fn read_file_to_bytes_internal(path : String) -> Bytes! { + let res = read_file_to_bytes_ffi(string_to_extern(path)) + guard res != -1 else { raise IOError(get_error_message()) } + byte_array_from_extern(get_file_content_ffi()) +} + +fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + utf8_bytes_to_mbt_string(read_file_to_bytes_internal!(path)) +} + +fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { + let res = write_bytes_to_file_ffi(string_to_extern(path), byte_array_to_extern(content)) + guard res != -1 else { raise IOError(get_error_message()) } +} + +fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + write_bytes_to_file_internal!(path, mbt_string_to_utf8_bytes(content, false)) +} + + +///| +priv type XStringCreateHandle + +///| +priv type XExternString + +///| +priv type XExternByteArray + +///| +priv type XByteArrayCreateHandle + +///| +priv type XByteArrayReadHandle + +///| +fn begin_create_string() -> XStringCreateHandle = "__moonbit_fs_unstable" "begin_create_string" + +///| +fn string_append_char(handle : XStringCreateHandle, ch : Char) = "__moonbit_fs_unstable" "string_append_char" + +///| +fn finish_create_string(handle : XStringCreateHandle) -> XExternString = "__moonbit_fs_unstable" "finish_create_string" + +///| +fn string_to_extern(s : String) -> XExternString { + let handle = begin_create_string() + for i = 0; i < s.length(); i = i + 1 { + string_append_char(handle, s[i]) + } + finish_create_string(handle) +} + +///| +fn begin_read_byte_array(s : XExternByteArray) -> XByteArrayReadHandle = "__moonbit_fs_unstable" "begin_read_byte_array" + +///| +fn byte_array_read_byte(handle : XByteArrayReadHandle) -> Int = "__moonbit_fs_unstable" "byte_array_read_byte" + +///| +fn finish_read_byte_array(handle : XByteArrayReadHandle) = "__moonbit_fs_unstable" "finish_read_byte_array" + +///| +fn begin_create_byte_array() -> XByteArrayCreateHandle = "__moonbit_fs_unstable" "begin_create_byte_array" + +///| +fn byte_array_append_byte(handle : XByteArrayCreateHandle, ch : Int) = "__moonbit_fs_unstable" "byte_array_append_byte" + +///| +fn finish_create_byte_array( + handle : XByteArrayCreateHandle +) -> XExternByteArray = "__moonbit_fs_unstable" "finish_create_byte_array" + +///| +fn byte_array_to_extern(s : Bytes) -> XExternByteArray { + let handle = begin_create_byte_array() + for i = 0; i < s.length(); i = i + 1 { + byte_array_append_byte(handle, s[i].to_int()) + } + finish_create_byte_array(handle) +} + +///| +fn byte_array_from_extern(e : XExternByteArray) -> Bytes { + let buf = Array::new() + let handle = begin_read_byte_array(e) + while true { + let ch = byte_array_read_byte(handle) + if ch == -1 { + break + } else { + buf.push(ch.to_byte()) + } + } + finish_read_byte_array(handle) + Bytes::from_array(buf) +} + +priv type XStringReadHandle +///| +fn begin_read_string(s : XExternString) -> XStringReadHandle = "__moonbit_fs_unstable" "begin_read_string" + +///| Read one char from the string, returns -1 if the end of the string is reached. +/// The number returned is the unicode codepoint of the character. +fn string_read_char(handle : XStringReadHandle) -> Int = "__moonbit_fs_unstable" "string_read_char" + +///| +fn finish_read_string(handle : XStringReadHandle) = "__moonbit_fs_unstable" "finish_read_string" + +fn string_from_extern(e : XExternString) -> String { + let buf = StringBuilder::new() + let handle = begin_read_string(e) + while true { + let ch = string_read_char(handle) + if ch == -1 { + break + } else { + buf.write_char(Char::from_int(ch)) + } + } + finish_read_string(handle) + buf.to_string() +} From 65901fb8823bc48f0bd02f03d8db8212aa5db6fb Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:37:23 +0800 Subject: [PATCH 3/7] internal(fs): add js support --- fs_sync/fs_sync_js.mbt | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 fs_sync/fs_sync_js.mbt diff --git a/fs_sync/fs_sync_js.mbt b/fs_sync/fs_sync_js.mbt new file mode 100644 index 000000000..6bd4276dd --- /dev/null +++ b/fs_sync/fs_sync_js.mbt @@ -0,0 +1,82 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +extern "js" fn read_file_ffi(path : String) -> Int = + #| function(path) { + #| fs = require('fs'); + #| try { + #| const content = fs.readFileSync(path); + #| globalThis.fileContent = content; + #| return 0; + #| } catch (error) { + #| globalThis.errorMessage = error.message; + #| return -1; + #| } + #| } + +///| +extern "js" fn write_file_ffi(path : String, content : Bytes) -> Int = + #| function(path, content) { + #| fs = require('fs'); + #| try { + #| fs.writeFileSync(path, Buffer.from(content)); + #| return 0; + #| } catch (error) { + #| globalThis.errorMessage = error.message; + #| return -1; + #| } + #| } + +///| +extern "js" fn get_file_content_ffi() -> FixedArray[Byte] = + #| function() { + #| return globalThis.fileContent; + #| } + +///| +extern "js" fn get_error_message_ffi() -> String = + #| function() { + #| return globalThis.errorMessage || ''; + #| } + +///| +fn read_file_to_bytes_internal(path : String) -> Bytes! { + let res = read_file_ffi(path) + if res == -1 { + raise IOError(get_error_message_ffi()) + } + let content = get_file_content_ffi() + Bytes::from_iter(content.iter()) +} + +fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + let bytes = read_file_to_bytes_internal!(path) + utf8_bytes_to_mbt_string(bytes) +} + +///| +fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { + let res = write_file_ffi(path, content) + if res == -1 { + raise IOError(get_error_message_ffi()) + } +} + +fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + let bytes = mbt_string_to_utf8_bytes(content, false) + write_bytes_to_file_internal!(path, bytes) +} From a78819950dfa95c34ffec31e0bae401a016bb5f7 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:37:30 +0800 Subject: [PATCH 4/7] internal(fs): add native support --- fs_sync/fs_sync_native.mbt | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 fs_sync/fs_sync_native.mbt diff --git a/fs_sync/fs_sync_native.mbt b/fs_sync/fs_sync_native.mbt new file mode 100644 index 000000000..bbab03742 --- /dev/null +++ b/fs_sync/fs_sync_native.mbt @@ -0,0 +1,84 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +priv type File_Handler + +///| +fn fopen_ffi(path : Bytes, mode : Bytes) -> File_Handler = "$moonbit.fopen_ffi" + +///| +fn is_null(ptr : File_Handler) -> Int = "$moonbit.is_null" + +///| +fn fread_ffi(ptr : Bytes, size : Int, nitems : Int, stream : File_Handler) -> Int = "$moonbit.fread_ffi" + +///| +fn fwrite_ffi(ptr : Bytes, size : Int, nitems : Int, stream : File_Handler) -> Int = "$moonbit.fwrite_ffi" + +///| +fn get_error_message_ffi() -> Bytes = "$moonbit.get_error_message" + +///| +fn fseek_ffi(file : File_Handler, offset : Int, whence : Int) -> Int = "$moonbit.fseek_ffi" + +///| +fn ftell_ffi(file : File_Handler) -> Int = "$moonbit.ftell_ffi" + +///| +fn fflush_ffi(stream : File_Handler) -> Int = "$moonbit.fflush_ffi" + +///| +fn fclose_ffi(file : File_Handler) -> Int = "$moonbit.fclose_ffi" + +///| +fn get_error_message() -> String { + utf8_bytes_to_mbt_string(get_error_message_ffi()) +} + +///| +fn read_file_to_bytes_internal(path : String) -> Bytes! { + let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"rb") + guard is_null(file) == 0 else { raise IOError(get_error_message()) } + guard fseek_ffi(file, 0, 2) == 0 else { raise IOError(get_error_message()) } + let size = ftell_ffi(file) + guard size != -1 else { raise IOError(get_error_message()) } + guard fseek_ffi(file, 0, 0) == 0 else { raise IOError(get_error_message()) } + let bytes = Bytes::make(size, 0) + let bytes_read = fread_ffi(bytes, 1, size, file) + guard bytes_read == size else { raise IOError(get_error_message()) } + guard fclose_ffi(file) == 0 else { raise IOError(get_error_message()) } + bytes +} + +fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + utf8_bytes_to_mbt_string(read_file_to_bytes_internal!(path)) +} + +///| +fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { + let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"wb") + guard is_null(file) == 0 else { raise IOError(get_error_message()) } + let bytes_written = fwrite_ffi(content, 1, content.length(), file) + guard bytes_written == content.length() else { raise IOError(get_error_message()) } + guard fflush_ffi(file) == 0 else { raise IOError(get_error_message()) } + guard fclose_ffi(file) == 0 else { raise IOError(get_error_message()) } +} + +fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { + guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } + let bytes = mbt_string_to_utf8_bytes(content, false) + write_bytes_to_file_internal!(path, bytes) +} From c8f6ac59a8ec874132e16db97238a95961779eb8 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:40:17 +0800 Subject: [PATCH 5/7] minor: moon fmt --- fs_sync/blackbox_test.mbt | 2 +- fs_sync/fs_sync.mbt | 15 ++++++++---- fs_sync/fs_sync_js.mbt | 25 ++++++++++++++++---- fs_sync/fs_sync_native.mbt | 44 +++++++++++++++++++++++++++++------ fs_sync/fs_sync_wasm.mbt | 47 ++++++++++++++++++++++++++++++++------ 5 files changed, 110 insertions(+), 23 deletions(-) diff --git a/fs_sync/blackbox_test.mbt b/fs_sync/blackbox_test.mbt index 341a2a97d..5fc5e95a0 100644 --- a/fs_sync/blackbox_test.mbt +++ b/fs_sync/blackbox_test.mbt @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +///| test "write_and_read" { let path_1 = "./fs_sync/test_1.txt" let path_2 = "./fs_sync/test_2.txt" @@ -20,7 +21,6 @@ test "write_and_read" { @fs_sync.write_bytes_to_file!(path_1, content_1.to_bytes()) let res_1 = @fs_sync.read_file_to_bytes!(path_1) inspect!(res_1.to_unchecked_string(), content=content_1) - @fs_sync.write_string_to_file!(path_2, content_2) let res_2 = @fs_sync.read_file_to_string!(path_2) inspect!(res_2, content=content_2) diff --git a/fs_sync/fs_sync.mbt b/fs_sync/fs_sync.mbt index 4a7feb44e..b140cd434 100644 --- a/fs_sync/fs_sync.mbt +++ b/fs_sync/fs_sync.mbt @@ -25,7 +25,7 @@ pub type! IOError String derive(Show) /// # Returns /// /// - A `Bytes` representing the content of the file. -pub fn read_file_to_bytes(path: String) -> Bytes! { +pub fn read_file_to_bytes(path : String) -> Bytes! { read_file_to_bytes_internal!(path) } @@ -40,7 +40,10 @@ pub fn read_file_to_bytes(path: String) -> Bytes! { /// # Returns /// /// - A `String` representing the content of the file. -pub fn read_file_to_string(path: String, encoding~ : String = "utf8") -> String! { +pub fn read_file_to_string( + path : String, + encoding~ : String = "utf8" +) -> String! { read_file_to_string_internal!(path, encoding~) } @@ -50,7 +53,7 @@ pub fn read_file_to_string(path: String, encoding~ : String = "utf8") -> String! /// /// - `path` : The path to the file where the bytes will be written. /// - `content` : A `Bytes` to be written to the file. -pub fn write_bytes_to_file(path: String, content: Bytes) -> Unit! { +pub fn write_bytes_to_file(path : String, content : Bytes) -> Unit! { write_bytes_to_file_internal!(path, content) } @@ -61,6 +64,10 @@ pub fn write_bytes_to_file(path: String, content: Bytes) -> Unit! { /// - `path` : The path to the file where the string will be written. /// - `content` : A `String` to be written to the file. /// - `encoding~` : The encoding of the file. Only support `utf8` for now. -pub fn write_string_to_file(path: String, content: String, encoding~ : String = "utf8") -> Unit! { +pub fn write_string_to_file( + path : String, + content : String, + encoding~ : String = "utf8" +) -> Unit! { write_string_to_file_internal!(path, content, encoding~) } diff --git a/fs_sync/fs_sync_js.mbt b/fs_sync/fs_sync_js.mbt index 6bd4276dd..805c43e06 100644 --- a/fs_sync/fs_sync_js.mbt +++ b/fs_sync/fs_sync_js.mbt @@ -61,8 +61,16 @@ fn read_file_to_bytes_internal(path : String) -> Bytes! { Bytes::from_iter(content.iter()) } -fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn read_file_to_string_internal( + path : String, + encoding~ : String = "utf8" +) -> String! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } let bytes = read_file_to_bytes_internal!(path) utf8_bytes_to_mbt_string(bytes) } @@ -75,8 +83,17 @@ fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { } } -fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn write_string_to_file_internal( + path : String, + content : String, + encoding~ : String = "utf8" +) -> Unit! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } let bytes = mbt_string_to_utf8_bytes(content, false) write_bytes_to_file_internal!(path, bytes) } diff --git a/fs_sync/fs_sync_native.mbt b/fs_sync/fs_sync_native.mbt index bbab03742..adec7986e 100644 --- a/fs_sync/fs_sync_native.mbt +++ b/fs_sync/fs_sync_native.mbt @@ -22,10 +22,20 @@ fn fopen_ffi(path : Bytes, mode : Bytes) -> File_Handler = "$moonbit.fopen_ffi" fn is_null(ptr : File_Handler) -> Int = "$moonbit.is_null" ///| -fn fread_ffi(ptr : Bytes, size : Int, nitems : Int, stream : File_Handler) -> Int = "$moonbit.fread_ffi" +fn fread_ffi( + ptr : Bytes, + size : Int, + nitems : Int, + stream : File_Handler +) -> Int = "$moonbit.fread_ffi" ///| -fn fwrite_ffi(ptr : Bytes, size : Int, nitems : Int, stream : File_Handler) -> Int = "$moonbit.fwrite_ffi" +fn fwrite_ffi( + ptr : Bytes, + size : Int, + nitems : Int, + stream : File_Handler +) -> Int = "$moonbit.fwrite_ffi" ///| fn get_error_message_ffi() -> Bytes = "$moonbit.get_error_message" @@ -62,8 +72,16 @@ fn read_file_to_bytes_internal(path : String) -> Bytes! { bytes } -fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn read_file_to_string_internal( + path : String, + encoding~ : String = "utf8" +) -> String! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } utf8_bytes_to_mbt_string(read_file_to_bytes_internal!(path)) } @@ -72,13 +90,25 @@ fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"wb") guard is_null(file) == 0 else { raise IOError(get_error_message()) } let bytes_written = fwrite_ffi(content, 1, content.length(), file) - guard bytes_written == content.length() else { raise IOError(get_error_message()) } + guard bytes_written == content.length() else { + raise IOError(get_error_message()) + } guard fflush_ffi(file) == 0 else { raise IOError(get_error_message()) } guard fclose_ffi(file) == 0 else { raise IOError(get_error_message()) } + } -fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn write_string_to_file_internal( + path : String, + content : String, + encoding~ : String = "utf8" +) -> Unit! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } let bytes = mbt_string_to_utf8_bytes(content, false) write_bytes_to_file_internal!(path, bytes) } diff --git a/fs_sync/fs_sync_wasm.mbt b/fs_sync/fs_sync_wasm.mbt index 3067e0828..1e81952ca 100644 --- a/fs_sync/fs_sync_wasm.mbt +++ b/fs_sync/fs_sync_wasm.mbt @@ -12,40 +12,70 @@ // See the License for the specific language governing permissions and // limitations under the License. +///| fn get_error_message_ffi() -> XExternString = "__moonbit_fs_unstable" "get_error_message" +///| fn get_error_message() -> String { string_from_extern(get_error_message_ffi()) } +///| fn get_file_content_ffi() -> XExternByteArray = "__moonbit_fs_unstable" "get_file_content" +///| fn read_file_to_bytes_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "read_file_to_bytes_new" -fn write_bytes_to_file_ffi(path : XExternString, content : XExternByteArray) -> Int = "__moonbit_fs_unstable" "write_bytes_to_file_new" +///| +fn write_bytes_to_file_ffi( + path : XExternString, + content : XExternByteArray +) -> Int = "__moonbit_fs_unstable" "write_bytes_to_file_new" +///| fn read_file_to_bytes_internal(path : String) -> Bytes! { let res = read_file_to_bytes_ffi(string_to_extern(path)) guard res != -1 else { raise IOError(get_error_message()) } byte_array_from_extern(get_file_content_ffi()) } -fn read_file_to_string_internal(path : String, encoding~ : String = "utf8") -> String! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn read_file_to_string_internal( + path : String, + encoding~ : String = "utf8" +) -> String! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } utf8_bytes_to_mbt_string(read_file_to_bytes_internal!(path)) } +///| fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { - let res = write_bytes_to_file_ffi(string_to_extern(path), byte_array_to_extern(content)) + let res = write_bytes_to_file_ffi( + string_to_extern(path), + byte_array_to_extern(content), + ) guard res != -1 else { raise IOError(get_error_message()) } + } -fn write_string_to_file_internal(path : String, content : String, encoding~ : String = "utf8") -> Unit! { - guard encoding == "utf8" else { raise IOError("Unsupported encoding: \{encoding}, only utf8 is supported for now") } +///| +fn write_string_to_file_internal( + path : String, + content : String, + encoding~ : String = "utf8" +) -> Unit! { + guard encoding == "utf8" else { + raise IOError( + "Unsupported encoding: \{encoding}, only utf8 is supported for now", + ) + } write_bytes_to_file_internal!(path, mbt_string_to_utf8_bytes(content, false)) } - ///| priv type XStringCreateHandle @@ -124,7 +154,9 @@ fn byte_array_from_extern(e : XExternByteArray) -> Bytes { Bytes::from_array(buf) } +///| priv type XStringReadHandle + ///| fn begin_read_string(s : XExternString) -> XStringReadHandle = "__moonbit_fs_unstable" "begin_read_string" @@ -135,6 +167,7 @@ fn string_read_char(handle : XStringReadHandle) -> Int = "__moonbit_fs_unstable" ///| fn finish_read_string(handle : XStringReadHandle) = "__moonbit_fs_unstable" "finish_read_string" +///| fn string_from_extern(e : XExternString) -> String { let buf = StringBuilder::new() let handle = begin_read_string(e) From 6e0ca3590cd91a91cc2eda97008bd1c248dbf9cc Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 16:53:00 +0800 Subject: [PATCH 6/7] ci: add windows-latest & macos-13 for bleeding --- .github/workflows/check.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 4c9c50e56..330f4e28d 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -240,16 +240,25 @@ jobs: continue-on-error: true strategy: matrix: - os: [macos-latest, ubuntu-latest] + os: [macos-latest, ubuntu-latest, windows-latest, macos-13] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: install + if: ${{ matrix.os != 'windows-latest' }} run: | - curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s 'bleeding' + curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s bleeding echo "$HOME/.moon/bin" >> $GITHUB_PATH + - name: install on windows + env: + MOONBIT_INSTALL_VERSION: bleeding + if: ${{ matrix.os == 'windows-latest' }} + run: | + Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex + "C:\Users\runneradmin\.moon\bin" | Out-File -FilePath $env:GITHUB_PATH -Append + - name: moon version run: | moon version --all @@ -259,6 +268,7 @@ jobs: run: moon check --deny-warn - name: Set ulimit and run moon test + if: ${{ matrix.os != 'windows-latest' }} run: | ulimit -s 8176 moon test --target all @@ -266,6 +276,17 @@ jobs: moon test --target native moon test --target native --release + - name: Setup MSVC + if: ${{ matrix.os == 'windows-latest' }} + uses: ilammy/msvc-dev-cmd@v1 + + - name: Run moon test on Windows + if: ${{ matrix.os == 'windows-latest' }} + run: | + moon test --target all + moon test --release --target all + moon test --target native + - name: moon test --doc run: | moon test --doc From 18b74d76e058f2226562549542a9be8e28f2d340 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 24 Jan 2025 17:24:46 +0800 Subject: [PATCH 7/7] fix: windows fopen mode should end with \x00 --- fs_sync/fs_sync_native.mbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs_sync/fs_sync_native.mbt b/fs_sync/fs_sync_native.mbt index adec7986e..dfe1dccd7 100644 --- a/fs_sync/fs_sync_native.mbt +++ b/fs_sync/fs_sync_native.mbt @@ -59,7 +59,7 @@ fn get_error_message() -> String { ///| fn read_file_to_bytes_internal(path : String) -> Bytes! { - let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"rb") + let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"rb\x00") guard is_null(file) == 0 else { raise IOError(get_error_message()) } guard fseek_ffi(file, 0, 2) == 0 else { raise IOError(get_error_message()) } let size = ftell_ffi(file) @@ -87,7 +87,7 @@ fn read_file_to_string_internal( ///| fn write_bytes_to_file_internal(path : String, content : Bytes) -> Unit! { - let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"wb") + let file = fopen_ffi(mbt_string_to_utf8_bytes(path, true), b"wb\x00") guard is_null(file) == 0 else { raise IOError(get_error_message()) } let bytes_written = fwrite_ffi(content, 1, content.length(), file) guard bytes_written == content.length() else {