{"id":"2XBm7c82nD","url":"https://pastebin.ca/2XBm7c82nD","raw_url":"https://raw.anybin.ca/2XBm7c82nD","visibility":"public","access":"public","created_at":1780449818772,"expires_at":null,"fetch_limit":null,"fetches_used":0,"reads_remaining":null,"size_bytes":2937,"syntax_hint":"hew","title":"Hew v0.5 — redis-like KV store (in-process actor + ask, RESP-style protocol)","filename":"kv_actor.hew","change_note":null,"cipher":null,"cipher_meta":null,"parent_id":null,"root_id":"2XBm7c82nD","version":1,"owner_id":"06F6D6Y710X9HQV90HF93KWQT8","recipient_id":null,"body":"// In-process redis-like KV store (v0.5 Hew).\n//\n// The actor + HashMap + ask CORE a networked redis server is built on. The TCP\n// front-end is currently blocked by the D10 module-qualified-type-in-Result\n// codegen gap (`import std::net`), so this drives the store with a scripted client\n// session instead of over a socket.\n//\n// The actor receives ONE command-line string and returns a RESP-style reply\n// string (+OK / +value / -NOT_FOUND / :count). A single string arg keeps within\n// the current \"one argument per receive\" limit, and parsing the command line in\n// the actor is how a real redis server works anyway.\nactor Store {\n    let data: HashMap<string, string>;\n    receive fn exec(line: string) -> string {\n        let cmd = line.trim();\n        if cmd == \"PING\" {\n            \"+PONG\"\n        } else if cmd == \"DBSIZE\" {\n            let n = data.len();\n            f\":{n}\"\n        } else if cmd.starts_with(\"SET \") {\n            let rest = cmd.slice(4, cmd.len());\n            let sp = rest.find(\" \");\n            if sp < 0 {\n                \"-ERR usage: SET key value\"\n            } else {\n                let key = rest.slice(0, sp);\n                let value = rest.slice(sp + 1, rest.len());\n                data.insert(key, value);\n                \"+OK\"\n            }\n        } else if cmd.starts_with(\"GET \") {\n            let key = cmd.slice(4, cmd.len());\n            match data.get(key) {\n                Some(val) => f\"+{val}\",\n                None => \"-NOT_FOUND\",\n            }\n        } else if cmd.starts_with(\"DEL \") {\n            let key = cmd.slice(4, cmd.len());\n            if data.contains_key(key) {\n                data.remove(key);\n                \"+OK\"\n            } else {\n                \"-NOT_FOUND\"\n            }\n        } else {\n            \"-ERR unknown command\"\n        }\n    }\n}\n\nfn call(store: LocalPid<Store>, line: string) -> string {\n    match await store.exec(line) {\n        Ok(reply) => reply,\n        Err(_) => \"-ASK_FAILED\",\n    }\n}\n\nfn main() -> i64 {\n    let data: HashMap<string, string> = HashMap::new();\n    let store = spawn Store(data: data);\n    var ok = true;\n    let r1 = call(store, \"PING\");\n    println(f\"PING            -> {r1}\");\n    if r1 != \"+PONG\" {\n        ok = false;\n    }\n    let r2 = call(store, \"SET foo bar\");\n    println(f\"SET foo bar     -> {r2}\");\n    if r2 != \"+OK\" {\n        ok = false;\n    }\n    let r3 = call(store, \"GET foo\");\n    println(f\"GET foo         -> {r3}\");\n    if r3 != \"+bar\" {\n        ok = false;\n    }\n    let r4 = call(store, \"DBSIZE\");\n    println(f\"DBSIZE          -> {r4}\");\n    if r4 != \":1\" {\n        ok = false;\n    }\n    let r5 = call(store, \"DEL foo\");\n    println(f\"DEL foo         -> {r5}\");\n    if r5 != \"+OK\" {\n        ok = false;\n    }\n    let r6 = call(store, \"GET foo\");\n    println(f\"GET foo (gone)  -> {r6}\");\n    if r6 != \"-NOT_FOUND\" {\n        ok = false;\n    }\n    if ok {\n        0\n    } else {\n        1\n    }\n}\n"}