blobstore: export impl_loadable_storable macro

Summary:
This will help people to introduce new blobstore objects in their code (for
instance I intend to use it in the following diff).

The `private` module exists to allow the use of the exported macro without the
need to write a bunch of `use` statements, and without pollution the re-export
namespace. The idea is that everything needed by the exported macro exists in
the `private` module of the crate, and this module is public.
So long as some other crate imports the macro, it expands to
`$crate::private::NEEDED_THING` in the right places and no further `use`
statements of dependencies are needed. At the same time, the name `private`
should discourage people from using whatever is in this module directly. The
idea is taken from `anyhow`.

Reviewed By: StanislavGlebik

Differential Revision: D27997228

fbshipit-source-id: fd2c421d0daf0fe88e2b9001bb4088fe7b4d59b7
This commit is contained in:
Kostia Balytskyi 2021-04-26 06:36:28 -07:00 committed by Facebook GitHub Bot
parent fbdd7a453c
commit 82ffc5b731
5 changed files with 131 additions and 85 deletions

View File

@ -17,6 +17,7 @@ async-trait = "0.1.45"
auto_impl = "0.4"
bytes = { version = "0.5", features = ["serde"] }
context = { version = "0.1.0", path = "../server/context" }
fbthrift = { version = "0.0.1+unstable", git = "https://github.com/facebook/fbthrift.git", branch = "master" }
futures-old = { package = "futures", version = "0.1.31" }
serde = { version = "=1.0.118", features = ["derive", "rc"] }
serde_derive = "1.0"

View File

@ -10,6 +10,7 @@
mod counted_blobstore;
mod disabled;
mod errors;
pub mod macros;
use abomonation_derive::Abomonation;
use anyhow::{Error, Result};
@ -29,6 +30,20 @@ pub use crate::counted_blobstore::CountedBlobstore;
pub use crate::disabled::DisabledBlob;
pub use crate::errors::ErrorKind;
// This module exists to namespace re-exported
// imports, needed for macto exports.
pub mod private {
pub use crate::{
Blobstore, BlobstoreBytes, BlobstoreGetData, Loadable, LoadableError, Storable,
};
pub use anyhow::Error;
pub use async_trait::async_trait;
pub use context::CoreContext;
pub use fbthrift::compact_protocol;
pub use std::convert::TryFrom;
pub use std::convert::TryInto;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobstoreGetData {
meta: BlobstoreMetadata,

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#[macro_export]
macro_rules! impl_blobstore_conversions {
($ty:ident, $thrift_ty:ident) => {
impl $crate::private::TryFrom<$crate::private::BlobstoreBytes> for $ty {
type Error = $crate::private::Error;
fn try_from(
bytes: $crate::private::BlobstoreBytes,
) -> Result<Self, $crate::private::Error> {
use std::convert::TryInto;
let t: $thrift_ty =
$crate::private::compact_protocol::deserialize(bytes.as_bytes().as_ref())?;
t.try_into()
}
}
impl From<$ty> for $crate::private::BlobstoreBytes {
fn from(other: $ty) -> Self {
let thrift: $thrift_ty = other.into();
let data = $crate::private::compact_protocol::serialize(&thrift);
Self::from_bytes(data)
}
}
impl $crate::private::TryFrom<$crate::private::BlobstoreGetData> for $ty {
type Error = $crate::private::Error;
fn try_from(
blob: $crate::private::BlobstoreGetData,
) -> Result<Self, $crate::private::Error> {
use std::convert::TryInto;
blob.into_bytes().try_into()
}
}
impl From<$ty> for $crate::private::BlobstoreGetData {
fn from(other: $ty) -> Self {
Into::<$crate::private::BlobstoreBytes>::into(other).into()
}
}
};
}
/// You can use this macro under the following conditions:
/// 1. handle_thrift_type can be serialized into handle_type using thrift compact protocol
/// 2. value_thrift_type can be serialized into value_type using thrift compact protocol
/// 3. value_type has method `fn handle(&self) -> handle_type`
/// 4. handle_type has method `fb blobstore_key(&self) -> String`
#[macro_export]
macro_rules! impl_loadable_storable {
(
handle_type => $handle: ident,
handle_thrift_type => $thrift_handle: ident,
value_type => $ty:ident,
value_thrift_type => $thrift_ty: ident,
) => {
#[$crate::private::async_trait]
impl $crate::private::Storable for $ty {
type Key = $handle;
async fn store<'a, B: $crate::private::Blobstore>(
self,
ctx: &'a $crate::private::CoreContext,
blobstore: &'a B,
) -> Result<Self::Key, $crate::private::Error> {
let handle = *self.handle();
let key = handle.blobstore_key();
blobstore.put(ctx, key, self.into()).await?;
Ok(handle)
}
}
#[$crate::private::async_trait]
impl $crate::private::Loadable for $handle {
type Value = $ty;
async fn load<'a, B: $crate::private::Blobstore>(
&'a self,
ctx: &'a $crate::private::CoreContext,
blobstore: &'a B,
) -> Result<Self::Value, $crate::private::LoadableError> {
use std::convert::TryInto;
let id = *self;
let bytes = blobstore.get(ctx, &id.blobstore_key()).await?;
match bytes {
Some(bytes) => bytes
.try_into()
.map_err($crate::private::LoadableError::Error),
None => Err($crate::private::LoadableError::Missing(id.blobstore_key())),
}
}
}
$crate::impl_blobstore_conversions!($handle, $thrift_handle);
$crate::impl_blobstore_conversions!($ty, $thrift_ty);
};
}

