//! Copy backend interface.

use std::{any::Any, ffi::OsStr, fmt::Display, path::Path, time::SystemTime};

use crate::reporting::CopyError;

#[derive(Debug, Clone)]
pub struct PathMeta {
    pub size: u64,
    pub mtime: SystemTime,
    pub ctime: SystemTime,
}

/// Trait implemented by path visitors for tree walking.
///
/// All paths are relative to the walk root.
pub trait PathVisitor {
    /// Visit a directory.
    fn visit_dir(&mut self, path: &Path, meta: &PathMeta) -> Result<(), CopyError>;
    /// Visit a file.
    fn visit_file(&mut self, path: &Path, meta: &PathMeta) -> Result<(), CopyError>;
}

/// Trait for copy sources.
pub trait CopySource: Display + Any {
    /// Walk the copy source, yielding the paths to copy.
    fn walk_paths(&self, visitor: &mut dyn PathVisitor) -> Result<(), CopyError>;
}

/// Trait for copy destinations.
pub trait CopyDest: Display {
    /// Open the copy destination to write to its root folder.
    fn open<'a>(&'a self) -> Result<Box<dyn CopyDestFolder<'a> + 'a>, CopyError>;
}

/// Trait for a single folder in the copy destination.
///
/// Copying happens in a particular folder.
pub trait CopyDestFolder<'a> {
    /// Copy a file into the destination.
    fn add_file(&mut self, name: &OsStr, src: &dyn CopySource) -> Result<bool, CopyError>;
    /// Create a directory in the destination.
    fn add_directory(
        &mut self,
        name: &OsStr,
    ) -> Result<Box<dyn CopyDestFolder<'a> + 'a>, CopyError>;
    /// Finish this destination folder.
    fn finish(&mut self) -> Result<usize, CopyError>;
}
