sapling/eden/mononoke/derived_data/blame/lib.rs
Simon Farnsworth 454de31134 Switch Loadable and Storable interfaces to new-style futures
Summary:
Eventually, we want everything to be `async`/`await`; as a stepping stone in that direction, switch some of the blobstore interfaces to new-style `BoxFuture` with a `'static` lifetime.

This does not enable any fixes at this point, but does mean that `.compat()` moves to the places that need old-style futures instead of new. It also means that the work needed to make the transition fully complete is changed from a full conversion to new futures, to simply changing the lifetimes involved and fixing the resulting compile failures.

Reviewed By: krallin

Differential Revision: D22164315

fbshipit-source-id: dc655c36db4711d84d42d1e81b76e5dddd16f59d
2020-06-25 08:45:37 -07:00

129 lines
4.2 KiB
Rust

/*
* 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.
*/
#![deny(warnings)]
#![type_length_limit = "1441792"]
mod derived;
pub use derived::{fetch_file_full_content, BlameRoot, BlameRootMapping};
#[cfg(test)]
mod tests;
use anyhow::Error;
use blobrepo::BlobRepo;
use blobstore::{Loadable, LoadableError};
use bytes::Bytes;
use cloned::cloned;
use context::CoreContext;
use derived_data::{BonsaiDerived, DeriveError};
use futures::future::TryFutureExt;
use futures_ext::FutureExt;
use futures_old::{future, Future};
use manifest::ManifestOps;
use mononoke_types::{
blame::{Blame, BlameId, BlameMaybeRejected, BlameRejected},
ChangesetId, MPath,
};
use thiserror::Error;
use unodes::RootUnodeManifestId;
#[derive(Debug, Error)]
pub enum BlameError {
#[error("No such path: {0}")]
NoSuchPath(MPath),
#[error("Blame is not available for directories: {0}")]
IsDirectory(MPath),
#[error("{0}")]
Rejected(#[from] BlameRejected),
#[error("{0}")]
DeriveError(#[from] DeriveError),
#[error("{0}")]
Error(#[from] Error),
}
/// Fetch content and blame for a file with specified file path
///
/// Blame will be derived if it is not available yet.
pub fn fetch_blame(
ctx: CoreContext,
repo: BlobRepo,
csid: ChangesetId,
path: MPath,
) -> impl Future<Item = (Bytes, Blame), Error = BlameError> {
fetch_blame_if_derived(ctx.clone(), repo.clone(), csid, path)
.and_then({
cloned!(ctx, repo);
move |result| match result {
Ok((blame_id, blame)) => future::ok((blame_id, blame)).left_future(),
Err(blame_id) => BlameRoot::derive(ctx.clone(), repo.clone(), csid)
.from_err()
.and_then(move |_| {
blame_id
.load(ctx.clone(), repo.blobstore())
.compat()
.from_err()
.and_then(|blame_maybe_rejected| blame_maybe_rejected.into_blame())
.map(move |blame| (blame_id, blame))
.from_err()
})
.right_future(),
}
})
.and_then(move |(blame_id, blame)| {
derived::fetch_file_full_content(ctx, repo.get_blobstore().boxed(), blame_id.into())
.and_then(|result| result.map_err(Error::from))
.map(|content| (content, blame))
.from_err()
})
}
fn fetch_blame_if_derived(
ctx: CoreContext,
repo: BlobRepo,
csid: ChangesetId,
path: MPath,
) -> impl Future<Item = Result<(BlameId, Blame), BlameId>, Error = BlameError> {
let blobstore = repo.get_blobstore();
RootUnodeManifestId::derive(ctx.clone(), repo, csid)
.from_err()
.and_then({
cloned!(ctx, blobstore, path);
move |mf_root| {
mf_root
.manifest_unode_id()
.clone()
.find_entry(ctx, blobstore, Some(path))
}
})
.from_err()
.and_then({
cloned!(path);
move |entry_opt| {
let entry = entry_opt.ok_or_else(|| BlameError::NoSuchPath(path.clone()))?;
match entry.into_leaf() {
None => Err(BlameError::IsDirectory(path)),
Some(file_unode_id) => Ok(BlameId::from(file_unode_id)),
}
}
})
.and_then({
cloned!(ctx, blobstore);
move |blame_id| {
blame_id
.load(ctx.clone(), &blobstore)
.compat()
.then(move |result| match result {
Ok(BlameMaybeRejected::Blame(blame)) => Ok(Ok((blame_id, blame))),
Ok(BlameMaybeRejected::Rejected(reason)) => Err(reason.into()),
Err(LoadableError::Error(error)) => Err(error.into()),
Err(LoadableError::Missing(_)) => Ok(Err(blame_id)),
})
}
})
}