View File

@ -14,7 +14,6 @@ cloned = { version = "0.1.0", git = "https://github.com/facebookexperimental/rus
context = { version = "0.1.0", path = "../../server/context" }
derived_data = { version = "0.1.0", path = "../../derived_data" }
digest = "0.8"
fbthrift = { version = "0.0.1+unstable", git = "https://github.com/facebook/fbthrift.git", branch = "master" }
filestore = { version = "0.1.0", path = "../../filestore" }
futures = { version = "0.3.13", features = ["async-await", "compat"] }
futures_ext = { package = "futures_01_ext", version = "0.1.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "master" }

View File

@ -5,90 +5,14 @@
* GNU General Public License version 2.
*/
use anyhow::Error;
use async_trait::async_trait;
use blobstore::{Blobstore, BlobstoreBytes, BlobstoreGetData, Loadable, LoadableError, Storable};
use context::CoreContext;
use fbthrift::compact_protocol;
use std::convert::TryFrom;
use std::convert::TryInto;
use blobstore::impl_loadable_storable;
use crate::{thrift, Tree, TreeHandle};
use crate::thrift::{Tree as ThriftTree, TreeHandle as ThriftTreeHandle};
use crate::{Tree, TreeHandle};
macro_rules! impl_blobstore_conversions {
($ty:ident) => {
impl TryFrom<BlobstoreBytes> for $ty {
type Error = Error;
fn try_from(bytes: BlobstoreBytes) -> Result<Self, Error> {
let t: thrift::$ty = compact_protocol::deserialize(bytes.as_bytes().as_ref())?;
t.try_into()
}
}
impl Into<BlobstoreBytes> for $ty {
fn into(self) -> BlobstoreBytes {
let thrift: thrift::$ty = self.into();
let data = compact_protocol::serialize(&thrift);
BlobstoreBytes::from_bytes(data)
}
}
impl TryFrom<BlobstoreGetData> for $ty {
type Error = Error;
fn try_from(blob: BlobstoreGetData) -> Result<Self, Error> {
blob.into_bytes().try_into()
}
}
impl Into<BlobstoreGetData> for $ty {
fn into(self) -> BlobstoreGetData {
Into::<BlobstoreBytes>::into(self).into()
}
}
};
impl_loadable_storable! {
handle_type => TreeHandle,
handle_thrift_type => ThriftTreeHandle,
value_type => Tree,
value_thrift_type => ThriftTree,
}
macro_rules! impl_loadable_storable {
($handle: ident, $ty:ident) => {
#[async_trait]
impl Storable for $ty {
type Key = $handle;
async fn store<'a, B: Blobstore>(
self,
ctx: &'a CoreContext,
blobstore: &'a B,
) -> Result<Self::Key, Error> {
let handle = *self.handle();
let key = handle.blobstore_key();
blobstore.put(ctx, key, self.into()).await?;
Ok(handle)
}
}
#[async_trait]
impl Loadable for $handle {
type Value = $ty;
async fn load<'a, B: Blobstore>(
&'a self,
ctx: &'a CoreContext,
blobstore: &'a B,
) -> Result<Self::Value, LoadableError> {
let id = *self;
let bytes = blobstore.get(ctx, &id.blobstore_key()).await?;
match bytes {
Some(bytes) => bytes.try_into().map_err(LoadableError::Error),
None => Err(LoadableError::Missing(id.blobstore_key())),
}
}
}
impl_blobstore_conversions!($handle);
impl_blobstore_conversions!($ty);
};
}
impl_loadable_storable!(TreeHandle, Tree);