@@ -245,83 +245,74 @@ impl<'a> Iterator for MergedIterator<'a> {
245245impl DB {
246246 pub fn new ( root_dir : & str , memtable_threshold : usize , jstable_threshold : u64 ) -> Self {
247247 fs:: create_dir_all ( root_dir) . unwrap ( ) ;
248- let mut collections = HashMap :: new ( ) ;
249-
250- if let Ok ( entries) = fs:: read_dir ( root_dir) {
251- for entry in entries {
252- if let Ok ( entry) = entry {
253- if entry. path ( ) . is_dir ( ) {
254- let dir_path = entry. path ( ) ;
255-
256- // Try to find collection name from JSTable-0
257- let jstable_path = dir_path. join ( "jstable-0" ) ;
258- let col_name = if jstable_path. exists ( ) {
259- if let Ok ( iter) =
260- jstable:: JSTableIterator :: new ( jstable_path. to_str ( ) . unwrap ( ) )
261- {
262- Some ( iter. collection )
263- } else {
264- None
265- }
266- } else {
267- // Fallback to directory name (sanitized) if no jstable
268- entry. file_name ( ) . to_str ( ) . map ( |s| s. to_string ( ) )
269- } ;
270-
271- if let Some ( name) = col_name {
272- let collection = Collection :: new (
273- name. clone ( ) ,
274- dir_path,
275- memtable_threshold,
276- jstable_threshold,
277- ) ;
278- collections. insert ( name, collection) ;
279- }
280- }
281- }
282- }
283- }
284-
285248 DB {
286249 root_dir : PathBuf :: from ( root_dir) ,
287- collections,
250+ collections : HashMap :: new ( ) ,
288251 memtable_threshold,
289252 jstable_threshold,
290253 }
291254 }
292255
293- fn get_collection ( & mut self , name : & str ) -> & mut Collection {
294- self . collections . entry ( name. to_string ( ) ) . or_insert_with ( || {
295- let safe_name = sanitize_filename ( name) ;
296- let col_dir = self . root_dir . join ( safe_name) ;
297- Collection :: new (
298- name. to_string ( ) ,
299- col_dir,
300- self . memtable_threshold ,
301- self . jstable_threshold ,
302- )
303- } )
256+ fn get_collection_mut ( & mut self , name : & str ) -> Result < & mut Collection , String > {
257+ self . collections
258+ . get_mut ( name)
259+ . ok_or_else ( || format ! ( "Collection '{}' not found" , name) )
304260 }
305261
306- pub fn insert ( & mut self , collection : & str , doc : Value ) -> String {
307- self . get_collection ( collection) . insert ( doc)
262+ fn get_collection ( & self , name : & str ) -> Result < & Collection , String > {
263+ self . collections
264+ . get ( name)
265+ . ok_or_else ( || format ! ( "Collection '{}' not found" , name) )
308266 }
309267
310- pub fn delete ( & mut self , collection : & str , id : & str ) {
311- self . get_collection ( collection) . delete ( id) ;
312- }
313-
314- pub fn update ( & mut self , collection : & str , id : & str , doc : Value ) {
315- self . get_collection ( collection) . update ( id, doc) ;
268+ pub fn create_collection ( & mut self , name : & str ) -> Result < ( ) , String > {
269+ if self . collections . contains_key ( name) {
270+ return Err ( format ! ( "Collection '{}' already exists" , name) ) ;
271+ }
272+ let safe_name = sanitize_filename ( name) ;
273+ let col_dir = self . root_dir . join ( safe_name) ;
274+ let collection = Collection :: new (
275+ name. to_string ( ) ,
276+ col_dir,
277+ self . memtable_threshold ,
278+ self . jstable_threshold ,
279+ ) ;
280+ self . collections . insert ( name. to_string ( ) , collection) ;
281+ Ok ( ( ) )
316282 }
317283
318- pub fn scan ( & self , collection : & str ) -> Box < dyn Iterator < Item = ( String , Value ) > + ' _ > {
319- if let Some ( col ) = self . collections . get ( collection ) {
320- Box :: new ( col . scan ( ) )
284+ pub fn drop_collection ( & mut self , name : & str ) -> Result < ( ) , String > {
285+ if let Some ( collection ) = self . collections . remove ( name ) {
286+ fs :: remove_dir_all ( collection . dir ) . map_err ( |e| e . to_string ( ) )
321287 } else {
322- Box :: new ( std :: iter :: empty ( ) )
288+ Err ( format ! ( "Collection '{}' not found" , name ) )
323289 }
324290 }
291+
292+ pub fn show_collections ( & self ) -> Vec < String > {
293+ self . collections . keys ( ) . cloned ( ) . collect ( )
294+ }
295+
296+ pub fn insert ( & mut self , collection : & str , doc : Value ) -> Result < String , String > {
297+ self . get_collection_mut ( collection) . map ( |c| c. insert ( doc) )
298+ }
299+
300+ pub fn delete ( & mut self , collection : & str , id : & str ) -> Result < ( ) , String > {
301+ self . get_collection_mut ( collection) . map ( |c| c. delete ( id) )
302+ }
303+
304+ pub fn update ( & mut self , collection : & str , id : & str , doc : Value ) -> Result < ( ) , String > {
305+ self . get_collection_mut ( collection)
306+ . map ( |c| c. update ( id, doc) )
307+ }
308+
309+ pub fn scan (
310+ & self ,
311+ collection : & str ,
312+ ) -> Result < Box < dyn Iterator < Item = ( String , Value ) > + ' _ > , String > {
313+ self . get_collection ( collection)
314+ . map ( |c| Box :: new ( c. scan ( ) ) as Box < dyn Iterator < Item = ( String , Value ) > + ' _ > )
315+ }
325316}
326317
327318#[ cfg( test) ]
@@ -341,15 +332,16 @@ mod tests {
341332 MEMTABLE_THRESHOLD ,
342333 JSTABLE_THRESHOLD ,
343334 ) ;
335+ db. create_collection ( "test" ) . unwrap ( ) ;
344336
345337 for i in 0 ..MEMTABLE_THRESHOLD {
346- db. insert ( "test" , json ! ( { "a" : i } ) ) ;
338+ db. insert ( "test" , json ! ( { "a" : i } ) ) . unwrap ( ) ;
347339 }
348340 let col = db. collections . get ( "test" ) . unwrap ( ) ;
349341 assert_eq ! ( col. memtable. len( ) , MEMTABLE_THRESHOLD ) ;
350342 assert_eq ! ( col. jstable_count, 0 ) ;
351343
352- db. insert ( "test" , json ! ( { "a" : MEMTABLE_THRESHOLD } ) ) ;
344+ db. insert ( "test" , json ! ( { "a" : MEMTABLE_THRESHOLD } ) ) . unwrap ( ) ;
353345 let col = db. collections . get ( "test" ) . unwrap ( ) ;
354346 assert_eq ! ( col. memtable. len( ) , 1 ) ;
355347 assert_eq ! ( col. jstable_count, 1 ) ;
@@ -368,13 +360,14 @@ mod tests {
368360 MEMTABLE_THRESHOLD ,
369361 JSTABLE_THRESHOLD ,
370362 ) ;
363+ db. create_collection ( "test" ) . unwrap ( ) ;
371364 let doc1 = json ! ( { "a" : 1 } ) ;
372- let id1 = db. insert ( "test" , doc1. clone ( ) ) ;
365+ let id1 = db. insert ( "test" , doc1. clone ( ) ) . unwrap ( ) ;
373366
374367 let doc2 = json ! ( { "b" : "hello" } ) ;
375- db. update ( "test" , & id1, doc2. clone ( ) ) ;
368+ db. update ( "test" , & id1, doc2. clone ( ) ) . unwrap ( ) ;
376369
377- db. delete ( "test" , & id1) ;
370+ db. delete ( "test" , & id1) . unwrap ( ) ;
378371
379372 let col = db. collections . get ( "test" ) . unwrap ( ) ;
380373 let log_path = col. dir . join ( "argus.log" ) ;
@@ -414,20 +407,22 @@ mod tests {
414407 MEMTABLE_THRESHOLD ,
415408 JSTABLE_THRESHOLD ,
416409 ) ;
410+ db. create_collection ( "test" ) . unwrap ( ) ;
417411 let doc1 = json ! ( { "a" : 1 } ) ;
418- let id1 = db. insert ( "test" , doc1. clone ( ) ) ;
412+ let id1 = db. insert ( "test" , doc1. clone ( ) ) . unwrap ( ) ;
419413
420414 let doc2 = json ! ( { "b" : "hello" } ) ;
421- let id2 = db. insert ( "test" , doc2. clone ( ) ) ;
415+ let id2 = db. insert ( "test" , doc2. clone ( ) ) . unwrap ( ) ;
422416
423- db. delete ( "test" , & id1) ;
417+ db. delete ( "test" , & id1) . unwrap ( ) ;
424418
425419 // Recover by creating new DB instance pointed to same dir
426- let db2 = DB :: new (
420+ let mut db2 = DB :: new (
427421 dir. path ( ) . to_str ( ) . unwrap ( ) ,
428422 MEMTABLE_THRESHOLD ,
429423 JSTABLE_THRESHOLD ,
430424 ) ;
425+ db2. create_collection ( "test" ) . unwrap ( ) ;
431426 // "test" should be loaded if it persisted JSTable or fallback to dir name
432427 let col = db2. collections . get ( "test" ) . unwrap ( ) ;
433428
@@ -444,14 +439,15 @@ mod tests {
444439 MEMTABLE_THRESHOLD ,
445440 JSTABLE_THRESHOLD ,
446441 ) ;
442+ db. create_collection ( "test" ) . unwrap ( ) ;
447443
448444 for i in 0 ..( MEMTABLE_THRESHOLD * JSTABLE_THRESHOLD as usize ) {
449- db. insert ( "test" , json ! ( { "a" : i } ) ) ;
445+ db. insert ( "test" , json ! ( { "a" : i } ) ) . unwrap ( ) ;
450446 }
451447
452448 let col = db. collections . get ( "test" ) . unwrap ( ) ;
453449 assert_eq ! ( col. jstable_count, JSTABLE_THRESHOLD - 1 ) ;
454- db. insert ( "test" , json ! ( { "a" : 999 } ) ) ;
450+ db. insert ( "test" , json ! ( { "a" : 999 } ) ) . unwrap ( ) ;
455451
456452 let col = db. collections . get ( "test" ) . unwrap ( ) ;
457453 assert_eq ! ( col. jstable_count, 1 ) ;
@@ -465,32 +461,34 @@ mod tests {
465461 MEMTABLE_THRESHOLD ,
466462 JSTABLE_THRESHOLD ,
467463 ) ;
464+ db. create_collection ( "test" ) . unwrap ( ) ;
468465
469- let id_to_delete = db. insert ( "test" , json ! ( { "a" : 100 } ) ) ;
466+ let id_to_delete = db. insert ( "test" , json ! ( { "a" : 100 } ) ) . unwrap ( ) ;
470467
471468 for i in 0 ..9 {
472- db. insert ( "test" , json ! ( { "fill" : i } ) ) ;
469+ db. insert ( "test" , json ! ( { "fill" : i } ) ) . unwrap ( ) ;
473470 }
474- db. insert ( "test" , json ! ( { "trigger_1" : 1 } ) ) ;
471+ db. insert ( "test" , json ! ( { "trigger_1" : 1 } ) ) . unwrap ( ) ;
475472
476473 let col = db. collections . get ( "test" ) . unwrap ( ) ;
477474 assert_eq ! ( col. jstable_count, 1 ) ;
478475
479- db. delete ( "test" , & id_to_delete) ;
476+ db. delete ( "test" , & id_to_delete) . unwrap ( ) ;
480477
481478 for i in 0 ..8 {
482- db. insert ( "test" , json ! ( { "fill_2" : i } ) ) ;
479+ db. insert ( "test" , json ! ( { "fill_2" : i } ) ) . unwrap ( ) ;
483480 }
484- db. insert ( "test" , json ! ( { "trigger_2" : 1 } ) ) ;
481+ db. insert ( "test" , json ! ( { "trigger_2" : 1 } ) ) . unwrap ( ) ;
485482
486483 let col = db. collections . get ( "test" ) . unwrap ( ) ;
487484 assert_eq ! ( col. jstable_count, 2 ) ;
488485
489486 for t in 0 ..3 {
490487 for i in 0 ..9 {
491- db. insert ( "test" , json ! ( { "fill_more" : t, "i" : i } ) ) ;
488+ db. insert ( "test" , json ! ( { "fill_more" : t, "i" : i } ) )
489+ . unwrap ( ) ;
492490 }
493- db. insert ( "test" , json ! ( { "trigger_more" : t } ) ) ;
491+ db. insert ( "test" , json ! ( { "trigger_more" : t } ) ) . unwrap ( ) ;
494492 }
495493
496494 let col = db. collections . get ( "test" ) . unwrap ( ) ;
@@ -510,13 +508,14 @@ mod tests {
510508 MEMTABLE_THRESHOLD ,
511509 JSTABLE_THRESHOLD ,
512510 ) ;
511+ db. create_collection ( "test" ) . unwrap ( ) ;
513512
514513 for i in 0 ..MEMTABLE_THRESHOLD {
515- db. insert ( "test" , json ! ( { "val" : i} ) ) ;
514+ db. insert ( "test" , json ! ( { "val" : i} ) ) . unwrap ( ) ;
516515 }
517- db. insert ( "test" , json ! ( { "val" : 10 } ) ) ;
516+ db. insert ( "test" , json ! ( { "val" : 10 } ) ) . unwrap ( ) ;
518517
519- let results: HashMap < String , Value > = db. scan ( "test" ) . collect ( ) ;
518+ let results: HashMap < String , Value > = db. scan ( "test" ) . unwrap ( ) . collect ( ) ;
520519 assert_eq ! ( results. len( ) , 11 ) ;
521520 }
522521
@@ -526,4 +525,83 @@ mod tests {
526525 assert_eq ! ( sanitize_filename( "foo/bar" ) , "foo_2fbar" ) ;
527526 assert_eq ! ( sanitize_filename( "test.1" ) , "test_2e1" ) ;
528527 }
528+
529+ #[ test]
530+ fn test_create_collection ( ) {
531+ let dir = tempdir ( ) . unwrap ( ) ;
532+ let mut db = DB :: new (
533+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
534+ MEMTABLE_THRESHOLD ,
535+ JSTABLE_THRESHOLD ,
536+ ) ;
537+ db. create_collection ( "test" ) . unwrap ( ) ;
538+ assert ! ( db. collections. contains_key( "test" ) ) ;
539+ }
540+
541+ #[ test]
542+ fn test_create_collection_already_exists ( ) {
543+ let dir = tempdir ( ) . unwrap ( ) ;
544+ let mut db = DB :: new (
545+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
546+ MEMTABLE_THRESHOLD ,
547+ JSTABLE_THRESHOLD ,
548+ ) ;
549+ db. create_collection ( "test" ) . unwrap ( ) ;
550+ let res = db. create_collection ( "test" ) ;
551+ assert ! ( res. is_err( ) ) ;
552+ }
553+
554+ #[ test]
555+ fn test_drop_collection ( ) {
556+ let dir = tempdir ( ) . unwrap ( ) ;
557+ let mut db = DB :: new (
558+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
559+ MEMTABLE_THRESHOLD ,
560+ JSTABLE_THRESHOLD ,
561+ ) ;
562+ db. create_collection ( "test" ) . unwrap ( ) ;
563+ assert ! ( db. collections. contains_key( "test" ) ) ;
564+ db. drop_collection ( "test" ) . unwrap ( ) ;
565+ assert ! ( !db. collections. contains_key( "test" ) ) ;
566+ }
567+
568+ #[ test]
569+ fn test_drop_collection_not_found ( ) {
570+ let dir = tempdir ( ) . unwrap ( ) ;
571+ let mut db = DB :: new (
572+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
573+ MEMTABLE_THRESHOLD ,
574+ JSTABLE_THRESHOLD ,
575+ ) ;
576+ let res = db. drop_collection ( "test" ) ;
577+ assert ! ( res. is_err( ) ) ;
578+ }
579+
580+ #[ test]
581+ fn test_show_collections ( ) {
582+ let dir = tempdir ( ) . unwrap ( ) ;
583+ let mut db = DB :: new (
584+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
585+ MEMTABLE_THRESHOLD ,
586+ JSTABLE_THRESHOLD ,
587+ ) ;
588+ db. create_collection ( "test1" ) . unwrap ( ) ;
589+ db. create_collection ( "test2" ) . unwrap ( ) ;
590+ let collections = db. show_collections ( ) ;
591+ assert_eq ! ( collections. len( ) , 2 ) ;
592+ assert ! ( collections. contains( & "test1" . to_string( ) ) ) ;
593+ assert ! ( collections. contains( & "test2" . to_string( ) ) ) ;
594+ }
595+
596+ #[ test]
597+ fn test_insert_into_non_existent_collection ( ) {
598+ let dir = tempdir ( ) . unwrap ( ) ;
599+ let mut db = DB :: new (
600+ dir. path ( ) . to_str ( ) . unwrap ( ) ,
601+ MEMTABLE_THRESHOLD ,
602+ JSTABLE_THRESHOLD ,
603+ ) ;
604+ let res = db. insert ( "test" , json ! ( { "a" : 1 } ) ) ;
605+ assert ! ( res. is_err( ) ) ;
606+ }
529607}
0 commit comments