| 1 | module main |
| 2 | |
| 3 | import net |
| 4 | import sync |
| 5 | import net.jsonrpc |
| 6 | import log |
| 7 | |
| 8 | // ---- CRUD domain ---- |
| 9 | struct KvItem { |
| 10 | key string |
| 11 | value string |
| 12 | } |
| 13 | |
| 14 | struct KvKey { |
| 15 | key string |
| 16 | } |
| 17 | |
| 18 | // ---- Handler ---- |
| 19 | struct KvStore { |
| 20 | mut: |
| 21 | mu &sync.RwMutex = sync.new_rwmutex() |
| 22 | store map[string]string |
| 23 | } |
| 24 | |
| 25 | fn (mut s KvStore) create(key string, value string) bool { |
| 26 | s.mu.@lock() |
| 27 | defer { s.mu.unlock() } |
| 28 | if key in s.store { |
| 29 | return false |
| 30 | } |
| 31 | s.store[key] = value |
| 32 | return true |
| 33 | } |
| 34 | |
| 35 | fn (mut s KvStore) get(key string) ?string { |
| 36 | s.mu.@rlock() |
| 37 | defer { s.mu.runlock() } |
| 38 | if value := s.store[key] { |
| 39 | return value |
| 40 | } |
| 41 | return none |
| 42 | } |
| 43 | |
| 44 | fn (mut s KvStore) update(key string, value string) bool { |
| 45 | s.mu.@lock() |
| 46 | defer { s.mu.unlock() } |
| 47 | if key in s.store { |
| 48 | s.store[key] = value |
| 49 | return true |
| 50 | } |
| 51 | return false |
| 52 | } |
| 53 | |
| 54 | fn (mut s KvStore) delete(key string) bool { |
| 55 | s.mu.@lock() |
| 56 | defer { s.mu.unlock() } |
| 57 | if key in s.store { |
| 58 | s.store.delete(key) |
| 59 | return true |
| 60 | } |
| 61 | return false |
| 62 | } |
| 63 | |
| 64 | fn (s KvStore) dump() map[string]string { |
| 65 | return s.store |
| 66 | } |
| 67 | |
| 68 | @[heap] |
| 69 | struct KvHandler { |
| 70 | mut: |
| 71 | store KvStore |
| 72 | } |
| 73 | |
| 74 | fn (mut h KvHandler) handle_create(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) { |
| 75 | p := req.decode_params[KvItem]() or { |
| 76 | wr.write_error(jsonrpc.invalid_params) |
| 77 | return |
| 78 | } |
| 79 | if p.key.len == 0 { |
| 80 | wr.write_error(jsonrpc.invalid_params) |
| 81 | return |
| 82 | } |
| 83 | log.warn('params=${p}') |
| 84 | if !h.store.create(p.key, p.value) { |
| 85 | wr.write_error(jsonrpc.ResponseError{ // custom app-level error code |
| 86 | code: -32010 |
| 87 | message: 'Key already exists' |
| 88 | data: p.key |
| 89 | }) |
| 90 | return |
| 91 | } |
| 92 | |
| 93 | wr.write({ |
| 94 | 'ok': true |
| 95 | }) |
| 96 | } |
| 97 | |
| 98 | fn (mut h KvHandler) handle_get(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) { |
| 99 | p := req.decode_params[KvKey]() or { |
| 100 | wr.write_error(jsonrpc.invalid_params) |
| 101 | return |
| 102 | } |
| 103 | |
| 104 | value := h.store.get(p.key) or { |
| 105 | wr.write_error(jsonrpc.ResponseError{ |
| 106 | code: -32004 |
| 107 | message: 'Not found' |
| 108 | data: p.key |
| 109 | }) |
| 110 | return |
| 111 | } |
| 112 | |
| 113 | wr.write(KvItem{ key: p.key, value: value }) |
| 114 | } |
| 115 | |
| 116 | fn (mut h KvHandler) handle_update(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) { |
| 117 | p := req.decode_params[KvItem]() or { |
| 118 | wr.write_error(jsonrpc.invalid_params) |
| 119 | return |
| 120 | } |
| 121 | |
| 122 | if !h.store.update(p.key, p.value) { |
| 123 | wr.write_error(jsonrpc.ResponseError{ |
| 124 | code: -32004 |
| 125 | message: 'Not found' |
| 126 | data: p.key |
| 127 | }) |
| 128 | return |
| 129 | } |
| 130 | |
| 131 | wr.write({ |
| 132 | 'ok': true |
| 133 | }) |
| 134 | } |
| 135 | |
| 136 | fn (mut h KvHandler) handle_delete(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) { |
| 137 | p := req.decode_params[KvKey]() or { |
| 138 | wr.write_error(jsonrpc.invalid_params) |
| 139 | return |
| 140 | } |
| 141 | |
| 142 | if !h.store.delete(p.key) { |
| 143 | wr.write_error(jsonrpc.ResponseError{ |
| 144 | code: -32004 |
| 145 | message: 'Not found' |
| 146 | data: p.key |
| 147 | }) |
| 148 | return |
| 149 | } |
| 150 | |
| 151 | wr.write({ |
| 152 | 'ok': true |
| 153 | }) |
| 154 | } |
| 155 | |
| 156 | fn (mut h KvHandler) handle_list(_req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) { |
| 157 | mut items := []KvItem{} |
| 158 | for k, v in h.store.dump() { |
| 159 | items << KvItem{ |
| 160 | key: k |
| 161 | value: v |
| 162 | } |
| 163 | } |
| 164 | items.sort(a.key < b.key) |
| 165 | wr.write(items) |
| 166 | } |
| 167 | |
| 168 | // ---- Per-connection server loop ---- |
| 169 | // The jsonrpc.Server.start() reads from stream and writes to same stream. |
| 170 | fn handle_conn(mut conn net.TcpConn, h jsonrpc.Handler) { |
| 171 | defer { conn.close() or {} } |
| 172 | |
| 173 | mut log_inter := jsonrpc.LoggingInterceptor{} |
| 174 | mut inters := jsonrpc.Interceptors{ |
| 175 | event: [log_inter.on_event] |
| 176 | encoded_request: [log_inter.on_encoded_request] |
| 177 | request: [log_inter.on_request] |
| 178 | response: [log_inter.on_response] |
| 179 | encoded_response: [log_inter.on_encoded_response] |
| 180 | } |
| 181 | |
| 182 | mut srv := jsonrpc.new_server(jsonrpc.ServerConfig{ |
| 183 | stream: conn |
| 184 | handler: h |
| 185 | interceptors: inters |
| 186 | }) |
| 187 | |
| 188 | jsonrpc.dispatch_event(inters.event, 'start', 'server started') |
| 189 | srv.start() |
| 190 | } |
| 191 | |
| 192 | fn main() { |
| 193 | mut s := KvStore{} |
| 194 | mut h := KvHandler{ |
| 195 | store: s |
| 196 | } |
| 197 | mut r := jsonrpc.Router{} |
| 198 | r.register('kv.create', h.handle_create) |
| 199 | r.register('kv.get', h.handle_get) |
| 200 | r.register('kv.update', h.handle_update) |
| 201 | r.register('kv.delete', h.handle_delete) |
| 202 | r.register('kv.list', h.handle_list) |
| 203 | |
| 204 | addr := '127.0.0.1:42228' |
| 205 | mut l := net.listen_tcp(.ip, addr)! |
| 206 | println('TCP JSON-RPC server on ${addr} (Content-Length framing)') |
| 207 | |
| 208 | for { |
| 209 | mut c := l.accept()! |
| 210 | println('Accepted') |
| 211 | go handle_conn(mut c, r.handle_jsonrpc) |
| 212 | } |
| 213 | } |
| 214 | |