Skip to content

Commit

Permalink
feat(bindings/cpp): expose lister (#3011)
Browse files Browse the repository at this point in the history
Signed-off-by: silver-ymz <[email protected]>
  • Loading branch information
silver-ymz authored Sep 6, 2023
1 parent 0dc5031 commit 3760176
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 0 deletions.
78 changes: 78 additions & 0 deletions bindings/cpp/include/opendal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct Entry {
};

class Reader;
class Lister;

/**
* @class Operator
Expand Down Expand Up @@ -184,6 +185,8 @@ class Operator {
*/
std::vector<Entry> list(std::string_view path);

Lister lister(std::string_view path);

private:
std::optional<rust::Box<opendal::ffi::Operator>> operator_;
};
Expand Down Expand Up @@ -219,6 +222,7 @@ class Reader
* @class ReaderStream
* @brief ReaderStream is a stream wrapper of Reader which can provide
* `iostream` interface.
* @note It's an undefined behavior to make multiple streams from one reader.
*/
class ReaderStream
: public boost::iostreams::stream<boost::reference_wrapper<Reader>> {
Expand All @@ -227,4 +231,78 @@ class ReaderStream
: boost::iostreams::stream<boost::reference_wrapper<Reader>>(
boost::ref(reader)) {}
};

/**
* @class Lister
* @brief Lister is designed to list the entries of a directory.
* @details It provides next operation to get the next entry. You can also use
* it like an iterator.
* @code{.cpp}
* auto lister = operator.lister("dir/");
* for (const auto &entry : lister) {
* // Do something with entry
* }
* @endcode
*/
class Lister {
public:
Lister(rust::Box<opendal::ffi::Lister> &&lister)
: raw_lister_(std::move(lister)) {}

/**
* @class ListerIterator
* @brief ListerIterator is an iterator of Lister.
* @note It's an undefined behavior to make multiple iterators from one
* Lister.
*/
class ListerIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = Entry;
using difference_type = std::ptrdiff_t;
using pointer = Entry *;
using reference = Entry &;

ListerIterator(Lister &lister) : lister_(lister) {
current_entry_ = lister_.next();
}

Entry operator*() { return current_entry_.value(); }

ListerIterator &operator++() {
if (current_entry_) {
current_entry_ = lister_.next();
}
return *this;
}

bool operator!=(const ListerIterator &other) const {
return current_entry_ != std::nullopt ||
other.current_entry_ != std::nullopt;
}

protected:
// Only used for end iterator
ListerIterator(Lister &lister, bool /*end*/) : lister_(lister) {}

private:
Lister &lister_;
std::optional<Entry> current_entry_;

friend class Lister;
};

/**
* @brief Get the next entry of the lister
*
* @return The next entry of the lister
*/
std::optional<Entry> next();

ListerIterator begin() { return ListerIterator(*this); }
ListerIterator end() { return ListerIterator(*this, true); }

private:
rust::Box<opendal::ffi::Lister> raw_lister_;
};
} // namespace opendal
15 changes: 15 additions & 0 deletions bindings/cpp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
// specific language governing permissions and limitations
// under the License.

mod lister;
mod reader;
mod types;

use anyhow::Result;
use lister::Lister;
use opendal as od;
use reader::Reader;
use std::str::FromStr;
Expand Down Expand Up @@ -48,6 +50,11 @@ mod ffi {
value: String,
}

struct OptionalEntry {
has_value: bool,
value: Entry,
}

struct Metadata {
mode: EntryMode,
content_length: u64,
Expand All @@ -66,6 +73,7 @@ mod ffi {
extern "Rust" {
type Operator;
type Reader;
type Lister;

fn new_operator(scheme: &str, configs: Vec<HashMapValue>) -> Result<Box<Operator>>;
fn read(self: &Operator, path: &str) -> Result<Vec<u8>>;
Expand All @@ -78,9 +86,12 @@ mod ffi {
fn stat(self: &Operator, path: &str) -> Result<Metadata>;
fn list(self: &Operator, path: &str) -> Result<Vec<Entry>>;
fn reader(self: &Operator, path: &str) -> Result<Box<Reader>>;
fn lister(self: &Operator, path: &str) -> Result<Box<Lister>>;

fn read(self: &mut Reader, buf: &mut [u8]) -> Result<usize>;
fn seek(self: &mut Reader, offset: u64, dir: SeekFrom) -> Result<u64>;

fn next(self: &mut Lister) -> Result<OptionalEntry>;
}
}

Expand Down Expand Up @@ -144,4 +155,8 @@ impl Operator {
fn reader(&self, path: &str) -> Result<Box<Reader>> {
Ok(Box::new(Reader(self.0.reader(path)?)))
}

fn lister(&self, path: &str) -> Result<Box<Lister>> {
Ok(Box::new(Lister(self.0.lister(path)?)))
}
}
31 changes: 31 additions & 0 deletions bindings/cpp/src/lister.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.

use super::ffi;
use anyhow::Result;
use opendal as od;

pub struct Lister(pub od::BlockingLister);

impl Lister {
pub fn next(&mut self) -> Result<ffi::OptionalEntry> {
match self.0.next() {
Some(entry) => Ok(Some(entry?).into()),
None => Ok(None.into()),
}
}
}
15 changes: 15 additions & 0 deletions bindings/cpp/src/opendal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ std::vector<Entry> Operator::list(std::string_view path) {
return entries;
}

Lister Operator::lister(std::string_view path) {
return operator_.value()->lister(RUST_STR(path));
}

Reader Operator::reader(std::string_view path) {
return operator_.value()->reader(RUST_STR(path));
}
Expand All @@ -105,6 +109,17 @@ std::streampos Reader::seek(std::streamoff off, std::ios_base::seekdir dir) {
return raw_reader_->seek(off, to_rust_seek_dir(dir));
}

// Lister

std::optional<Entry> Lister::next() {
auto entry = raw_lister_->next();
if (entry.has_value) {
return std::move(entry.value);
} else {
return std::nullopt;
}
}

// Metadata

std::optional<std::string> parse_optional_string(ffi::OptionalString &&s);
Expand Down
17 changes: 17 additions & 0 deletions bindings/cpp/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,20 @@ impl From<Option<String>> for ffi::OptionalString {
}
}
}

impl From<Option<od::Entry>> for ffi::OptionalEntry {
fn from(entry: Option<od::Entry>) -> Self {
match entry {
Some(entry) => Self {
has_value: true,
value: entry.into(),
},
None => Self {
has_value: false,
value: ffi::Entry {
path: String::default(),
},
},
}
}
}
20 changes: 20 additions & 0 deletions bindings/cpp/tests/basic_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,26 @@ TEST_F(OpendalTest, ReaderTest) {
EXPECT_EQ(reader_data, data);
}

TEST_F(OpendalTest, ListerTest) {
op.create_dir("test_dir/");
op.write("test_dir/test1", {1, 2, 3});
op.write("test_dir/test2", {4, 5, 6});

int size = 0;
auto lister = op.lister("test_dir/");
for (const auto &entry : lister) {
EXPECT_TRUE(entry.path.find("test_dir/test") == 0);
size += 1;
}
EXPECT_EQ(size, 2);

lister = op.lister("test_dir/");
std::vector<opendal::Entry> paths(lister.begin(), lister.end());
EXPECT_EQ(paths.size(), 2);
EXPECT_EQ(paths[0].path, "test_dir/test1");
EXPECT_EQ(paths[1].path, "test_dir/test2");
}

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
Expand Down

0 comments on commit 3760176

Please sign in to comment.