char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
78f4748
main
1use std:: io; 2use std:: sync:: Arc ; 3 4use avril_caps::{ Capabilities , Category , Initial }; 5use avril_kv::{ BatchOp , Decision , StoredEntry }; 6 7use super :: registry:: Registry ; 8 9// The ops' shapes: one re-authorized store operation per call. 10trait Shorthand { 11fn get ( & self , db : u64 , keyspace : & str , key : & [ u8 ]) -> io:: Result < Option < StoredEntry >>; 12fn set ( & self , db : u64 , keyspace : & str , key : & [ u8 ], value : & [ u8 ]) -> io:: Result < String >; 13fn list_keyspaces ( & self , db : u64 ) -> io:: Result < Vec < String >>; 14fn atomic_commit ( & self , db : u64 , ops : Vec < BatchOp >) -> io:: Result <( bool , Option < String >)>; 15} 16 17impl Shorthand for Registry { 18fn get ( & self , db : u64 , keyspace : & str , key : & [ u8 ]) -> io:: Result < Option < StoredEntry >> { 19self . with_entry ( db, false , |store| store. read_tx ( |tx| tx. get ( keyspace, key))) 20} 21 22fn set ( & self , db : u64 , keyspace : & str , key : & [ u8 ], value : & [ u8 ]) -> io:: Result < String > { 23self . with_entry ( db, true , |store|{ 24 store. write_tx ( |tx| tx. put ( keyspace, key, value). map ( Decision :: Commit )) 25}) 26} 27 28fn list_keyspaces ( & self , db : u64 ) -> io:: Result < Vec < String >> { 29self . with_entry ( db, false , |store| store. read_tx ( |tx| tx. list_keyspaces ())) 30} 31 32fn atomic_commit ( & self , db : u64 , ops : Vec < BatchOp >) -> io:: Result <( bool , Option < String >)> { 33self . with_entry ( db, true , |store| store. atomic_commit ( & ops)) 34} 35} 36 37fn caps ( dir : & std:: path:: Path ) ->Arc < Capabilities > { 38Capabilities :: new ( Initial :: new ( false ), dir. to_owned ()) 39} 40 41fn open_shared ( registry : & Registry , caps : & Arc < Capabilities >, name : & str ) ->u64 { 42 registry. open ( caps, name, "shared" , true ). unwrap () 43} 44 45fn set_op ( keyspace : & str , key : & [ u8 ], value : & [ u8 ]) ->BatchOp { 46BatchOp :: Set { 47keyspace : keyspace. into (), 48key : key. to_vec (), 49value : value. to_vec (), 50} 51} 52 53fn get < ' a >( 54registry : & Registry , 55db : u64 , 56keyspace : &' a str , 57key : &' a [ u8 ], 58) ->Option <( Vec < u8 >, String )> { 59 registry. get ( db, keyspace, key). unwrap () 60} 61 62# [ test ] 63fn atomic_commit_spans_keyspaces () { 64let root = tempfile:: tempdir (). unwrap (); 65let registry =Registry :: new (); 66let db =open_shared ( & registry, & caps ( root. path ()), "kv.redb" ); 67let ( ok, stamp) = registry 68. atomic_commit ( 69 db, 70vec! [ set_op ( "a" , b"k1" , b"v1" ), set_op ( "b" , b"k2" , b"v2" )], 71) 72. unwrap (); 73assert! ( ok); 74let stamp = stamp. unwrap (); 75assert_eq! ( 76get ( & registry, db, "a" , b"k1" ). unwrap (), 77( b"v1" . to_vec (), stamp. clone ()) 78); 79assert_eq! ( 80get ( & registry, db, "b" , b"k2" ). unwrap (), 81( b"v2" . to_vec (), stamp) 82); 83} 84 85# [ test ] 86fn atomic_check_failure_writes_nothing () { 87let root = tempfile:: tempdir (). unwrap (); 88let registry =Registry :: new (); 89let db =open_shared ( & registry, & caps ( root. path ()), "kv.redb" ); 90let stamp = registry. set ( db, "a" , b"k" , b"v" ). unwrap (); 91let check =BatchOp :: Check { 92keyspace : "a" . into (), 93key : b"k" . to_vec (), 94expected : None , 95}; 96let ( ok, commit) = registry 97. atomic_commit ( 98 db, 99vec! [ check, set_op ( "a" , b"k" , b"v2" ), set_op ( "b" , b"k" , b"w" )], 100) 101. unwrap (); 102assert! ( !ok); 103assert_eq! ( commit, None ); 104assert_eq! ( 105get ( & registry, db, "a" , b"k" ). unwrap (), 106( b"v" . to_vec (), stamp) 107); 108assert_eq! ( get ( & registry, db, "b" , b"k" ), None ); 109} 110 111# [ test ] 112fn closed_and_unknown_handles () { 113let root = tempfile:: tempdir (). unwrap (); 114let registry =Registry :: new (); 115let db =open_shared ( & registry, & caps ( root. path ()), "kv.redb" ); 116 registry. close ( db). unwrap (); 117// Close is idempotent. 118 registry. close ( db). unwrap (); 119 registry. close ( 999_999 ). unwrap (); 120for errorin [ 121 registry. get ( db, "ks" , b"k" ). unwrap_err (), 122 registry. set ( db, "ks" , b"k" , b"v" ). map ( |_|()). unwrap_err (), 123 registry. list_keyspaces ( db). map ( |_|()). unwrap_err (), 124 registry 125. atomic_commit ( db, vec! [ set_op ( "ks" , b"k" , b"v" )]) 126. map ( |_|()) 127. unwrap_err (), 128] { 129assert_eq! ( error. kind (), std:: io:: ErrorKind :: NotConnected ); 130} 131assert_eq! ( 132 registry. get ( 999_999 , "ks" , b"k" ). unwrap_err (). kind (), 133 std:: io:: ErrorKind :: NotConnected 134); 135} 136 137# [ test ] 138fn close_releases_the_database_file () { 139let root = tempfile:: tempdir (). unwrap (); 140let registry =Registry :: new (); 141let db = registry 142. open ( & caps ( root. path ()), "kv.redb" , "exclusive" , true ) 143. unwrap (); 144 registry. set ( db, "ks" , b"k" , b"v" ). unwrap (); 145 registry. close ( db). unwrap (); 146// Reopening exclusively works only if close physically released the file. 147let db = registry 148. open ( & caps ( root. path ()), "kv.redb" , "exclusive" , false ) 149. unwrap (); 150 registry. set ( db, "ks" , b"k" , b"v2" ). unwrap (); 151assert_eq! ( get ( & registry, db, "ks" , b"k" ). unwrap (). 0 , b"v2" ); 152 registry. close ( db). unwrap (); 153} 154 155# [ test ] 156fn capability_denial () { 157let root = tempfile:: tempdir (). unwrap (); 158let locked =Capabilities :: new ( Initial :: new ( true ), root. path (). to_owned ()); 159let registry =Registry :: new (); 160let error = registry 161. open ( & locked, "kv.redb" , "shared" , true ) 162. unwrap_err (); 163assert_eq! ( error. kind (), std:: io:: ErrorKind :: PermissionDenied ); 164assert! ( !root. path (). join ( "kv.redb" ). exists ()); 165} 166 167# [ test ] 168fn pledge_narrowing_applies_to_open_handles () { 169let root = tempfile:: tempdir (). unwrap (); 170let live =caps ( root. path ()); 171let registry =Registry :: new (); 172let db =open_shared ( & registry, & live, "kv.redb" ); 173 registry. set ( db, "ks" , b"k" , b"v" ). unwrap (); 174assert! ( registry. get ( db, "ks" , b"k" ). unwrap (). is_some ()); 175 live. pledge ( vec! [ 176( Category :: Read , Some ( vec! []), vec! []), 177( Category :: Write , Some ( vec! []), vec! []), 178]) 179. unwrap (); 180assert_eq! ( 181 registry. get ( db, "ks" , b"k" ). unwrap_err (). kind (), 182 std:: io:: ErrorKind :: PermissionDenied 183); 184assert_eq! ( 185 registry. set ( db, "ks" , b"k" , b"v2" ). unwrap_err (). kind (), 186 std:: io:: ErrorKind :: PermissionDenied 187); 188assert_eq! ( 189 registry. list_keyspaces ( db). unwrap_err (). kind (), 190 std:: io:: ErrorKind :: PermissionDenied 191); 192assert_eq! ( 193 registry 194. atomic_commit ( db, vec! [ set_op ( "ks" , b"k" , b"v3" )]) 195. unwrap_err () 196. kind (), 197 std:: io:: ErrorKind :: PermissionDenied 198); 199} 200 201# [ test ] 202fn two_read_only_handles_coexist_on_the_same_database () { 203let root = tempfile:: tempdir (). unwrap (); 204let registry =Registry :: new (); 205let db =open_shared ( & registry, & caps ( root. path ()), "kv.redb" ); 206 registry. set ( db, "ks" , b"k" , b"v" ). unwrap (); 207 registry. close ( db). unwrap (); 208let first = registry 209. open ( & caps ( root. path ()), "kv.redb" , "read-only" , false ) 210. unwrap (); 211let second = registry 212. open ( & caps ( root. path ()), "kv.redb" , "read-only" , false ) 213. unwrap (); 214assert_eq! ( get ( & registry, first, "ks" , b"k" ). unwrap (). 0 , b"v" ); 215assert_eq! ( get ( & registry, second, "ks" , b"k" ). unwrap (). 0 , b"v" ); 216 registry. close ( first). unwrap (); 217 registry. close ( second). unwrap (); 218} 219 220# [ test ] 221fn read_only_handle_on_a_missing_file_does_not_block_exclusive_create () { 222let root = tempfile:: tempdir (). unwrap (); 223let registry =Registry :: new (); 224let read = registry 225. open ( & caps ( root. path ()), "kv.redb" , "read-only" , false ) 226. unwrap (); 227assert! ( !root. path (). join ( "kv.redb" ). exists ()); 228let write = registry 229. open ( & caps ( root. path ()), "kv.redb" , "exclusive" , true ) 230. unwrap (); 231 registry. set ( write, "ks" , b"k" , b"v" ). unwrap (); 232// The read-only handle snapshotted the missing file at open; it still 233// reads empty rather than observing the later creation. 234assert_eq! ( registry. get ( read, "ks" , b"k" ). unwrap (), None ); 235 registry. close ( read). unwrap (); 236 registry. close ( write). unwrap (); 237}