
Message.pudu
Pudu82 lines3.0 KB
1/** @Domain.Rpc.Message.Module — classifies one JSON-RPC input line */2module PuduLangMcp.Domain.Rpc.Message34import Std.Json as Json5import PuduLangMcp.Constants.Protocol as Protocol6import PuduLangMcp.Errors.RpcError as RpcError7import PuduLangMcp.Utils.JsonAccess as Access89/** @Domain.Rpc.Message.RequestId — string or integer request identity */10export type RequestId = NumberId(Int) | TextId(Str)1112/** @Domain.Rpc.Message.Incoming — one classified input line */13export type Incoming14 = Request(RequestId, Str, Json.Json)15 | Notification(Str, Json.Json)16 | ClientResponse17 | Refused(Option[RequestId], RpcError.ProtocolError)181920type IdReading = NoId | ValidId(RequestId) | BadId(Str)212223export fn parse(line: Str) -> Incoming {24 let value = match Json.decode(line.trim()) {25 case Ok(decoded) => decoded26 case Err(problem) => { return Refused(None, RpcError.ParseError(Json.explain(&problem))) }27 }28 match value {29 case Json.List(_) => { return Refused(None, RpcError.InvalidRequest("batched messages are not supported")) }30 case Json.Object(_) => ()31 case _ => { return Refused(None, RpcError.InvalidRequest("a message must be a JSON object")) }32 }33 let id = match readId(&value) {34 case BadId(reason) => { return Refused(None, RpcError.InvalidRequest(reason)) }35 case ValidId(found) => Some(found)36 case NoId => None37 }38 if Access.text(&value, "jsonrpc") != Some(Protocol.JSONRPC_VERSION) {39 return Refused(id, RpcError.InvalidRequest("jsonrpc must be \"2.0\""))40 }41 let method = match Access.member(&value, "method") {42 case Some(Json.Text(name)) => name43 case Some(_) => { return Refused(id, RpcError.InvalidRequest("method must be a string")) }44 case None => {45 let answered = Access.member(&value, "result") != None || Access.member(&value, "error") != None46 if answered { return ClientResponse }47 return Refused(id, RpcError.InvalidRequest("method must be a string"))48 }49 }50 let params = match Access.member(&value, "params") {51 case None => Json.object(&[])52 case Some(Json.Object(fields)) => Json.Object(fields)53 case Some(_) => match id {54 case Some(_) => { return Refused(id, RpcError.InvalidParams("params must be an object")) }55 case None => Json.object(&[])56 }57 }58 match id {59 case Some(found) => Request(found, method, params)60 case None => Notification(method, params)61 }62}636465export fn idJson(id: &RequestId) -> Json.Json {66 match id {67 case NumberId(number) => Json.Number(number)68 case TextId(text) => Json.Text(text)69 }70}717273fn readId(value: &Json.Json) -> IdReading {74 match Access.member(value, "id") {75 case None => NoId76 case Some(Json.Number(number)) => ValidId(NumberId(number))77 case Some(Json.Text(text)) => ValidId(TextId(text))78 case Some(Json.Null) => BadId("id must not be null")79 case Some(_) => BadId("id must be a string or an integer")80 }81}82