use crate::admin::domain::*; use async_trait::async_trait; use rear::admin::state::AdminRegistry; use serde_json::Value; use std::path::PathBuf; struct FileRepository { directory_path: PathBuf, } impl FileRepository { pub fn new(from_path: &str) -> Self { let proper_path = file_operations::get_proper_path(from_path); FileRepository { directory_path: proper_path, } } } #[async_trait] impl AdminRepository for FileRepository { type Key = String; fn key_from_string(&self, s: String) -> Option { Some(s) } async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { RepoInfo { name: "File Repository", lookup_key: "path", display_list: &["path"], fields: &[ "path", "size", "modified_time", "created_time", "is_directory", "is_file", "is_symlink", ], } .into() } // GET on item collection. async fn list(&self, model: &RepositoryContext) -> RepositoryList { if let Ok(file_data) = file_operations::directory_info(&self.directory_path) { let items: Vec = file_data .into_iter() .map(|fields| { RepositoryItem { fields, detail_url: None, // TODO: change the return type of directory_info into a struct, so we can access key. change_url: None, } }) .collect(); return RepositoryList::List { values: items }; } RepositoryList::Empty } // POST on item collection. async fn create( &mut self, model: &RepositoryContext, mut data: Value, ) -> Option { None } // GET single item. async fn get(&self, model: &RepositoryContext, id: &Self::Key) -> Option { None } // PATCH single item. async fn update( &mut self, model: &RepositoryContext, id: &Self::Key, data: Value, ) -> Option { None } // PUT single item. async fn replace( &mut self, model: &RepositoryContext, id: &Self::Key, data: Value, ) -> Option { None } // DELETE single item. async fn delete(&mut self, _: &RepositoryContext, id: &Self::Key) -> Option { None } } pub fn register(registry: &mut AdminRegistry, path: &str) { let app_key = registry.register_app("Files"); let repo = FileRepository::new(path); let model_config = AdminModelConfig { app_key: app_key, name: "Files".to_owned(), }; let model_result = registry.register_model(model_config, repo); match model_result { Err(err) => panic!("{}", err), _ => (), } } mod file_operations { use chrono::{DateTime, TimeZone, Utc}; use serde_json::{json, Value}; use std::env; use std::fs; use std::path::Path; use std::path::PathBuf; use std::time::SystemTime; fn system_time_to_iso_string(system_time: SystemTime) -> String { let datetime: DateTime = system_time.into(); datetime.to_rfc3339() } fn get_metadata_as_json(path: &Path) -> Result> { let metadata = fs::metadata(path)?; // File size let size = metadata.len(); // Modification and creation times (if available) let modified_time = metadata.modified().ok(); let created_time = metadata.created().ok(); // File type information let file_type = metadata.file_type(); let is_dir = file_type.is_dir(); let is_file = file_type.is_file(); let is_symlink = file_type.is_symlink(); // Format times as strings if they exist let modified_time_str = modified_time.map(|t| system_time_to_iso_string(t)); let created_time_str = created_time.map(|t| system_time_to_iso_string(t)); Ok(json!({ "path": path.display().to_string(), "size": size, "modified_time": modified_time_str, "created_time": created_time_str, "is_directory": is_dir, "is_file": is_file, "is_symlink": is_symlink })) } pub fn file_info(file_path: &PathBuf) -> Result> { let path = file_path.as_path(); get_metadata_as_json(path) } pub fn directory_info( directory_path: &PathBuf, ) -> Result, Box> { let entries = fs::read_dir(directory_path)?; let mut results = Vec::new(); for entry in entries { let entry = entry?; let path = entry.path(); if let Ok(metadata_json) = get_metadata_as_json(&path) { results.push(metadata_json); } } Ok(results) } pub fn get_proper_path(some_path: &str) -> PathBuf { let path = Path::new(some_path); if path.is_absolute() { return path.to_path_buf(); } else { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()); let template_path = PathBuf::from(manifest_dir).join(path); return template_path; } } }