Skip to content

Commit

Permalink
add multi backend support for fs io (#59)
Browse files Browse the repository at this point in the history
* wasm

* js

* basiclly done

* add comment

* moon info

* use Bytes instead of Array[Byte]

* use guard and type!

* moon fmt

* enable coverage-check-bleeding

* bump version

* polish error show msg
  • Loading branch information
Young-Flash authored Sep 23, 2024
1 parent 83230f0 commit 3a09c6d
Show file tree
Hide file tree
Showing 17 changed files with 626 additions and 168 deletions.
1 change: 0 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ jobs:
git diff --exit-code
coverage-check-bleeding:
if: false
runs-on: macos-14
continue-on-error: true
steps:
Expand Down
155 changes: 155 additions & 0 deletions fs/fs.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright 2024 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 {
NotFound(String)
}

pub impl Show for IOError with output(self, logger) {
logger.write_string(self.to_string())
}

fn IOError::to_string(self : IOError) -> String {
match self {
IOError::NotFound(path) => "`\{path}` does not exist"
}
}

/// Writes a string to a file.
///
/// # Parameters
/// - `path`: A `String` representing the file path.
/// - `content`: A `String` containing the content to be written to the file.
pub fn write_string_to_file(~path : String, ~content : String) -> Unit {
@ffi.write_string_to_file(path, content)
}

/// Writes an array of bytes to a file at the specified path.
///
/// # Parameters
///
/// - `path` : The path to the file where the bytes will be written.
/// - `content` : An array of bytes to be written to the file.
pub fn write_bytes_to_file(~path : String, ~content : Bytes) -> Unit {
@ffi.write_bytes_to_file(path, content)
}

/// Checks if a path exists.
///
/// # Parameters
/// - `path`: A `String` representing the file path.
///
/// # Returns
/// A boolean indicating whether the path exists.
pub fn path_exists(~path : String) -> Bool {
@ffi.path_exists(path)
}

/// Reads the entire contents of a file into a string.
///
/// # Parameters
/// - `path`: A `String` representing the file path.
///
/// # Returns
/// A `String` containing the file contents if the file exists, otherwise raises an error.
pub fn read_file_to_string(~path : String) -> String! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.read_file_to_string(path)
}

/// Reads the content of a file specified by the given path and returns its
/// content as an array of bytes. If the file does not exist, an error is raised.
///
/// # Parameters
///
/// - `path` : The path to the file to be read.
///
/// # Returns
///
/// - An array of bytes representing the content of the file.
pub fn read_file_to_bytes(~path : String) -> Bytes! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.read_file_to_bytes(path)
}

/// Reads the contents of a directory and returns an array of filenames.
///
/// # Parameters
///
/// - `path` : The path to the directory to be read.
///
/// # Returns
///
/// - An array of strings representing the file name and directory name in the directory.
pub fn read_dir(~path : String) -> Array[String]! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.read_dir(path)
}

/// Creates a directory at the specified path.
///
/// # Parameters
///
/// - `path` : The path where the directory should be created.
pub fn create_dir(~path : String) -> Unit {
@ffi.create_dir(path)
}

/// Checks if the given path is a directory.
///
/// # Parameters
///
/// - `path` : The string representing the path to be checked.
///
/// # Returns
///
/// - `Bool` : `true` if the path is a directory, `false` otherwise.
pub fn is_dir(~path : String) -> Bool! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.is_dir(path)
}

/// Check if the given path points to a file.
///
/// # Parameters
///
/// - `path` : The string representing the path to be checked.
///
/// # Returns
///
/// - `Bool` : `true` if the path points to a file, `false` otherwise.
pub fn is_file(~path : String) -> Bool! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.is_file(path)
}

/// Removes a directory at the specified path.
///
/// # Parameters
///
/// - `path` : The string path to the directory that needs to be removed.
pub fn remove_dir(~path : String) -> Unit! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.remove_dir(path)
}

/// Removes a file at the specified path.
///
/// # Parameters
///
/// - `path` : The path to the file that needs to be removed.
pub fn remove_file(~path : String) -> Unit! {
guard path_exists(~path) else { raise IOError::NotFound(path) }
@ffi.remove_file(path)
}
26 changes: 23 additions & 3 deletions fs/fs.mbti
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
package moonbitlang/x/fs

// Values
fn exists(~path : String) -> Bool
fn create_dir(~path : String) -> Unit

fn read_to_string(~path : String) -> String!
fn is_dir(~path : String) -> Bool!

fn write_string(~path : String, ~content : String) -> Unit
fn is_file(~path : String) -> Bool!

fn path_exists(~path : String) -> Bool

fn read_dir(~path : String) -> Array[String]!

fn read_file_to_bytes(~path : String) -> Bytes!

fn read_file_to_string(~path : String) -> String!

fn remove_dir(~path : String) -> Unit!

fn remove_file(~path : String) -> Unit!

fn write_bytes_to_file(~path : String, ~content : Bytes) -> Unit

fn write_string_to_file(~path : String, ~content : String) -> Unit

// Types and methods
pub type! IOError {
NotFound(String)
}

// Type aliases

// Traits

// Extension Methods
impl Show for IOError

86 changes: 0 additions & 86 deletions fs/fs.wasm-gc.mbt

This file was deleted.

98 changes: 98 additions & 0 deletions fs/fs_test.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2024 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.txt"
@fs.write_string_to_file(
path="1.txt",
content=
#|target/
#|.mooncakes/
#|
,
)
assert_true!(@fs.path_exists(~path))
let byte = @fs.read_file_to_bytes!(~path).iter().map(fn(x) { x.to_uint() })
inspect!(
byte,
content="[116, 97, 114, 103, 101, 116, 47, 10, 46, 109, 111, 111, 110, 99, 97, 107, 101, 115, 47, 10]",
)
@fs.remove_file!(~path)
assert_false!(@fs.path_exists(~path))
try {
@fs.read_file_to_string!(~path) |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`1.txt` does not exist")
_ => return
}
let bytes = Bytes::from_array([65, 97].map(fn(x) { x.to_byte() }))
@fs.write_bytes_to_file(~path, content=bytes)
assert_true!(@fs.path_exists(~path))
let content = @fs.read_file_to_string!(~path)
inspect!(content, content="Aa")
@fs.remove_file!(~path)
assert_false!(@fs.path_exists(~path))
try {
@fs.remove_file!(~path) |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`1.txt` does not exist")
_ => return
}
}

test "path_exist" {
// dir exist
assert_true!(@fs.path_exists(path=".github"))
// dir don't exist
assert_false!(@fs.path_exists(path="no_exist"))

// file exist
assert_true!(@fs.path_exists(path=".gitignore"))

// file don't exist
assert_false!(@fs.path_exists(path="no_exist.txt"))
}

test "create_and_remove_dir" {
@fs.create_dir(path="hello/1/12.txt")
assert_true!(@fs.path_exists(path="hello/1/12.txt"))
@fs.remove_dir!(path="hello")
assert_false!(@fs.path_exists(path="hello"))
try {
@fs.remove_dir!(path="hello") |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`hello` does not exist")
_ => return
}
}

test "read_dir" {
let dir_content = @fs.read_dir!(path=".")..sort()
inspect!(
dir_content,
content=
#|["fs", "num", ".git", "time", "uuid", "json5", "stack", "crypto", "target", ".github", "README.md", ".gitignore", ".mooncakes", "_typos.toml", "moon.mod.json", "licenserc.toml"]
,
)
try {
@fs.read_dir!(path="fasd") |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`fasd` does not exist")
_ => return
}
}
Loading

0 comments on commit 3a09c6d

Please sign in to comment.