bernardo-tui 0.2.7

A keyboard-only, distraction-free TUI widget library
Documentation
use std::fmt::{Debug, Formatter};
use std::fs::Metadata;
use std::io;
use std::io::{Error, Write};
use std::path::{Path, PathBuf};

use log::{error, info, warn};
use streaming_iterator::StreamingIterator;

use crate::fs::dir_entry::DirEntry;
use crate::fs::filesystem_front::FilesystemFront;
use crate::fs::fsf_ref::FsfRef;
use crate::fs::read_error::{ListError, ReadError};
use crate::fs::write_error::WriteError;

pub struct RealFS {
    root_path: PathBuf,
}

impl RealFS {
    pub fn new(root_path: PathBuf) -> io::Result<RealFS> {
        let root_path = if root_path.is_absolute() {
            root_path
        } else {
            root_path.canonicalize()?
        };

        Ok(RealFS { root_path })
    }
}

impl Debug for RealFS {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Filesystem({})", self.root_path.to_string_lossy())
    }
}

impl FilesystemFront for RealFS {
    fn root_path(&self) -> &PathBuf {
        &self.root_path
    }

    fn blocking_read_entire_file(&self, path: &Path) -> Result<Vec<u8>, ReadError> {
        let full_path = self.root_path.join(path);
        std::fs::read(&full_path).map_err(|e| e.into())
    }

    fn is_dir(&self, path: &Path) -> bool {
        let full_path = self.root_path.join(path);
        full_path.is_dir()
    }

    fn is_file(&self, path: &Path) -> bool {
        let full_path = self.root_path.join(path);
        full_path.is_file()
    }

    fn hash_seed(&self) -> usize {
        1
    }

    fn blocking_list(&self, path: &Path) -> Result<Vec<DirEntry>, ListError> {
        let fullpath = self.root_path.join(path);
        let readdir = match std::fs::read_dir(&fullpath) {
            Ok(r) => r,
            Err(e) => {
                error!(target: "fsf", "failed to read_dir {:?} because {}", &fullpath, e);
                return Err(ListError::UnmappedError(e.to_string()));
            }
        };

        let mut items: Vec<DirEntry> = Vec::new();
        for item in readdir {
            match item {
                Ok(dir_entry) => match dir_entry.path().file_name() {
                    Some(file_name) => {
                        items.push(DirEntry::new(file_name));
                    }
                    None => {
                        warn!("received dir_entry {:?} that does not have file_name, ignoring.", dir_entry);
                    }
                },
                Err(e) => {
                    error!("failed read dir because {}", e);
                    return Err(e.into());
                }
            }
        }
        Ok(items)
    }

    fn metadata(&self, path: &Path) -> Result<Metadata, ReadError> {
        let fullpath: PathBuf = self.root_path.join(path);

        match std::fs::metadata(&fullpath) {
            Ok(meta) => Ok(meta),
            Err(e) => {
                info!(target: "fsf", "failed reading metadata for {:?}, because {}", fullpath, e);
                Err(ReadError::UnmappedError(e.to_string()))
            }
        }
    }

    fn file_size(&self, path: &Path) -> Result<u64, ReadError> {
        self.metadata(path).map(|meta| meta.len())
    }

    fn exists(&self, path: &Path) -> bool {
        path.exists()
    }

    fn blocking_overwrite_with_stream(
        &self,
        path: &Path,
        stream: &mut dyn StreamingIterator<Item = [u8]>,
        must_exist: bool,
    ) -> Result<usize, WriteError> {
        if must_exist && path.exists() {
            return Err(WriteError::FileNotFound);
        }

        let mut file = std::fs::File::create(path)?;

        let mut bytes_written: usize = 0;
        while let Some(bytes) = stream.next() {
            let num_bytes = match file.write(bytes) {
                Ok(num_bytes) => num_bytes,
                Err(e) => {
                    error!("failed to write to file {} because {}", path.to_string_lossy(), e);
                    return Err(WriteError::UnmappedError(e.to_string()));
                }
            };

            if num_bytes != bytes.len() {
                error!("unexpected number of bytes written");
                break;
            }
            bytes_written += num_bytes;
        }

        file.flush()?;
        Ok(bytes_written)
    }

    fn blocking_overwrite_with_bytes(&self, path: &Path, s: &[u8], must_exist: bool) -> Result<usize, WriteError> {
        if must_exist && path.exists() {
            return Err(WriteError::FileNotFound);
        }

        std::fs::write(path, s)?;
        Ok(s.len())
    }

    fn to_fsf(self) -> FsfRef {
        FsfRef::new(self)
    }
}