quilt-rs 0.5.6

Rust library for accessing Quilt data packages.
Documentation
use std::path::Path;

use tokio::{
    fs,
    io::{AsyncRead, AsyncWriteExt},
};

pub type File = Box<dyn AsyncRead + Unpin + Send>;

pub async fn open(path: impl AsRef<Path>) -> Result<File, String> {
    // real impl
    fs::File::open(path)
        .await
        // .map(|file| Box::new(file) as Box<dyn io::AsyncRead + Unpin>)
        .map_err(|err| err.to_string())
        .map(|file| Box::new(file) as File)

    // TODO: fake impl
}

pub async fn exists(path: impl AsRef<Path>) -> bool {
    // real impl
    fs::metadata(path).await.is_ok()
    // TODO: fake impl
}

pub async fn write(path: impl AsRef<Path>, bytes: &[u8]) -> Result<(), String> {
    let parent = path.as_ref().parent().ok_or("no parent".to_string())?;
    fs::create_dir_all(&parent)
        .await
        .map_err(|err| err.to_string())?;

    // TODO: Write to a temporary location, then move.
    let mut file = fs::File::create(&path)
        .await
        .map_err(|err| err.to_string())?;

    file.write_all(&bytes)
        .await
        .map_err(|err| err.to_string())?;

    Ok(())
}

pub async fn read_to_string(path: impl AsRef<Path>) -> std::io::Result<String> {
    fs::read_to_string(path).await
}

// XXX: scope?
// fn child(&self, path: &str) -> Self;

// #[derive(Clone)]
// pub struct MemoryFile {
//     contents: String,
// }
//
// impl AsyncRead for MemoryFile {
//     fn poll_read(
//         self: std::pin::Pin<&mut Self>,
//         _cx: &mut std::task::Context<'_>,
//         _buf: &mut ReadBuf<'_>,
//     ) -> std::task::Poll<std::io::Result<()>> {
//         // TODO: put the data into the buffer
//         std::task::Poll::Ready(Ok(()))
//     }
// }
//
// // XXX: use object_store? it has InMemory impl
// pub type MemoryFS = HashMap<String, String>;
//
// pub trait MemoryFSUtil {
//     fn from_strs<'a>(strs: impl IntoIterator<Item = (&'a str, &'a str)>) -> Self;
// }
//
// impl MemoryFSUtil for MemoryFS {
//     fn from_strs<'a>(strs: impl IntoIterator<Item = (&'a str, &'a str)>) -> Self {
//         strs.into_iter()
//             .map(|(k, v)| (k.to_string(), v.to_string()))
//             .collect()
//     }
// }

// fn get(&self, path: &str) -> Option<String> {
//     let key = self.path.join(path).into_os_string().into_string().unwrap();
//     self.root_fs.get(&key).cloned()
// }

// async fn open(&self, path: &str) -> Result<LocalFile, String> {
//     match self.get(path) {
//         Some(contents) => Ok(Box::new(MemoryFile { contents: contents.clone() })),
//         None => Err(format!("file not found: {}", path)),
//     }
// }
//
// async fn exists(&self, path: &str) -> bool {
//     self.get(path).is_some()
// }

pub async fn get_file_modified_ts(path: &Path) -> Result<chrono::DateTime<chrono::Utc>, String> {
    let modified = tokio::fs::metadata(path)
        .await
        .map(|m| m.modified())
        .map_err(|err| err.to_string())?
        .map_err(|err| err.to_string())?;
    Ok(chrono::DateTime::<chrono::Utc>::from(modified))
}