char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7325e8c
main
1mod registry; 2 3use std:: io; 4use std:: sync:: Arc ; 5 6use avril_caps:: Capabilities ; 7use avril_core:: serde_v8::{ JsBuffer , ToJsBuffer }; 8use avril_core::{ Extension , Op , PublicModule }; 9use avril_kv::{ Decision , Store }; 10use tokio:: runtime:: Handle ; 11 12pub use avril_kv::{ MAX_BATCH_BYTES , MAX_BATCH_OPS }; 13pub use registry:: Registry ; 14 15struct Shared { 16handle : Handle , 17caps : Arc < Capabilities >, 18registry : Registry , 19} 20 21// redb work runs off the isolate thread. 22fn blocking < T , W >( shared : & Arc < Shared >, work : W ) ->impl Future < Output = io:: Result < T >> +use <T , W > 23where 24T : Send +' static , 25W : FnOnce ( & Shared ) -> io:: Result < T > +Send +' static , 26{ 27let task ={ 28let shared = shared. clone (); 29 shared. handle . clone (). spawn_blocking ( move ||work ( & shared)) 30}; 31async move { 32 task. await 33. unwrap_or_else ( |_|Err ( io:: Error :: other ( "kv background task stopped" ))) 34} 35} 36 37// An op on an open database: `function` splits its arguments into the 38// database id and the work to run against the re-authorized store. 39fn kv_op < A , R , W >( 40shared : & Arc < Shared >, 41name : &' static str , 42write : bool , 43function : impl Fn ( A ) ->( u64 , W ) +' static , 44) ->Op 45where 46A : serde:: de:: DeserializeOwned , 47R : serde:: Serialize +Send +' static , 48W : FnOnce ( & Store ) -> io:: Result < R > +Send +' static , 49{ 50let shared = shared. clone (); 51Op :: asynchronous ( name, move |args : A |{ 52let ( db, work) =function ( args); 53blocking ( & shared, move |shared|{ 54 shared. registry . with_entry ( db, write, work) 55}) 56}) 57} 58 59# [ derive ( serde :: Deserialize )] 60struct ListArgs { 61db : u64 , 62keyspace : String , 63start : Option < JsBuffer >, 64end : Option < JsBuffer >, 65prefix : Option < JsBuffer >, 66limit : usize , 67reverse : bool , 68} 69 70// Internal tagging buffers each op before picking a variant, which strips the 71// typed-array identity that JsBuffer needs; serde_bytes accepts plain bytes. 72# [ derive ( serde :: Deserialize )] 73# [ serde ( tag = "t" , rename_all = "snake_case" )] 74enum AtomicOp { 75Check { 76ks : String , 77# [ serde ( with = "serde_bytes" )] 78key : Vec < u8 >, 79expected : Option < String >, 80}, 81Set { 82ks : String , 83# [ serde ( with = "serde_bytes" )] 84key : Vec < u8 >, 85# [ serde ( with = "serde_bytes" )] 86value : Vec < u8 >, 87}, 88Delete { 89ks : String , 90# [ serde ( with = "serde_bytes" )] 91key : Vec < u8 >, 92}, 93} 94 95impl From < AtomicOp > for avril_kv:: BatchOp { 96fn from ( op : AtomicOp ) ->Self { 97match op{ 98AtomicOp :: Check { ks, key, expected} =>Self :: Check { keyspace : ks, key, expected}, 99AtomicOp :: Set { ks, key, value} =>Self :: Set { keyspace : ks, key, value}, 100AtomicOp :: Delete { ks, key} =>Self :: Delete { keyspace : ks, key}, 101} 102} 103} 104 105pub fn init ( handle : Handle , caps : Arc < Capabilities >) ->Extension { 106let shared =Arc :: new ( Shared { 107 handle, 108 caps, 109registry : Registry :: new (), 110}); 111let open = shared. clone (); 112let close = shared. clone (); 113Extension { 114name : "ext:kv" , 115dependencies : & [ "ext:file" , "ext:url" , "ext:web" , "ext:encoding" ], 116ops : vec! [ 117Op :: asynchronous ( 118"op_kv_open" , 119move |( path, access, create): ( String , String , bool ) |{ 120blocking ( & open, move |shared|{ 121 shared. registry . open ( & shared. caps , & path, & access, create) 122}) 123}, 124), 125Op :: asynchronous ( "op_kv_close" , move |id : u64 |{ 126blocking ( & close, move |shared| shared. registry . close ( id)) 127}), 128kv_op ( & shared, "op_kv_list_keyspaces" , false , |db : u64 |{ 129( db, |store : & Store | store. read_tx ( |tx| tx. list_keyspaces ())) 130}), 131kv_op ( 132& shared, 133"op_kv_get_many" , 134false , 135 |( db, keyspace, keys): ( u64 , String , Vec < JsBuffer >) |{ 136( db, move |store : & Store |{ 137 store. read_tx ( |tx|{ 138 keys. iter () 139. map ( |key|{ 140let entry = tx. get ( & keyspace, key) ?; 141Ok ( entry. map ( |( value, stamp) |( ToJsBuffer :: from ( value), stamp))) 142}) 143. collect ::< io:: Result < Vec < _ >>>() 144}) 145}) 146}, 147), 148kv_op ( 149& shared, 150"op_kv_set" , 151true , 152 |( db, keyspace, key, value): ( u64 , String , JsBuffer , JsBuffer ) |{ 153( db, move |store : & Store |{ 154 store. write_tx ( |tx| tx. put ( & keyspace, & key, & value). map ( Decision :: Commit )) 155}) 156}, 157), 158kv_op ( 159& shared, 160"op_kv_delete" , 161true , 162 |( db, keyspace, key): ( u64 , String , JsBuffer ) |{ 163( db, move |store : & Store |{ 164 store. write_tx ( |tx|{ 165 tx. delete ( & keyspace, & key) ?; 166Ok ( Decision :: Commit (())) 167}) 168}) 169}, 170), 171kv_op ( 172& shared, 173"op_kv_clear" , 174true , 175 |( db, keyspace): ( u64 , String ) |{ 176( db, move |store : & Store |{ 177 store. write_tx ( |tx| tx. clear ( & keyspace). map ( Decision :: Commit )) 178}) 179}, 180), 181kv_op ( & shared, "op_kv_list" , false , |args : ListArgs |{ 182( args. db , move |store : & Store |{ 183let entries = store. read_tx ( |tx|{ 184 tx. list ( 185& args. keyspace , 186 args. start . as_deref (), 187 args. end . as_deref (), 188 args. prefix . as_deref (), 189 args. limit , 190 args. reverse , 191) 192}) ?; 193Ok ( entries 194. into_iter () 195. map ( |( key, value, stamp) |{ 196( ToJsBuffer :: from ( key), ToJsBuffer :: from ( value), stamp) 197}) 198. collect ::< Vec < _ >>()) 199}) 200}), 201kv_op ( 202& shared, 203"op_kv_atomic" , 204true , 205 |( db, ops): ( u64 , Vec < AtomicOp >) |{ 206let ops: Vec < avril_kv:: BatchOp > = ops. into_iter (). map ( Into :: into). collect (); 207( db, move |store : & Store | store. atomic_commit ( & ops)) 208}, 209), 210], 211modules : const { & [ PUBLIC_MODULES [ 0 ]. module ] }, 212entry_point : Some ( "ext:kv/main.js" ), 213} 214} 215 216pub const PUBLIC_MODULES : & [ PublicModule ] = 217& [ avril_core:: public_module!( "avril:kv" , "ext:kv" , "main" )]; 218 219# [ cfg ( test )] 220mod tests;