sapling/eden/mononoke/mononoke_api/src/file.rs
David Tolnay e988a88be9 rust: Rename futures_preview:: to futures::
Summary:
Context: https://fb.workplace.com/groups/rust.language/permalink/3338940432821215/

This codemod replaces *all* dependencies on `//common/rust/renamed:futures-preview` with `fbsource//third-party/rust:futures-preview` and their uses in Rust code from `futures_preview::` to `futures::`.

This does not introduce any collisions with `futures::` meaning 0.1 futures because D20168958 previously renamed all of those to `futures_old::` in crates that depend on *both* 0.1 and 0.3 futures.

Codemod performed by:

```
rg \
    --files-with-matches \
    --type-add buck:TARGETS \
    --type buck \
    --glob '!/experimental' \
    --regexp '(_|\b)rust(_|\b)' \
| sed 's,TARGETS$,:,' \
| xargs \
    -x \
    buck query "labels(srcs, rdeps(%Ss, //common/rust/renamed:futures-preview, 1))" \
| xargs sed -i 's,\bfutures_preview::,futures::,'

rg \
    --files-with-matches \
    --type-add buck:TARGETS \
    --type buck \
    --glob '!/experimental' \
    --regexp '(_|\b)rust(_|\b)' \
| xargs sed -i 's,//common/rust/renamed:futures-preview,fbsource//third-party/rust:futures-preview,'
```

Reviewed By: k21

Differential Revision: D20213432

fbshipit-source-id: 07ee643d350c5817cda1f43684d55084f8ac68a6
2020-03-03 11:01:20 -08:00

204 lines
6.4 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.
*/
use std::convert::TryInto;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use anyhow::format_err;
use bytes::{Bytes, BytesMut};
use cloned::cloned;
use context::CoreContext;
use filestore::{self, get_metadata, FetchKey};
use futures::compat::{Future01CompatExt, Stream01CompatExt};
use futures::future::{FutureExt, Shared};
use futures::stream::TryStreamExt;
use crate::errors::MononokeError;
use crate::repo::RepoContext;
/// A file's ID is its content id.
pub use mononoke_types::ContentId as FileId;
/// The type of a file.
pub use mononoke_types::FileType;
/// Metadata about a file.
pub use mononoke_types::ContentMetadata as FileMetadata;
#[derive(Clone)]
pub struct FileContext {
repo: RepoContext,
fetch_key: FetchKey,
metadata: Shared<Pin<Box<dyn Future<Output = Result<FileMetadata, MononokeError>> + Send>>>,
}
impl fmt::Debug for FileContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"FileContext(repo={:?} fetch_key={:?})",
self.repo().name(),
self.fetch_key
)
}
}
/// Context for accessing a file in a repository.
///
/// Files are content-addressed, so if the same file occurs in multiple
/// places in the repository, this context represents all of them. As such,
/// it's not possible to go back to the commit or path from a `FileContext`.
///
/// See `ChangesetPathContext` if you need to refer to a specific file in a
/// specific commit.
impl FileContext {
/// Create a new FileContext. The file must exist in the repository.
///
/// To construct a `FileContext` for a file that might not exist, use
/// `new_check_exists`.
pub(crate) fn new(repo: RepoContext, fetch_key: FetchKey) -> Self {
let metadata = {
cloned!(repo, fetch_key);
async move {
get_metadata(repo.blob_repo().blobstore(), repo.ctx().clone(), &fetch_key)
.compat()
.await
.map_err(MononokeError::from)
.and_then(|metadata| {
metadata.ok_or_else(|| content_not_found_error(&fetch_key))
})
}
};
let metadata = metadata.boxed().shared();
Self {
repo,
fetch_key,
metadata,
}
}
/// Create a new FileContext using an ID that might not exist. Returns
/// `None` if the file doesn't exist.
pub(crate) async fn new_check_exists(
repo: RepoContext,
fetch_key: FetchKey,
) -> Result<Option<Self>, MononokeError> {
// Try to get the file metadata immediately to see if it exists.
let file = get_metadata(repo.blob_repo().blobstore(), repo.ctx().clone(), &fetch_key)
.compat()
.await?
.map(|metadata| {
let metadata = async move { Ok(metadata) };
let metadata = metadata.boxed().shared();
Self {
repo,
fetch_key,
metadata,
}
});
Ok(file)
}
/// The context for this query.
pub(crate) fn ctx(&self) -> &CoreContext {
&self.repo.ctx()
}
/// The `RepoContext` for this query.
pub(crate) fn repo(&self) -> &RepoContext {
&self.repo
}
/// Return the ID of the file.
pub async fn id(&self) -> Result<FileId, MononokeError> {
let meta = self.metadata().await?;
Ok(meta.content_id)
}
/// Return the metadata for a file.
pub async fn metadata(&self) -> Result<FileMetadata, MononokeError> {
self.metadata.clone().await
}
/// Return the content for the file.
///
/// This method buffers the full file content in memory, which may
/// be expensive in the case of large files.
pub async fn content_concat(&self) -> Result<Bytes, MononokeError> {
let bytes = filestore::fetch_concat_opt(
self.repo().blob_repo().blobstore(),
self.ctx().clone(),
&self.fetch_key,
)
.compat()
.await;
match bytes {
Ok(Some(bytes)) => Ok(bytes),
Ok(None) => Err(content_not_found_error(&self.fetch_key)),
Err(e) => Err(MononokeError::from(e)),
}
}
/// Return the content for a range within the file.
///
/// If the range goes past the end of the file, then content up to
/// the end of the file is returned. If the range starts past the
/// end of the file, then an empty buffer is returned.
pub async fn content_range_concat(
&self,
start: u64,
size: u64,
) -> Result<Bytes, MononokeError> {
let ret = filestore::fetch_range_with_size(
self.repo().blob_repo().blobstore(),
self.ctx().clone(),
&self.fetch_key,
start,
size,
)
.compat()
.await;
match ret {
Ok(Some((stream, size))) => {
let size = size.try_into().map_err(|_| {
MononokeError::from(format_err!("content too large: {:?}", self.fetch_key))
})?;
let bytes = stream
.compat()
.map_err(MononokeError::from)
.try_fold(
BytesMut::with_capacity(size),
|mut buff, chunk| async move {
buff.extend_from_slice(&chunk);
Ok(buff)
},
)
.await?
.freeze();
Ok(bytes)
}
Ok(None) => Err(content_not_found_error(&self.fetch_key)),
Err(e) => Err(MononokeError::from(e)),
}
}
}
/// File contexts should only exist for files that are known to be in the
/// blobstore. If attempting to access the content results in an error, this
/// error is returned. This is an internal error, as it means either the data
/// has been lost from the blobstore, or the file context was erroneously
/// constructed.
fn content_not_found_error(fetch_key: &FetchKey) -> MononokeError {
MononokeError::from(format_err!("content not found: {:?}", fetch_key))
}