diff --git a/src/database/hierarchies.rs b/src/database/hierarchies.rs index 8819f07..d24d81b 100644 --- a/src/database/hierarchies.rs +++ b/src/database/hierarchies.rs @@ -15,7 +15,7 @@ use crate::database::constants::{ }; use crate::database::entry::{Entry, EntryValue}; use crate::database::lang::{EntryQuery, Query, QueryComponent, QueryPart}; -use crate::database::{bulk_retrieve_objects, insert_entry, query, DbPool}; +use crate::database::{insert_entry, query, DbPool}; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct UNode(String); @@ -37,14 +37,14 @@ impl UNode { } #[derive(Debug, Clone, PartialEq)] -pub struct UPath(pub Vec); +pub struct UHierPath(pub Vec); -impl std::str::FromStr for UPath { +impl std::str::FromStr for UHierPath { type Err = anyhow::Error; fn from_str(string: &str) -> Result { if string.is_empty() { - Ok(UPath(vec![])) + Ok(UHierPath(vec![])) } else { let result: Result> = string .trim_end_matches('/') @@ -52,7 +52,7 @@ impl std::str::FromStr for UPath { .map(|part| UNode::new(String::from(part))) .collect(); - Ok(UPath(result?)) + Ok(UHierPath(result?)) } } } @@ -63,7 +63,7 @@ impl std::fmt::Display for UNode { } } -impl std::fmt::Display for UPath { +impl std::fmt::Display for UHierPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -77,11 +77,11 @@ impl std::fmt::Display for UPath { } } -trait EntryList { +trait PointerEntries { fn extract_pointers(&self) -> Vec<(Address, Address)>; } -impl EntryList for Vec { +impl PointerEntries for Vec { fn extract_pointers(&self) -> Vec<(Address, Address)> { self.iter() .filter_map(|e| { @@ -95,7 +95,7 @@ impl EntryList for Vec { } } -pub fn list_orphans>(connection: &C) -> Result> { +pub fn list_roots>(connection: &C) -> Result> { let all_directories: Vec = query( connection, Query::SingleQuery(QueryPart::Matches(EntryQuery { @@ -125,34 +125,6 @@ pub fn list_orphans>(connection: &C) -> Result>( - connection: &C, - path: &UPath, -) -> Result> { - let resolved_path: Vec
= resolve_path(connection, path, false)?; - let entry_addresses = match resolved_path.last() { - Some(last) => query( - connection, - Query::SingleQuery(QueryPart::Matches(EntryQuery { - entity: QueryComponent::Exact(last.clone()), - attribute: QueryComponent::Exact(HIER_HAS_ATTR.to_string()), - value: QueryComponent::Any, - })), - )? - .extract_pointers() - .into_iter() - .map(|(addr, val)| [addr, val]) - .flatten() - .collect(), - None => list_orphans(connection)?, - }; - - Ok(bulk_retrieve_objects(connection, entry_addresses)? - .into_iter() - // .filter(|e| [DIR_KEY, FILENAME_KEY, FILE_IDENTITY_KEY].contains(&e.attribute.as_str())) - .collect::>()) -} - lazy_static! { static ref FETCH_CREATE_LOCK: Mutex<()> = Mutex::new(()); } @@ -199,7 +171,7 @@ pub fn fetch_or_create_dir>( .into_iter() .map(|(_, val)| val) .collect(), - None => list_orphans(connection)?, + None => list_roots(connection)?, }; let valid_directories: Vec
= matching_directories @@ -248,7 +220,7 @@ pub fn fetch_or_create_dir>( pub fn resolve_path>( connection: &C, - path: &UPath, + path: &UHierPath, create: bool, ) -> Result> { let mut result: Vec
= vec![]; @@ -272,7 +244,7 @@ pub type ResolveCache = LruCache<(Option
, UNode), Address>; pub fn resolve_path_cached>( connection: &C, - path: &UPath, + path: &UHierPath, create: bool, cache: &Arc>, ) -> Result> { @@ -326,7 +298,7 @@ mod tests { #[test] fn test_path_codec() { - let path = UPath(vec![ + let path = UHierPath(vec![ UNode("top".to_string()), UNode("foo".to_string()), UNode("bar".to_string()), @@ -336,7 +308,7 @@ mod tests { let str_path = path.to_string(); assert!(!str_path.is_empty()); - let decoded_path: Result = str_path.parse(); + let decoded_path: Result = str_path.parse(); assert!(decoded_path.is_ok()); assert_eq!(path, decoded_path.unwrap()); @@ -344,13 +316,13 @@ mod tests { #[test] fn test_path_validation() { - let valid_path: Result = "a/b/c/d/e/f/g".parse(); + let valid_path: Result = "a/b/c/d/e/f/g".parse(); assert!(valid_path.is_ok()); - let invalid_path: Result = "a/b/c//d/e/f/g".parse(); + let invalid_path: Result = "a/b/c//d/e/f/g".parse(); assert!(invalid_path.is_err()); - let invalid_path: Result = "a//b/c//d/e/f///g".parse(); + let invalid_path: Result = "a//b/c//d/e/f///g".parse(); assert!(invalid_path.is_err()); } @@ -386,7 +358,7 @@ mod tests { assert!(baz_result.is_ok()); let baz_result = baz_result.unwrap(); - let orphans = list_orphans(&open_result.pool.get().unwrap()); + let orphans = list_roots(&open_result.pool.get().unwrap()); assert!(orphans.is_ok()); assert_eq!(orphans.unwrap().len(), 2); diff --git a/src/database/mod.rs b/src/database/mod.rs index 6c01007..a49c1c5 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -9,7 +9,7 @@ pub mod hierarchies; pub mod inner; pub mod lang; -use crate::addressing::Address; +use crate::addressing::{Address, Addressable}; use crate::database::constants::{IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT}; use crate::database::entry::{Entry, EntryValue}; use crate::database::inner::models; @@ -118,43 +118,35 @@ pub fn retrieve_object>( ) -> Result> { use crate::database::inner::schema::data::dsl::*; - let matches = data + let primary = data .filter(entity.eq(object_address.encode()?)) .or_filter(value.eq(EntryValue::Address(object_address).to_string()?)) .load::(connection)?; - let entries = matches + + let entries = primary .iter() .map(Entry::try_from) - .filter_map(Result::ok) - .collect(); + .collect::>>()?; - Ok(entries) -} - -pub fn bulk_retrieve_objects>( - connection: &C, - object_addresses: Vec
, -) -> Result> { - use crate::database::inner::schema::data::dsl::*; - - let matches = data + let secondary = data .filter( entity.eq_any( - object_addresses + entries .iter() - .filter_map(|addr| addr.encode().ok()), + .map(|e| e.address()) + .filter_map(Result::ok) + .map(|addr| addr.encode()) + .collect::>>>()?, ), ) - // .or_filter(value.eq(EntryValue::Address(object_address).to_str()?)) .load::(connection)?; - let entries = matches + let secondary_entries = secondary .iter() .map(Entry::try_from) - .filter_map(Result::ok) - .collect(); + .collect::>>()?; - Ok(entries) + Ok([entries, secondary_entries].concat()) } pub fn remove_object>( diff --git a/src/filesystem.rs b/src/filesystem.rs index 2d08e63..ab9a614 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -9,7 +9,7 @@ use crate::database::constants::{ HIER_HAS_ATTR, IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_BASE_ATTR, TYPE_HAS_ATTR, }; use crate::database::entry::{Entry, EntryValue, InvariantEntry}; -use crate::database::hierarchies::{resolve_path_cached, ResolveCache, UNode, UPath}; +use crate::database::hierarchies::{resolve_path_cached, ResolveCache, UNode, UHierPath}; use crate::database::inner::models; use crate::database::{ file_set_valid, file_update_mtime, insert_entry, insert_file, retrieve_all_files, DbPool, @@ -350,7 +350,7 @@ fn _process_directory_entry>( let components = normalized_path.components().collect::>(); let (filename, dir_path) = components.split_last().unwrap(); - let upath = UPath( + let upath = UHierPath( iter::once(UNode::new("NATIVE").unwrap()) .chain(dir_path.iter().map(|component| { UNode::new(component.as_os_str().to_string_lossy().to_string()).unwrap() diff --git a/src/routes.rs b/src/routes.rs index 269aea6..5057693 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,6 +1,6 @@ use crate::addressing::{Address, Addressable}; use crate::database::entry::{Entry, InEntry}; -use crate::database::hierarchies::{list_path, UPath}; +use crate::database::hierarchies::{list_roots, resolve_path, UHierPath}; use crate::database::lang::Query; use crate::database::{ get_latest_files, insert_entry, query, remove_object, retrieve_file, retrieve_object, DbPool, @@ -84,34 +84,36 @@ pub async fn get_query( Ok(HttpResponse::Ok().json(&result)) } +trait EntriesAsHash { + fn as_hash(&self) -> Result>; +} + +impl EntriesAsHash for Vec { + fn as_hash(&self) -> Result> { + let mut result: HashMap = HashMap::new(); + for entry in self { + result.insert(encode(entry.address()?.encode()?), entry); + } + + Ok(result) + } +} + #[get("/api/obj/{address_str}")] pub async fn get_object( state: web::Data, address_str: web::Path, ) -> Result { let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; - let response: Result> = retrieve_object( + let result: Vec = retrieve_object( &connection, Address::decode(&decode(address_str.into_inner()).map_err(ErrorBadRequest)?) .map_err(ErrorBadRequest)?, - ); + ) + .map_err(ErrorInternalServerError)?; - debug!("{:?}", response); - - let mut result: HashMap = HashMap::new(); - for entry in response.map_err(error::ErrorInternalServerError)? { - result.insert( - encode( - entry - .address() - .map_err(ErrorInternalServerError)? - .encode() - .map_err(ErrorInternalServerError)?, - ), - entry, - ); - } - Ok(HttpResponse::Ok().json(result)) + debug!("{:?}", result); + Ok(HttpResponse::Ok().json(result.as_hash().map_err(ErrorInternalServerError)?)) } const MAX_SIZE: usize = 1_000_000; @@ -172,13 +174,35 @@ pub async fn list_hier( path: web::Path, ) -> Result { let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; - let upath: UPath = path.into_inner().parse().map_err(ErrorBadRequest)?; + let upath: UHierPath = path.into_inner().parse().map_err(ErrorBadRequest)?; trace!("Listing path \"{}\"", upath); - let entries: Vec = list_path(&connection, &upath) - .await - .map_err(ErrorNotFound)?; // todo: 500 if actual error occurs - Ok(HttpResponse::Ok().json(entries)) + let (addr, data) = if upath.0.is_empty() { + ( + None, + list_roots(&connection) + .map_err(ErrorInternalServerError)? + .into_iter() + .map(|root| { + retrieve_object(&connection, root) + }) + .collect::>>>().map_err(ErrorInternalServerError)? + .concat(), + ) + } else { + // todo: 500 if actual error occurs + let path = resolve_path(&connection, &upath, false).map_err(ErrorNotFound)?; + let last = path.last().unwrap().clone(); + ( + Some(last.clone()), + retrieve_object(&connection, last).map_err(ErrorInternalServerError)?, + ) + }; + + Ok(HttpResponse::Ok().json(json!({ + "addr": addr, + "data": data + }))) } #[post("/api/refresh")]