use std::path::Path;
#[cfg(any(feature = "async-std", feature = "tokio"))]
use std::pin::Pin;
#[cfg(any(feature = "async-std", feature = "tokio"))]
use std::task::{Context as TaskContext, Poll};
use ssri::{Algorithm, Integrity};
#[cfg(any(feature = "async-std", feature = "tokio"))]
use crate::async_lib::AsyncRead;
use crate::content::read;
use crate::errors::{Error, Result};
use crate::index::{self, Metadata};
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub struct Reader {
reader: read::AsyncReader,
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
impl AsyncRead for Reader {
#[cfg(feature = "async-std")]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut TaskContext<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.reader).poll_read(cx, buf)
}
#[cfg(feature = "tokio")]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut TaskContext<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<tokio::io::Result<()>> {
Pin::new(&mut self.reader).poll_read(cx, buf)
}
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
impl Reader {
pub fn check(self) -> Result<Algorithm> {
self.reader.check()
}
pub async fn open<P, K>(cache: P, key: K) -> Result<Reader>
where
P: AsRef<Path>,
K: AsRef<str>,
{
async fn inner(cache: &Path, key: &str) -> Result<Reader> {
if let Some(entry) = index::find_async(cache, key).await? {
Reader::open_hash(cache, entry.integrity).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref()).await
}
pub async fn open_hash<P>(cache: P, sri: Integrity) -> Result<Reader>
where
P: AsRef<Path>,
{
Ok(Reader {
reader: read::open_async(cache.as_ref(), sri).await?,
})
}
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn read<P, K>(cache: P, key: K) -> Result<Vec<u8>>
where
P: AsRef<Path>,
K: AsRef<str>,
{
async fn inner(cache: &Path, key: &str) -> Result<Vec<u8>> {
if let Some(entry) = index::find_async(cache, key).await? {
read_hash(cache, &entry.integrity).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn read_hash<P>(cache: P, sri: &Integrity) -> Result<Vec<u8>>
where
P: AsRef<Path>,
{
read::read_async(cache.as_ref(), sri).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn copy<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
if let Some(entry) = index::find_async(cache, key).await? {
copy_hash(cache, &entry.integrity, to).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn copy_unchecked<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
if let Some(entry) = index::find_async(cache, key).await? {
copy_hash_unchecked(cache, &entry.integrity, to).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn copy_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::copy_async(cache.as_ref(), sri, to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn copy_hash_unchecked<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::copy_unchecked_async(cache.as_ref(), sri, to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn reflink<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find_async(cache, key).await? {
reflink_hash(cache, &entry.integrity, to).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn reflink_unchecked<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find_async(cache, key).await? {
reflink_hash_unchecked_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn reflink_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::reflink_async(cache.as_ref(), sri, to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn hard_link_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::hard_link_async(cache.as_ref(), sri, to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn hard_link<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
hard_link_hash(cache, &entry.integrity, to).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn metadata<P, K>(cache: P, key: K) -> Result<Option<Metadata>>
where
P: AsRef<Path>,
K: AsRef<str>,
{
index::find_async(cache.as_ref(), key.as_ref()).await
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
pub async fn exists<P: AsRef<Path>>(cache: P, sri: &Integrity) -> bool {
read::has_content_async(cache.as_ref(), sri).await.is_some()
}
pub struct SyncReader {
reader: read::Reader,
}
impl std::io::Read for SyncReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.reader.read(buf)
}
}
impl SyncReader {
pub fn check(self) -> Result<Algorithm> {
self.reader.check()
}
pub fn open<P, K>(cache: P, key: K) -> Result<SyncReader>
where
P: AsRef<Path>,
K: AsRef<str>,
{
fn inner(cache: &Path, key: &str) -> Result<SyncReader> {
if let Some(entry) = index::find(cache, key)? {
SyncReader::open_hash(cache, entry.integrity)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref())
}
pub fn open_hash<P>(cache: P, sri: Integrity) -> Result<SyncReader>
where
P: AsRef<Path>,
{
Ok(SyncReader {
reader: read::open(cache.as_ref(), sri)?,
})
}
}
pub fn read_sync<P, K>(cache: P, key: K) -> Result<Vec<u8>>
where
P: AsRef<Path>,
K: AsRef<str>,
{
fn inner(cache: &Path, key: &str) -> Result<Vec<u8>> {
if let Some(entry) = index::find(cache, key)? {
read_hash_sync(cache, &entry.integrity)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref())
}
pub fn read_hash_sync<P>(cache: P, sri: &Integrity) -> Result<Vec<u8>>
where
P: AsRef<Path>,
{
read::read(cache.as_ref(), sri)
}
pub fn copy_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
if let Some(entry) = index::find(cache, key)? {
copy_hash_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn copy_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
if let Some(entry) = index::find(cache, key)? {
copy_hash_unchecked_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn copy_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::copy(cache.as_ref(), sri, to.as_ref())
}
pub fn copy_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::copy_unchecked(cache.as_ref(), sri, to.as_ref())
}
pub fn reflink_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
reflink_hash_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn reflink_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::reflink(cache.as_ref(), sri, to.as_ref())
}
pub fn reflink_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::reflink_unchecked(cache.as_ref(), sri, to.as_ref())
}
pub fn reflink_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
reflink_hash_unchecked_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn hard_link_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
hard_link_hash_unchecked_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn hard_link_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
read::hard_link(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}
pub fn hard_link_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::hard_link(cache.as_ref(), sri, to.as_ref())
}
pub fn hard_link_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::hard_link_unchecked(cache.as_ref(), sri, to.as_ref())
}
pub fn metadata_sync<P, K>(cache: P, key: K) -> Result<Option<Metadata>>
where
P: AsRef<Path>,
K: AsRef<str>,
{
index::find(cache.as_ref(), key.as_ref())
}
pub fn exists_sync<P: AsRef<Path>>(cache: P, sri: &Integrity) -> bool {
read::has_content(cache.as_ref(), sri).is_some()
}
#[cfg(test)]
mod tests {
#[cfg(any(feature = "async-std", feature = "tokio"))]
use crate::async_lib::AsyncReadExt;
use std::fs;
#[cfg(feature = "async-std")]
use async_attributes::test as async_test;
#[cfg(feature = "tokio")]
use tokio::test as async_test;
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_open() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write(&dir, "my-key", b"hello world").await.unwrap();
let mut handle = crate::Reader::open(&dir, "my-key").await.unwrap();
let mut str = String::new();
handle.read_to_string(&mut str).await.unwrap();
handle.check().unwrap();
assert_eq!(str, String::from("hello world"));
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_open_hash() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
let mut handle = crate::Reader::open_hash(&dir, sri).await.unwrap();
let mut str = String::new();
handle.read_to_string(&mut str).await.unwrap();
handle.check().unwrap();
assert_eq!(str, String::from("hello world"));
}
#[test]
fn test_open_sync() {
use std::io::prelude::*;
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write_sync(&dir, "my-key", b"hello world").unwrap();
let mut handle = crate::SyncReader::open(&dir, "my-key").unwrap();
let mut str = String::new();
handle.read_to_string(&mut str).unwrap();
handle.check().unwrap();
assert_eq!(str, String::from("hello world"));
}
#[test]
fn test_open_hash_sync() {
use std::io::prelude::*;
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write_sync(&dir, "my-key", b"hello world").unwrap();
let mut handle = crate::SyncReader::open_hash(&dir, sri).unwrap();
let mut str = String::new();
handle.read_to_string(&mut str).unwrap();
handle.check().unwrap();
assert_eq!(str, String::from("hello world"));
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_read() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write(&dir, "my-key", b"hello world").await.unwrap();
let data = crate::read(&dir, "my-key").await.unwrap();
assert_eq!(data, b"hello world");
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_read_hash() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
let data = crate::read_hash(&dir, &sri).await.unwrap();
assert_eq!(data, b"hello world");
}
#[test]
fn test_read_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write_sync(&dir, "my-key", b"hello world").unwrap();
let data = crate::read_sync(&dir, "my-key").unwrap();
assert_eq!(data, b"hello world");
}
#[test]
fn test_read_hash_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write_sync(&dir, "my-key", b"hello world").unwrap();
let data = crate::read_hash_sync(&dir, &sri).unwrap();
assert_eq!(data, b"hello world");
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_copy() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path();
let dest = dir.join("data");
crate::write(&dir, "my-key", b"hello world").await.unwrap();
crate::copy(&dir, "my-key", &dest).await.unwrap();
let data = crate::async_lib::read(&dest).await.unwrap();
assert_eq!(data, b"hello world");
}
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[async_test]
async fn test_copy_hash() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path();
let dest = dir.join("data");
let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
crate::copy_hash(&dir, &sri, &dest).await.unwrap();
let data = crate::async_lib::read(&dest).await.unwrap();
assert_eq!(data, b"hello world");
}
#[test]
fn test_copy_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path();
let dest = dir.join("data");
crate::write_sync(dir, "my-key", b"hello world").unwrap();
crate::copy_sync(dir, "my-key", &dest).unwrap();
let data = fs::read(&dest).unwrap();
assert_eq!(data, b"hello world");
}
#[test]
fn test_copy_hash_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path();
let dest = dir.join("data");
let sri = crate::write_sync(dir, "my-key", b"hello world").unwrap();
crate::copy_hash_sync(dir, &sri, &dest).unwrap();
let data = fs::read(&dest).unwrap();
assert_eq!(data, b"hello world");
}
}