Skip to content

Commit

Permalink
refactor: Split operator APIs into different part (#1483)
Browse files Browse the repository at this point in the history
Signed-off-by: Xuanwo <[email protected]>
  • Loading branch information
Xuanwo authored Mar 5, 2023
1 parent b9dd40f commit bee01df
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 205 deletions.
18 changes: 4 additions & 14 deletions src/object/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ impl ObjectLister {
}
}

/// Fetch the operator that used by this object.
pub(crate) fn operator(&self) -> Operator {
self.acc.clone().into()
}

/// next_page can be used to fetch a new object page.
///
/// # Notes
Expand Down Expand Up @@ -93,7 +88,7 @@ impl ObjectLister {
Ok(Some(
entries
.into_iter()
.map(|v| v.into_object(self.operator()))
.map(|v| v.into_object(self.acc.clone()))
.collect(),
))
}
Expand All @@ -104,7 +99,7 @@ impl Stream for ObjectLister {

fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if let Some(oe) = self.buf.pop_front() {
return Poll::Ready(Some(Ok(oe.into_object(self.operator()))));
return Poll::Ready(Some(Ok(oe.into_object(self.acc.clone()))));
}

if let Some(fut) = self.fut.as_mut() {
Expand Down Expand Up @@ -154,11 +149,6 @@ impl BlockingObjectLister {
}
}

/// Fetch the operator that used by this object.
pub(crate) fn operator(&self) -> Operator {
self.acc.clone().into()
}

/// next_page can be used to fetch a new object page.
pub fn next_page(&mut self) -> Result<Option<Vec<Object>>> {
let entries = if !self.buf.is_empty() {
Expand All @@ -176,7 +166,7 @@ impl BlockingObjectLister {
Ok(Some(
entries
.into_iter()
.map(|v| v.into_object(self.operator()))
.map(|v| v.into_object(self.acc.clone()))
.collect(),
))
}
Expand All @@ -188,7 +178,7 @@ impl Iterator for BlockingObjectLister {

fn next(&mut self) -> Option<Self::Item> {
if let Some(oe) = self.buf.pop_front() {
return Some(Ok(oe.into_object(self.operator())));
return Some(Ok(oe.into_object(self.acc.clone())));
}

self.buf = match self.pager.next() {
Expand Down
13 changes: 4 additions & 9 deletions src/object/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,18 @@ impl Object {
/// - All path will be converted into relative path (without any leading `/`)
/// - Path endswith `/` means it's a dir path.
/// - Otherwise, it's a file path.
pub fn new(op: Operator, path: &str) -> Self {
Self::with(op, path, None)
pub(crate) fn new(acc: FusedAccessor, path: &str) -> Self {
Self::with(acc, path, None)
}

pub(crate) fn with(op: Operator, path: &str, meta: Option<ObjectMetadata>) -> Self {
pub(crate) fn with(acc: FusedAccessor, path: &str, meta: Option<ObjectMetadata>) -> Self {
Self {
acc: op.inner(),
acc,
path: Arc::new(normalize_path(path)),
meta: meta.map(Arc::new),
}
}

/// Fetch the operator that used by this object.
pub fn operator(&self) -> Operator {
self.acc.clone().into()
}

pub(crate) fn accessor(&self) -> FusedAccessor {
self.acc.clone()
}
Expand Down
195 changes: 194 additions & 1 deletion src/operator/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,205 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::sync::Arc;

use crate::layers::*;
use crate::raw::*;
use crate::*;

/// # APIs to build an Operator
///
/// Operator should be built via [`OperatorBuilder`]. We recommend to use [`Operator::create`] to get started:
///
/// ```
/// # use anyhow::Result;
/// use opendal::services::Fs;
/// use opendal::Operator;
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// // Create fs backend builder.
/// let mut builder = Fs::default();
/// // Set the root for fs, all operations will happen under this root.
/// //
/// // NOTE: the root must be absolute path.
/// builder.root("/tmp");
///
/// // Build an `Operator` to start operating the storage.
/// let op: Operator = Operator::create(builder)?.finish();
///
/// // Create an object handle to start operation on object.
/// let _ = op.object("test_file");
///
/// Ok(())
/// }
/// ```
impl Operator {
/// Create a new operator.
///
/// # Examples
///
/// Read more backend init examples in [examples](https://github.com/datafuselabs/opendal/tree/main/examples).
///
/// ```
/// # use anyhow::Result;
/// use opendal::services::Fs;
/// use opendal::Builder;
/// use opendal::Operator;
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// // Create fs backend builder.
/// let mut builder = Fs::default();
/// // Set the root for fs, all operations will happen under this root.
/// //
/// // NOTE: the root must be absolute path.
/// builder.root("/tmp");
///
/// // Build an `Operator` to start operating the storage.
/// let op: Operator = Operator::new(builder.build()?).finish();
///
/// // Create an object handle to start operation on object.
/// let _ = op.object("test_file");
///
/// Ok(())
/// }
/// ```
#[allow(clippy::new_ret_no_self)]
pub fn new<A: Accessor>(acc: A) -> OperatorBuilder<impl Accessor> {
OperatorBuilder::new(acc)
}

/// Create a new operator with input builder.
///
/// OpenDAL will call `builder.build()` internally, so we don't need
/// to import `opendal::Builder` trait.
///
/// # Examples
///
/// Read more backend init examples in [examples](https://github.com/datafuselabs/opendal/tree/main/examples).
///
/// ```
/// # use anyhow::Result;
/// use opendal::services::Fs;
/// use opendal::Operator;
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// // Create fs backend builder.
/// let mut builder = Fs::default();
/// // Set the root for fs, all operations will happen under this root.
/// //
/// // NOTE: the root must be absolute path.
/// builder.root("/tmp");
///
/// // Build an `Operator` to start operating the storage.
/// let op: Operator = Operator::create(builder)?.finish();
///
/// // Create an object handle to start operation on object.
/// let _ = op.object("test_file");
///
/// Ok(())
/// }
/// ```
pub fn create<B: Builder>(mut ab: B) -> Result<OperatorBuilder<impl Accessor>> {
let acc = ab.build()?;
Ok(OperatorBuilder::new(acc))
}

/// Create a new operator from given map.
///
/// ```
/// # use anyhow::Result;
/// use std::collections::HashMap;
///
/// use opendal::services::Fs;
/// use opendal::Operator;
/// #[tokio::main]
/// async fn main() -> Result<()> {
/// let map = HashMap::from([
/// // Set the root for fs, all operations will happen under this root.
/// //
/// // NOTE: the root must be absolute path.
/// ("root".to_string(), "/tmp".to_string()),
/// ]);
///
/// // Build an `Operator` to start operating the storage.
/// let op: Operator = Operator::from_map::<Fs>(map)?.finish();
///
/// // Create an object handle to start operation on object.
/// let _ = op.object("test_file");
///
/// Ok(())
/// }
/// ```
pub fn from_map<B: Builder>(
map: HashMap<String, String>,
) -> Result<OperatorBuilder<impl Accessor>> {
let acc = B::from_map(map).build()?;
Ok(OperatorBuilder::new(acc))
}

/// Create a new operator from iter.
///
/// # WARNING
///
/// It's better to use `from_map`. We may remove this API in the
/// future.
#[allow(clippy::should_implement_trait)]
pub fn from_iter<B: Builder>(
iter: impl Iterator<Item = (String, String)>,
) -> Result<OperatorBuilder<impl Accessor>> {
let acc = B::from_iter(iter).build()?;
Ok(OperatorBuilder::new(acc))
}

/// Create a new operator from env.
///
/// # WARNING
///
/// It's better to use `from_map`. We may remove this API in the
/// future.
pub fn from_env<B: Builder>() -> Result<OperatorBuilder<impl Accessor>> {
let acc = B::from_env().build()?;
Ok(OperatorBuilder::new(acc))
}

/// Create a new layer with dynamic dispatch.
///
/// # Notes
///
/// `OperatorBuilder::layer()` is using static dispatch which is zero
/// cost. `Operator::layer()` is using dynamic dispatch which has a
/// bit runtime overhead with an extra vtable lookup and unable to
/// inline.
///
/// It's always recommended to use `OperatorBuilder::layer()` instead.
///
/// # Examples
///
/// ```no_run
/// # use std::sync::Arc;
/// # use anyhow::Result;
/// use opendal::layers::LoggingLayer;
/// use opendal::services::Fs;
/// use opendal::Operator;
///
/// # #[tokio::main]
/// # async fn main() -> Result<()> {
/// let op = Operator::create(Fs::default())?.finish();
/// let op = op.layer(LoggingLayer::default());
/// // All operations will go through the new_layer
/// let _ = op.object("test_file").read().await?;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn layer<L: Layer<FusedAccessor>>(self, layer: L) -> Self {
Self::from_inner(Arc::new(
TypeEraseLayer.layer(layer.layer(self.into_innter())),
))
}
}

/// OperatorBuilder is a typed builder to builder an Operator.
///
/// # Notes
Expand Down Expand Up @@ -117,6 +310,6 @@ impl<A: Accessor> OperatorBuilder<A> {
pub fn finish(self) -> Operator {
let ob = self.layer(TypeEraseLayer);

Operator::from(Arc::new(ob.accessor) as FusedAccessor)
Operator::from_inner(Arc::new(ob.accessor) as FusedAccessor)
}
}
Loading

1 comment on commit bee01df

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for opendal ready!

✅ Preview
https://opendal-nn2qqlyxi-databend.vercel.app

Built with commit bee01df.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.