lakestream 0.0.3

Portable file-utility for object-stores
Documentation
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;

use futures::Future;

use crate::base::callback_wrapper::CallbackItem;
use crate::utils::formatters::{bytes_human_readable, time_human_readable};

type BoxedAsyncCallback = Box<
    dyn Fn(&[FileObject]) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>
        + Send
        + Sync
        + 'static,
>;

pub struct FileObjectVec {
    file_objects: Vec<FileObject>,
    callback: Option<BoxedAsyncCallback>,
}

impl FileObjectVec {
    pub fn new(callback: Option<BoxedAsyncCallback>) -> Self {
        Self {
            file_objects: Vec::new(),
            callback,
        }
    }
    pub fn into_inner(self) -> Vec<FileObject> {
        self.file_objects
    }

    pub async fn extend_async<T: IntoIterator<Item = FileObject>>(
        &mut self,
        iter: T,
    ) {
        let new_file_objects: Vec<FileObject> = iter.into_iter().collect();
        if let Some(callback) = &self.callback {
            let fut = (callback)(&new_file_objects);
            fut.await;
        }

        self.file_objects.extend(new_file_objects);
    }
}

impl Deref for FileObjectVec {
    type Target = Vec<FileObject>;

    fn deref(&self) -> &Self::Target {
        &self.file_objects
    }
}

impl DerefMut for FileObjectVec {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.file_objects
    }
}

#[derive(Debug, Clone)]
pub struct FileObject {
    name: String,
    size: u64,
    modified: Option<u64>,
    tags: Option<HashMap<String, String>>,
}

impl FileObject {
    pub fn new(
        name: String,
        size: u64,
        modified: Option<u64>,
        tags: Option<HashMap<String, String>>,
    ) -> Self {
        FileObject {
            name,
            size,
            modified,
            tags,
        }
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn size(&self) -> u64 {
        self.size
    }

    pub fn modified(&self) -> Option<u64> {
        self.modified
    }

    pub fn tags(&self) -> &Option<HashMap<String, String>> {
        &self.tags
    }

    pub fn println_path(&self) -> String {
        self.printable(true)
    }

    fn printable(&self, full_path: bool) -> String {
        let name_without_trailing_slash = self.name.trim_end_matches('/');

        let mut name_to_print = if full_path {
            name_without_trailing_slash.to_string()
        } else {
            name_without_trailing_slash
                .split('/')
                .last()
                .unwrap_or(name_without_trailing_slash)
                .to_string()
        };

        if self.name.ends_with('/') {
            name_to_print.push('/');
        }

        format!(
            "{:8} {} {}",
            bytes_human_readable(self.size()),
            if let Some(modified) = self.modified() {
                time_human_readable(modified)
            } else {
                "PRE".to_string()
            },
            name_to_print
        )
    }
}

impl CallbackItem for FileObject {
    fn println_path(&self) -> String {
        self.println_path()
    }
}