use std::ffi::{OsStr, OsString}; use std::fs; use std::path::Path; use std::sync::Arc; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::sync::Mutex; use super::{Database, Error}; pub struct FileTree { db: Arc>, new_version_notifiers: Vec>, next_version_source: u64, root_path: OsString, } impl FileTree { pub fn open(db: Arc>, directory: &OsStr) -> Result { // Check whether the database entry for this file tree exists and fetch the root // file ID. // TODO // Create a lock file to make sure that the directory is only synchronized once. // TODO // Clear up any temporary files which were left over. // TODO // Enqueue the root directory to be rechecked against the database. // TODO Ok(FileTree { db, new_version_notifiers: Vec::new(), next_version_source: 0, root_path: directory.to_owned(), }) } pub fn root_path(&self) -> &OsStr { &self.root_path } pub fn new_notifier(&mut self) -> NewVersionNotifier { let (send, receive) = unbounded_channel(); self.new_version_notifiers.push(send); NewVersionNotifier { receive } } pub fn new_version_source(&mut self) -> NewVersionSource { let source = NewVersionSource { id: self.next_version_source, }; self.next_version_source += 1; source } // TODO: Limit on size of all temporary files, make this function return a // future which yields a temporary file once enough space is available? pub fn create_temporary_file(&self, _target_path: &OsStr) -> Result { // TODO panic!("Not yet implemented."); } pub async fn lock_path

(&mut self, path: P) -> Option where P: AsRef, { let _path = path.as_ref(); // TODO panic!("Not yet implemented."); } pub async fn lock_two_paths(&mut self, path1: P1, path2: P2) -> Option where P1: AsRef, P2: AsRef, { let _path1 = path1.as_ref(); let _path2 = path2.as_ref(); // TODO panic!("Not yet implemented."); } } pub struct LockedPath { // TODO } impl LockedPath { pub fn get_file_info

(&self, path: P) -> Option where P: AsRef, { let _path = path.as_ref(); // TODO panic!("Not yet implemented."); } pub fn get_directory_listing(&self, _path: &OsStr) -> Option> { // TODO panic!("Not yet implemented."); } pub fn local_file_changed(&self, _source: NewVersionSource, _path: &OsStr, _info: FileInfo) { // TODO panic!("Not yet implemented."); } pub fn local_file_removed

(&self, _source: NewVersionSource, _path: P) where P: AsRef, { // TODO panic!("Not yet implemented."); } pub fn update_file( &self, _source: NewVersionSource, _path: &OsStr, _info: FileInfo, _contents: TemporaryFile, _origin: OriginInfo, ) -> Result { // TODO panic!("Not yet implemented."); } pub fn create_directory

( &self, _source: NewVersionSource, _path: P, _origin: OriginInfo, ) -> Result where P: AsRef, { // TODO panic!("Not yet implemented."); } } impl Drop for LockedPath { fn drop(&mut self) { // Release the lock again. // TODO } } pub struct NewVersionNotifier { // TODO: Probably should not be public. pub receive: UnboundedReceiver, } pub struct NewVersionEvent { pub source: NewVersionSource, // TODO } #[derive(Clone, Copy, PartialEq, Eq)] pub struct NewVersionSource { id: u64, } pub struct FileInfo { pub file_name: OsString, pub file_type: FileType, /// Local modification time in seconds since the Unix epoch. Only valid for files and symlinks, /// not for directories. pub local_modification_time: Option, /// Local size of the file in bytes. Only valid for files and symlinks, not for directories. pub local_size: Option, // TODO } #[derive(PartialEq)] pub enum FileType { File, Directory, Symlink, } impl FileType { pub fn from(ft: fs::FileType) -> Option { if ft.is_file() { Some(FileType::File) } else if ft.is_dir() { Some(FileType::Directory) } else if ft.is_symlink() { Some(FileType::Symlink) } else { None } } } pub struct TemporaryFile { // TODO } pub enum OriginInfo { Local, // TODO: For remote sources, include the previous version to detect conflicts? Remote, }