From f35815d62a9daf83f2aeac5beae9a13625b82eb6 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 15 Apr 2021 20:07:39 +0200 Subject: [PATCH] Simplest chat system --- schemas/chat-protocol.prs | 5 +++ src/chat-client-standalone.ts | 60 ++++++++++++++++++++++++++++++ src/gen/chat-protocol.ts | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 schemas/chat-protocol.prs create mode 100644 src/chat-client-standalone.ts create mode 100644 src/gen/chat-protocol.ts diff --git a/schemas/chat-protocol.prs b/schemas/chat-protocol.prs new file mode 100644 index 0000000..b9a1d7d --- /dev/null +++ b/schemas/chat-protocol.prs @@ -0,0 +1,5 @@ +version 1 . +pointer Actor.Ref . + +Present = . +Says = . diff --git a/src/chat-client-standalone.ts b/src/chat-client-standalone.ts new file mode 100644 index 0000000..33dca3f --- /dev/null +++ b/src/chat-client-standalone.ts @@ -0,0 +1,60 @@ +import { $Present, $Says, asPresent, asSays, fromPresent, fromSays, Present, Says } from "./gen/chat-protocol.js"; +import { fromObserve, Observe } from "./gen/dataspace.js"; +import { Assertion, Handle, Ref, Turn } from "./actor.js"; +import readline from 'readline'; + +export default function (t: Turn, ds: Ref) { + let username = ''; + let usernameHandle: Handle | undefined; + + function updateUsername(t: Turn, u: string) { + username = u; + usernameHandle = t.replace(ds, usernameHandle, fromPresent(Present(username))); + } + updateUsername(t, 'user' + process.pid); + + const usermap = new Map(); + t.assert(ds, fromObserve(Observe({ + label: $Present, + observer: t.ref({ + assert(t: Turn, e0: Assertion, h: Handle): void { + const e = asPresent(e0); + console.log(`${e.username} arrived`); + usermap.set(h, e); + }, + retract(t: Turn, h: Handle): void { + const e = usermap.get(h); + if (e) { + usermap.delete(h); + console.log(`${e.username} departed`); + } + }, + }), + }))); + + t.assert(ds, fromObserve(Observe({ + label: $Says, + observer: t.ref({ + message(t: Turn, u0: Assertion): void { + const u = asSays(u0); + console.log(`${u.who}: ${u.what}`); + }, + }), + }))); + + function sendUtterance(what: string) { + t.freshen(t => t.message(ds, fromSays(Says({ who: username, what })))); + } + + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + + rl.on('line', (line: string) => { + if (line.startsWith('/nick ')) { + t.freshen(t => updateUsername(t, line.slice(5).trimLeft())); + } else { + sendUtterance(line); + } + }); + + rl.on('close', () => t.freshen(t => t.quit())); +} diff --git a/src/gen/chat-protocol.ts b/src/gen/chat-protocol.ts new file mode 100644 index 0000000..fce0b36 --- /dev/null +++ b/src/gen/chat-protocol.ts @@ -0,0 +1,69 @@ +import * as _ from "@preserves/core"; +import * as _i_Actor from "../actor"; + +export const $Present = Symbol.for("Present"); +export const $Says = Symbol.for("Says"); + +export type _ptr = _i_Actor.Ref; + +export type _val = _.Value<_ptr>; + +export type Present = {"username": string}; + +export type Says = {"who": string, "what": string}; + + +export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; + +export function Present(username: string): Present {return {"username": username};} + +export function Says({who, what}: {who: string, what: string}): Says {return {"who": who, "what": what};} + +export function asPresent(v: _val): Present { + let result = toPresent(v); + if (result === void 0) throw new TypeError(`Invalid Present: ${_.stringify(v)}`); + return result; +} + +export function toPresent(v: _val): undefined | Present { + let result: undefined | Present; + if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { + let _tmp0: (null) | undefined; + _tmp0 = _.is(v.label, $Present) ? null : void 0; + if (_tmp0 !== void 0) { + let _tmp1: (string) | undefined; + _tmp1 = typeof v[0] === 'string' ? v[0] : void 0; + if (_tmp1 !== void 0) {result = {"username": _tmp1};}; + }; + }; + return result; +} + +export function fromPresent(_v: Present): _val {return _.Record($Present, [_v["username"]]);} + +export function asSays(v: _val): Says { + let result = toSays(v); + if (result === void 0) throw new TypeError(`Invalid Says: ${_.stringify(v)}`); + return result; +} + +export function toSays(v: _val): undefined | Says { + let result: undefined | Says; + if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { + let _tmp0: (null) | undefined; + _tmp0 = _.is(v.label, $Says) ? null : void 0; + if (_tmp0 !== void 0) { + let _tmp1: (string) | undefined; + _tmp1 = typeof v[0] === 'string' ? v[0] : void 0; + if (_tmp1 !== void 0) { + let _tmp2: (string) | undefined; + _tmp2 = typeof v[1] === 'string' ? v[1] : void 0; + if (_tmp2 !== void 0) {result = {"who": _tmp1, "what": _tmp2};}; + }; + }; + }; + return result; +} + +export function fromSays(_v: Says): _val {return _.Record($Says, [_v["who"], _v["what"]]);} +