Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mcp

Model Context Protocol server for Pudu language documentation and compiler tools

0.1.1Apache-2.01

InstallClose

Message.pudu

Pudu82 lines3.0 KB

GitHub ↗
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)1819/// What reading the `id` member found.20type IdReading = NoId | ValidId(RequestId) | BadId(Str)2122/// One input line classified as a request, notification, response, or refusal.23export 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}6364/// A request id as the JSON it arrived as.65export 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}7172/// The `id` member, validated.73fn 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