diff --git a/bindings/nodejs/tests/binding.js b/bindings/nodejs/tests/binding.js index f77a4f79550..dd1e40d29ef 100644 --- a/bindings/nodejs/tests/binding.js +++ b/bindings/nodejs/tests/binding.js @@ -38,7 +38,7 @@ Then('The blocking file {string} entry mode must be file', function (path) { assert(meta.isFile()) }) -Then('The blocking file {string} content length must be "{int}"', function (path, size) { +Then('The blocking file {string} content length must be {int}', function (path, size) { let meta = this.op.statSync(path) assert(meta.contentLength == size) }) @@ -65,7 +65,7 @@ Then('The async file {string} entry mode must be file', async function (path) { assert(meta.isFile()) }) -Then('The async file {string} content length must be "{int}"', async function (path, size) { +Then('The async file {string} content length must be {int}', async function (path, size) { let meta = await this.op.stat(path) assert(meta.contentLength == size) }) diff --git a/bindings/python/tests/steps/binding.py b/bindings/python/tests/steps/binding.py index 49aa45a9238..feb36e982fa 100644 --- a/bindings/python/tests/steps/binding.py +++ b/bindings/python/tests/steps/binding.py @@ -35,7 +35,7 @@ def step_impl(context, filename): def step_impl(context, filename): assert context.op.stat(filename).mode.is_file() -@then('The blocking file "{filename}" content length must be "{size:d}"') +@then('The blocking file "{filename}" content length must be {size:d}') def step_impl(context, filename, size): assert context.op.stat(filename).content_length == size @@ -65,7 +65,7 @@ async def step_impl(context, filename): meta = await context.op.stat(filename) assert meta.mode.is_file() -@then('The async file "{filename}" content length must be "{size:d}"') +@then('The async file "{filename}" content length must be {size:d}') @async_run_until_complete async def step_impl(context, filename, size): meta = await context.op.stat(filename) diff --git a/bindings/ruby/src/lib.rs b/bindings/ruby/src/lib.rs index 59ee073e7a5..9af13fed2cf 100644 --- a/bindings/ruby/src/lib.rs +++ b/bindings/ruby/src/lib.rs @@ -103,6 +103,54 @@ impl Operator { .write(&path, bs.to_bytes()) .map_err(format_magnus_error) } + + /// Get current path's metadata **without cache** directly. + pub fn stat(&self, path: String) -> Result { + self.0 + .stat(&path) + .map_err(format_magnus_error) + .map(Metadata) + } +} + +#[magnus::wrap(class = "Metadata", free_immediately, size)] +pub struct Metadata(od::Metadata); + +impl Metadata { + /// Content-Disposition of this object + pub fn content_disposition(&self) -> Option<&str> { + self.0.content_disposition() + } + + /// Content length of this entry. + pub fn content_length(&self) -> u64 { + self.0.content_length() + } + + /// Content MD5 of this entry. + pub fn content_md5(&self) -> Option<&str> { + self.0.content_md5() + } + + /// Content Type of this entry. + pub fn content_type(&self) -> Option<&str> { + self.0.content_type() + } + + /// ETag of this entry. + pub fn etag(&self) -> Option<&str> { + self.0.etag() + } + + /// Returns `True` if this is a file. + pub fn is_file(&self) -> bool { + self.0.is_file() + } + + /// Returns `True` if this is a directory. + pub fn is_dir(&self) -> bool { + self.0.is_dir() + } } fn format_magnus_error(err: od::Error) -> Error { @@ -111,9 +159,22 @@ fn format_magnus_error(err: od::Error) -> Error { #[magnus::init] fn init() -> Result<()> { - let class = define_class("Operator", class::object())?; - class.define_singleton_method("new", function!(Operator::new, 2))?; - class.define_method("read", method!(Operator::read, 1))?; - class.define_method("write", method!(Operator::write, 2))?; + let operator_class = define_class("Operator", class::object())?; + operator_class.define_singleton_method("new", function!(Operator::new, 2))?; + operator_class.define_method("read", method!(Operator::read, 1))?; + operator_class.define_method("write", method!(Operator::write, 2))?; + operator_class.define_method("stat", method!(Operator::stat, 1))?; + + let metadata_class = define_class("Metadata", class::object())?; + metadata_class.define_method( + "content_disposition", + method!(Metadata::content_disposition, 0), + )?; + metadata_class.define_method("content_length", method!(Metadata::content_length, 0))?; + metadata_class.define_method("content_md5", method!(Metadata::content_md5, 0))?; + metadata_class.define_method("content_type", method!(Metadata::content_type, 0))?; + metadata_class.define_method("etag", method!(Metadata::etag, 0))?; + metadata_class.define_method("is_file", method!(Metadata::is_file, 0))?; + metadata_class.define_method("is_dir", method!(Metadata::is_dir, 0))?; Ok(()) } diff --git a/bindings/ruby/tests/steps/binding.rb b/bindings/ruby/tests/steps/binding.rb index d44986259a8..1607b2f7ef7 100644 --- a/bindings/ruby/tests/steps/binding.rb +++ b/bindings/ruby/tests/steps/binding.rb @@ -26,15 +26,15 @@ end Then("The blocking file {string} should exist") do |path| - pending # Write code here that turns the phrase above into concrete actions + @op.stat(path) end Then("The blocking file {string} entry mode must be file") do |path| - pending # Write code here that turns the phrase above into concrete actions + @op.stat(path).is_file == true end -Then("The blocking file {string} content length must be {string}") do |path, length| - pending # Write code here that turns the phrase above into concrete actions +Then("The blocking file {string} content length must be {int}") do |path, length| + @op.stat(path).content_length == length end Then("The blocking file {string} must have content {string}") do |path, content| @@ -57,7 +57,7 @@ pending # Write code here that turns the phrase above into concrete actions end -Then("The async file {string} content length must be {string}") do |path, length| +Then("The async file {string} content length must be {int}") do |path, length| pending # Write code here that turns the phrase above into concrete actions end diff --git a/bindings/tests/features/binding.feature b/bindings/tests/features/binding.feature index 22c08ab16c1..ad6832c091e 100644 --- a/bindings/tests/features/binding.feature +++ b/bindings/tests/features/binding.feature @@ -22,7 +22,7 @@ Feature: OpenDAL Binding When Blocking write path "test" with content "Hello, World!" Then The blocking file "test" should exist Then The blocking file "test" entry mode must be file - Then The blocking file "test" content length must be "13" + Then The blocking file "test" content length must be 13 Then The blocking file "test" must have content "Hello, World!" Scenario: OpenDAL Async Operations @@ -30,5 +30,5 @@ Feature: OpenDAL Binding When Async write path "test" with content "Hello, World!" Then The async file "test" should exist Then The async file "test" entry mode must be file - Then The async file "test" content length must be "13" + Then The async file "test" content length must be 13 Then The async file "test" must have content "Hello, World!"